1818
1919package  com .loopj .android .http ;
2020
21+ import  android .text .TextUtils ;
2122import  android .util .Log ;
2223
2324import  org .apache .http .Header ;
@@ -51,18 +52,12 @@ public class JsonStreamerEntity implements HttpEntity {
5152    // Buffer used for reading from input streams. 
5253    private  final  byte [] buffer  = new  byte [BUFFER_SIZE ];
5354
54-     // Reusable StringBuilder used by escape() method. 
55-     // Its size is just initial, if more space is needed, the system will 
56-     // automatically enlarge the buffer. 
57-     private  static  final  StringBuilder  BUILDER  = new  StringBuilder (128 );
58- 
5955    private  static  final  byte [] JSON_TRUE  = "true" .getBytes ();
6056    private  static  final  byte [] JSON_FALSE  = "false" .getBytes ();
6157    private  static  final  byte [] JSON_NULL  = "null" .getBytes ();
6258    private  static  final  byte [] STREAM_NAME  = escape ("name" );
6359    private  static  final  byte [] STREAM_TYPE  = escape ("type" );
6460    private  static  final  byte [] STREAM_CONTENTS  = escape ("contents" );
65-     private  static  final  byte [] STREAM_ELAPSED  = escape ("_elapsed" );
6661
6762    private  static  final  Header  HEADER_JSON_CONTENT  =
6863            new  BasicHeader (
@@ -80,11 +75,16 @@ public class JsonStreamerEntity implements HttpEntity {
8075    // Whether to use gzip compression while uploading 
8176    private  final  Header  contentEncoding ;
8277
78+     private  final  byte [] elapsedField ;
79+ 
8380    private  final  ResponseHandlerInterface  progressHandler ;
8481
85-     public  JsonStreamerEntity (ResponseHandlerInterface  progressHandler , boolean  useGZipCompression ) {
82+     public  JsonStreamerEntity (ResponseHandlerInterface  progressHandler , boolean  useGZipCompression ,  String   elapsedField ) {
8683        this .progressHandler  = progressHandler ;
8784        this .contentEncoding  = useGZipCompression  ? HEADER_GZIP_ENCODING  : null ;
85+         this .elapsedField  = TextUtils .isEmpty (elapsedField )
86+           ? null 
87+           : escape (elapsedField );
8888    }
8989
9090    /** 
@@ -147,7 +147,7 @@ public void writeTo(final OutputStream out) throws IOException {
147147
148148        // Use GZIP compression when sending streams, otherwise just use 
149149        // a buffered output stream to speed things up a bit. 
150-         OutputStream  os  = null  != contentEncoding 
150+         OutputStream  os  = contentEncoding  != null 
151151                ? new  GZIPOutputStream (out , BUFFER_SIZE )
152152                : out ;
153153
@@ -157,71 +157,90 @@ public void writeTo(final OutputStream out) throws IOException {
157157        // Keys used by the HashMaps. 
158158        Set <String > keys  = jsonParams .keySet ();
159159
160-         boolean  isFileWrapper ;
161- 
162-         // Go over all keys and handle each's value. 
163-         for  (String  key  : keys ) {
164-             // Evaluate the value (which cannot be null). 
165-             Object  value  = jsonParams .get (key );
166- 
167-             // Bail out prematurely if value's null. 
168-             if  (value  == null ) {
169-                 continue ;
170-             }
160+         int  keysCount  = keys .size ();
161+         if  (0  < keysCount ) {
162+             int  keysProcessed  = 0 ;
163+             boolean  isFileWrapper ;
171164
172-             // Write the JSON object's key. 
173-             os .write (escape (key ));
174-             os .write (':' );
165+             // Go over all keys and handle each's value. 
166+             for  (String  key  : keys ) {
167+                 // Indicate that this key has been processed. 
168+                 keysProcessed ++;
175169
176-             // Check if this is a FileWrapper. 
177-             isFileWrapper  = value  instanceof  RequestParams .FileWrapper ;
170+                 try  {
171+                     // Evaluate the value (which cannot be null). 
172+                     Object  value  = jsonParams .get (key );
178173
179-             // If a file should be uploaded. 
180-             if  (isFileWrapper  || value  instanceof  RequestParams .StreamWrapper ) {
181-                 // All uploads are sent as an object containing the file's details. 
182-                 os .write ('{' );
174+                     // Write the JSON object's key. 
175+                     os .write (escape (key ));
176+                     os .write (':' );
183177
184-                 // Determine how to handle this entry. 
185-                 if  (isFileWrapper ) {
186-                     writeToFromFile (os , (RequestParams .FileWrapper ) value );
187-                 } else  {
188-                     writeToFromStream (os , (RequestParams .StreamWrapper ) value );
178+                     // Bail out prematurely if value's null. 
179+                     if  (value  == null ) {
180+                         os .write (JSON_NULL );
181+                     } else  {
182+                         // Check if this is a FileWrapper. 
183+                         isFileWrapper  = value  instanceof  RequestParams .FileWrapper ;
184+ 
185+                         // If a file should be uploaded. 
186+                         if  (isFileWrapper  || value  instanceof  RequestParams .StreamWrapper ) {
187+                             // All uploads are sent as an object containing the file's details. 
188+                             os .write ('{' );
189+ 
190+                             // Determine how to handle this entry. 
191+                             if  (isFileWrapper ) {
192+                                 writeToFromFile (os , (RequestParams .FileWrapper ) value );
193+                             } else  {
194+                                 writeToFromStream (os , (RequestParams .StreamWrapper ) value );
195+                             }
196+ 
197+                             // End the file's object and prepare for next one. 
198+                             os .write ('}' );
199+                         } else  if  (value  instanceof  JsonValueInterface ) {
200+                             os .write (((JsonValueInterface ) value ).getEscapedJsonValue ());
201+                         } else  if  (value  instanceof  org .json .JSONObject ) {
202+                             os .write (((org .json .JSONObject ) value ).toString ().getBytes ());
203+                         } else  if  (value  instanceof  org .json .JSONArray ) {
204+                             os .write (((org .json .JSONArray ) value ).toString ().getBytes ());
205+                         } else  if  (value  instanceof  Boolean ) {
206+                             os .write ((Boolean ) value  ? JSON_TRUE  : JSON_FALSE );
207+                         } else  if  (value  instanceof  Long ) {
208+                             os .write ((((Number ) value ).longValue () + "" ).getBytes ());
209+                         } else  if  (value  instanceof  Double ) {
210+                             os .write ((((Number ) value ).doubleValue () + "" ).getBytes ());
211+                         } else  if  (value  instanceof  Float ) {
212+                             os .write ((((Number ) value ).floatValue () + "" ).getBytes ());
213+                         } else  if  (value  instanceof  Integer ) {
214+                             os .write ((((Number ) value ).intValue () + "" ).getBytes ());
215+                         } else  {
216+                             os .write (escape (value .toString ()));
217+                         }
218+                     }
219+                 } finally  {
220+                     // Separate each K:V with a comma, except the last one. 
221+                     if  (elapsedField  != null  || keysProcessed  < keysCount ) {
222+                         os .write (',' );
223+                     }
189224                }
225+             }
190226
191-                 // End the file's object and prepare for next one. 
192-                 os .write ('}' );
193-             } else  if  (value  instanceof  JsonValueInterface ) {
194-                 os .write (((JsonValueInterface ) value ).getEscapedJsonValue ());
195-             } else  if  (value  instanceof  org .json .JSONObject ) {
196-                 os .write (((org .json .JSONObject ) value ).toString ().getBytes ());
197-             } else  if  (value  instanceof  org .json .JSONArray ) {
198-                 os .write (((org .json .JSONArray ) value ).toString ().getBytes ());
199-             } else  if  (value  instanceof  Boolean ) {
200-                 os .write ((Boolean ) value  ? JSON_TRUE  : JSON_FALSE );
201-             } else  if  (value  instanceof  Long ) {
202-                 os .write ((((Number ) value ).longValue () + "" ).getBytes ());
203-             } else  if  (value  instanceof  Double ) {
204-                 os .write ((((Number ) value ).doubleValue () + "" ).getBytes ());
205-             } else  if  (value  instanceof  Float ) {
206-                 os .write ((((Number ) value ).floatValue () + "" ).getBytes ());
207-             } else  if  (value  instanceof  Integer ) {
208-                 os .write ((((Number ) value ).intValue () + "" ).getBytes ());
209-             } else  {
210-                 os .write (escape (value .toString ()));
227+             // Calculate how many milliseconds it took to upload the contents. 
228+             long  elapsedTime  = System .currentTimeMillis () - now ;
229+ 
230+             // Include the elapsed time taken to upload everything. 
231+             // This might be useful for somebody, but it serves us well since 
232+             // there will almost always be a ',' as the last sent character. 
233+             if  (elapsedField  != null ) {
234+                 os .write (elapsedField );
235+                 os .write (':' );
236+                 os .write ((elapsedTime  + "" ).getBytes ());
211237            }
212238
213-             os . write ( ',' );
239+             Log . i ( LOG_TAG ,  "Uploaded JSON in "  +  Math . floor ( elapsedTime  /  1000 ) +  " seconds" );
214240        }
215241
216-         // Include the elapsed time taken to upload everything. 
217-         // This might be useful for somebody, but it serves us well since 
218-         // there will almost always be a ',' as the last sent character. 
219-         os .write (STREAM_ELAPSED );
220-         os .write (':' );
221-         long  elapsedTime  = System .currentTimeMillis () - now ;
222-         os .write ((elapsedTime  + "}" ).getBytes ());
223- 
224-         Log .i (LOG_TAG , "Uploaded JSON in "  + Math .floor (elapsedTime  / 1000 ) + " seconds" );
242+         // Close the JSON object. 
243+         os .write ('}' );
225244
226245        // Flush the contents up the stream. 
227246        os .flush ();
@@ -321,60 +340,57 @@ static byte[] escape(String string) {
321340            return  JSON_NULL ;
322341        }
323342
343+         // Create a string builder to generate the escaped string. 
344+         StringBuilder  sb  = new  StringBuilder (128 );
345+ 
324346        // Surround with quotations. 
325-         BUILDER .append ('"' );
347+         sb .append ('"' );
326348
327349        int  length  = string .length (), pos  = -1 ;
328350        while  (++pos  < length ) {
329351            char  ch  = string .charAt (pos );
330352            switch  (ch ) {
331353                case  '"' :
332-                     BUILDER .append ("\\ \" " );
354+                     sb .append ("\\ \" " );
333355                    break ;
334356                case  '\\' :
335-                     BUILDER .append ("\\ \\ " );
357+                     sb .append ("\\ \\ " );
336358                    break ;
337359                case  '\b' :
338-                     BUILDER .append ("\\ b" );
360+                     sb .append ("\\ b" );
339361                    break ;
340362                case  '\f' :
341-                     BUILDER .append ("\\ f" );
363+                     sb .append ("\\ f" );
342364                    break ;
343365                case  '\n' :
344-                     BUILDER .append ("\\ n" );
366+                     sb .append ("\\ n" );
345367                    break ;
346368                case  '\r' :
347-                     BUILDER .append ("\\ r" );
369+                     sb .append ("\\ r" );
348370                    break ;
349371                case  '\t' :
350-                     BUILDER .append ("\\ t" );
372+                     sb .append ("\\ t" );
351373                    break ;
352374                default :
353375                    // Reference: http://www.unicode.org/versions/Unicode5.1.0/ 
354376                    if  ((ch  >= '\u0000'  && ch  <= '\u001F' ) || (ch  >= '\u007F'  && ch  <= '\u009F' ) || (ch  >= '\u2000'  && ch  <= '\u20FF' )) {
355377                        String  intString  = Integer .toHexString (ch );
356-                         BUILDER .append ("\\ u" );
378+                         sb .append ("\\ u" );
357379                        int  intLength  = 4  - intString .length ();
358380                        for  (int  zero  = 0 ; zero  < intLength ; zero ++) {
359-                             BUILDER .append ('0' );
381+                             sb .append ('0' );
360382                        }
361-                         BUILDER .append (intString .toUpperCase (Locale .US ));
383+                         sb .append (intString .toUpperCase (Locale .US ));
362384                    } else  {
363-                         BUILDER .append (ch );
385+                         sb .append (ch );
364386                    }
365387                    break ;
366388            }
367389        }
368390
369391        // Surround with quotations. 
370-         BUILDER .append ('"' );
371- 
372-         try  {
373-             return  BUILDER .toString ().getBytes ();
374-         } finally  {
375-             // Empty the String buffer. 
376-             // This is 20-30% faster than instantiating a new object. 
377-             BUILDER .setLength (0 );
378-         }
392+         sb .append ('"' );
393+ 
394+         return  sb .toString ().getBytes ();
379395    }
380396}
0 commit comments