19
19
import java .io .InputStream ;
20
20
import java .io .InputStreamReader ;
21
21
import java .nio .CharBuffer ;
22
+ import java .nio .charset .Charset ;
22
23
import org .json .JSONException ;
24
+ import org .json .JSONObject ;
23
25
24
26
/**
25
27
* Reads the length-prefixed JSON stream for Bundles.
26
28
*
27
29
* <p>The class takes a bundle stream and presents abstractions to read bundled elements out of the
28
30
* underlying content.
29
31
*/
30
- public class BundleReader extends BundleElement {
32
+ public class BundleReader {
31
33
/** The capacity for the internal char buffer. */
32
34
protected static final int BUFFER_CAPACITY = 1024 ;
33
35
@@ -40,8 +42,10 @@ public class BundleReader extends BundleElement {
40
42
41
43
public BundleReader (BundleSerializer serializer , InputStream data ) {
42
44
this .serializer = serializer ;
43
- dataReader = new InputStreamReader (data );
45
+ dataReader = new InputStreamReader (data , Charset . forName ( "UTF-8" ) );
44
46
buffer = CharBuffer .allocate (BUFFER_CAPACITY );
47
+
48
+ buffer .flip (); // Start the buffer in "reading mode"
45
49
}
46
50
47
51
/** Returns the metadata element from the bundle. */
@@ -91,23 +95,23 @@ public void close() throws IOException {
91
95
*/
92
96
@ Nullable
93
97
private BundleElement readNextElement () throws IOException , JSONException {
94
- int length = readLength ();
95
- if (length == - 1 ) {
98
+ String lengthPrefix = readLengthPrefix ();
99
+ if (lengthPrefix == null ) {
96
100
return null ;
97
101
}
98
102
99
- String json = readJsonString (length );
100
- bytesRead += ( int ) ( Math . log10 ( length ) + 1 ) + length ;
101
- return BundleElement . fromJson ( serializer , json );
103
+ String json = readJsonString (Integer . parseInt ( lengthPrefix ) );
104
+ bytesRead += lengthPrefix . length ( ) + json . length () ;
105
+ return decodeBundleElement ( json );
102
106
}
103
107
104
108
/**
105
109
* Reads the length prefix from the beginning of the internal buffer until the first '{'. Returns
106
110
* the integer-decoded length.
107
111
*
108
- * <p>If it reached the end of the stream, returns -1 .
112
+ * <p>If it reached the end of the stream, returns null .
109
113
*/
110
- private int readLength () throws IOException {
114
+ private @ Nullable String readLengthPrefix () throws IOException {
111
115
int nextOpenBracket ;
112
116
113
117
while ((nextOpenBracket = indexOfOpenBracket ()) == -1 ) {
@@ -119,7 +123,7 @@ private int readLength() throws IOException {
119
123
// We broke out of the loop because underlying stream is closed, and there happens to be no
120
124
// more data to process.
121
125
if (buffer .remaining () == 0 ) {
122
- return - 1 ;
126
+ return null ;
123
127
}
124
128
125
129
// We broke out of the loop because underlying stream is closed, but still cannot find an
@@ -130,14 +134,14 @@ private int readLength() throws IOException {
130
134
131
135
char [] c = new char [nextOpenBracket ];
132
136
buffer .get (c );
133
- return Integer . parseInt ( new String (c ) );
137
+ return new String (c );
134
138
}
135
139
136
140
/** Returns the index of the first open bracket, or -1 if none is found. */
137
141
private int indexOfOpenBracket () {
138
142
buffer .mark ();
139
143
try {
140
- for (int i = 0 ; i < buffer .limit (); ++i ) {
144
+ for (int i = 0 ; i < buffer .remaining (); ++i ) {
141
145
if (buffer .get () == '{' ) {
142
146
return i ;
143
147
}
@@ -155,20 +159,22 @@ private int indexOfOpenBracket() {
155
159
* <p>Returns a string decoded from the read bytes.
156
160
*/
157
161
private String readJsonString (int length ) throws IOException {
158
- char [] c = new char [ length ] ;
162
+ StringBuilder json = new StringBuilder () ;
159
163
160
- int read = Math .min (length , buffer .remaining ());
161
- buffer .get (c , 0 , read );
162
-
163
- while (read < length ) {
164
+ int remaining = length ;
165
+ while (remaining > 0 ) {
164
166
if (!pullMoreData ()) {
165
167
raiseError ("Reached the end of bundle when more data was expected." );
166
168
}
167
- int toRead = Math .min (length , buffer .remaining ());
168
- buffer .get (c , read , toRead );
169
- read += toRead ;
169
+
170
+ int read = Math .min (remaining , buffer .remaining ());
171
+ json .append (buffer , 0 , read );
172
+ buffer .position (buffer .position () + read );
173
+
174
+ remaining -= read ;
170
175
}
171
- return new String (c );
176
+
177
+ return json .toString ();
172
178
}
173
179
174
180
/**
@@ -179,17 +185,29 @@ private String readJsonString(int length) throws IOException {
179
185
private boolean pullMoreData () throws IOException {
180
186
if (buffer .remaining () == 0 ) {
181
187
buffer .compact ();
188
+ dataReader .read (buffer );
189
+ buffer .flip ();
182
190
}
183
-
184
- int read ;
185
- do {
186
- read = dataReader .read (buffer );
187
- } while (read > 0 );
188
- buffer .flip ();
189
-
190
191
return buffer .remaining () > 0 ;
191
192
}
192
193
194
+ /** Converts a JSON-encoded bundle element into its model class. */
195
+ private BundleElement decodeBundleElement (String json ) throws JSONException {
196
+ JSONObject object = new JSONObject (json );
197
+
198
+ if (object .has ("metadata" )) {
199
+ return serializer .decodeBundleMetadata (object .getJSONObject ("metadata" ));
200
+ } else if (object .has ("namedQuery" )) {
201
+ return serializer .decodeNamedQuery (object .getJSONObject ("namedQuery" ));
202
+ } else if (object .has ("documentMetadata" )) {
203
+ return serializer .decodeBundledDocumentMetadata (object .getJSONObject ("documentMetadata" ));
204
+ } else if (object .has ("document" )) {
205
+ return serializer .decodeDocument (object .getJSONObject ("document" ));
206
+ } else {
207
+ throw new IllegalArgumentException ("Cannot decode unknown Bundle element: " + json );
208
+ }
209
+ }
210
+
193
211
/** Closes the underlying stream and raises an IllegalArgumentException. */
194
212
private void raiseError (String message ) throws IOException {
195
213
close ();
0 commit comments