Coverage Report - org.apache.tapestry.json.JSONObject
 
Classes in this File Line Coverage Branch Coverage Complexity
JSONObject
0%
0/224
0%
0/182
3.857
JSONObject$1
N/A
N/A
3.857
JSONObject$Null
0%
0/4
0%
0/4
3.857
 
 1  
 package org.apache.tapestry.json;
 2  
 
 3  
 /*
 4  
  Copyright (c) 2002 JSON.org
 5  
 
 6  
  Permission is hereby granted, free of charge, to any person obtaining a copy
 7  
  of this software and associated documentation files (the "Software"), to deal
 8  
  in the Software without restriction, including without limitation the rights
 9  
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  
  copies of the Software, and to permit persons to whom the Software is
 11  
  furnished to do so, subject to the following conditions:
 12  
 
 13  
  The above copyright notice and this permission notice shall be included in all
 14  
  copies or substantial portions of the Software.
 15  
 
 16  
  The Software shall be used for Good, not Evil.
 17  
 
 18  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 24  
  SOFTWARE.
 25  
  */
 26  
 
 27  
 import java.text.ParseException;
 28  
 import java.util.*;
 29  
 
 30  
 /**
 31  
  * A JSONObject is an unordered collection of name/value pairs. Its external
 32  
  * form is a string wrapped in curly braces with colons between the names and
 33  
  * values, and commas between the values and names. The internal form is an
 34  
  * object having get() and opt() methods for accessing the values by name, and
 35  
  * put() methods for adding or replacing values by name. The values can be any
 36  
  * of these types: Boolean, JSONArray, JSONObject, Number, String, or the
 37  
  * JSONObject.NULL object.
 38  
  * <p>
 39  
  * The constructor can convert an external form string into an internal form
 40  
  * Java object. The toString() method creates an external form string.
 41  
  * <p>
 42  
  * A get() method returns a value if one can be found, and throws an exception
 43  
  * if one cannot be found. An opt() method returns a default value instead of
 44  
  * throwing an exception, and so is useful for obtaining optional values.
 45  
  * <p>
 46  
  * The generic get() and opt() methods return an object, which you can cast or
 47  
  * query for type. There are also typed get() and opt() methods that do type
 48  
  * checking and type coersion for you.
 49  
  * <p>
 50  
  * The texts produced by the toString() methods are very strict. The
 51  
  * constructors are more forgiving in the texts they will accept:
 52  
  * <ul>
 53  
  * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
 54  
  * before the closing brace.</li>
 55  
  * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single quote)</small>.</li>
 56  
  * <li>Strings do not need to be quoted at all if they do not begin with a
 57  
  * quote or single quote, and if they do not contain leading or trailing spaces,
 58  
  * and if they do not contain any of these characters:
 59  
  * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
 60  
  * if they are not the reserved words <code>true</code>, <code>false</code>,
 61  
  * or <code>null</code>.</li>
 62  
  * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
 63  
  * by <code>:</code></li>
 64  
  * <li>Values can be followed by <code>;</code> as well as by <code>,</code></li>
 65  
  * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
 66  
  * <code>0x-</code> <small>(hex)</small> prefix.</li>
 67  
  * <li>Line comments can begin with <code>#</code></li>
 68  
  * </ul>
 69  
  * 
 70  
  * @author JSON.org
 71  
  * @version 1
 72  
  */
 73  
 public class JSONObject
 74  
 {
 75  
 
 76  
     /**
 77  
      * It is sometimes more convenient and less ambiguous to have a NULL object
 78  
      * than to use Java's null value. JSONObject.NULL.equals(null) returns true.
 79  
      * JSONObject.NULL.toString() returns "null".
 80  
      */
 81  0
     public static final Object NULL = new Null();
 82  
 
 83  
     /**
 84  
      * JSONObject.NULL is equivalent to the value that JavaScript calls null,
 85  
      * whilst Java's null is equivalent to the value that JavaScript calls
 86  
      * undefined.
 87  
      */
 88  0
     private static final class Null
 89  
     {
 90  
 
 91  
         /**
 92  
          * There is only intended to be a single instance of the NULL object, so
 93  
          * the clone method returns itself.
 94  
          * CHECKSTYLE:OFF
 95  
          * @return NULL.
 96  
          */
 97  
         protected Object clone()
 98  
         {
 99  0
             return this;
 100  
         }
 101  
 
 102  
         /**
 103  
          * A Null object is equal to the null value and to itself.
 104  
          * 
 105  
          * @param object
 106  
          *            An object to test for nullness.
 107  
          * @return true if the object parameter is the JSONObject.NULL object or
 108  
          *         null.
 109  
          */
 110  
         public boolean equals(Object object)
 111  
         {
 112  0
             return object == null || object == this;
 113  
         }
 114  
 
 115  
         /**
 116  
          * Get the "null" string value.
 117  
          * 
 118  
          * @return The string "null".
 119  
          */
 120  
         public String toString()
 121  
         {
 122  0
             return "null";
 123  
         }
 124  
     }
 125  
 
 126  
     /**
 127  
      * The hash map where the JSONObject's properties are kept.
 128  
      */
 129  
     private HashMap myHashMap;
 130  
 
 131  
     /**
 132  
      * Construct an empty JSONObject.
 133  
      */
 134  
     public JSONObject()
 135  0
     {
 136  0
         this.myHashMap = new LinkedHashMap();
 137  0
     }
 138  
 
 139  
     /**
 140  
      * Construct a JSONObject from a subset of another JSONObject. An array of
 141  
      * strings is used to identify the keys that should be copied. Missing keys
 142  
      * are ignored.
 143  
      * 
 144  
      * @param jo
 145  
      *            A JSONObject.
 146  
      * @param sa
 147  
      *            An array of strings.
 148  
      */
 149  
     public JSONObject(JSONObject jo, String[] sa)
 150  
     {
 151  0
         this();
 152  0
         for(int i = 0; i < sa.length; i += 1)
 153  
         {
 154  0
             putOpt(sa[i], jo.opt(sa[i]));
 155  
         }
 156  0
     }
 157  
 
 158  
     /**
 159  
      * Construct a JSONObject from a JSONTokener.
 160  
      * 
 161  
      * @param x
 162  
      *            A JSONTokener object containing the source string.
 163  
      * @throws ParseException
 164  
      *             if there is a syntax error in the source string.
 165  
      */
 166  
     public JSONObject(JSONTokener x)
 167  
         throws ParseException
 168  
     {
 169  0
         this();
 170  
         char c;
 171  
         String key;
 172  
 
 173  0
         if (x.nextClean() != '{') { throw x
 174  
                 .syntaxError("A JSONObject must begin with '{'"); }
 175  
         while(true)
 176  
         {
 177  0
             c = x.nextClean();
 178  0
             switch(c)
 179  
             {
 180  
             case 0:
 181  0
                 throw x.syntaxError("A JSONObject must end with '}'");
 182  
             case '}':
 183  0
                 return;
 184  
             default:
 185  0
                 x.back();
 186  0
                 key = x.nextValue().toString();
 187  
             }
 188  
 
 189  
             /*
 190  
              * The key is followed by ':'. We will also tolerate '=' or '=>'.
 191  
              */
 192  
 
 193  0
             c = x.nextClean();
 194  0
             if (c == '=')
 195  
             {
 196  0
                 if (x.next() != '>')
 197  
                 {
 198  0
                     x.back();
 199  
                 }
 200  
             }
 201  0
             else if (c != ':') { throw x
 202  
                     .syntaxError("Expected a ':' after a key"); }
 203  0
             this.myHashMap.put(key, x.nextValue());
 204  
 
 205  
             /*
 206  
              * Pairs are separated by ','. We will also tolerate ';'.
 207  
              */
 208  
 
 209  0
             switch(x.nextClean())
 210  
             {
 211  
             case ';':
 212  
             case ',':
 213  0
                 if (x.nextClean() == '}') { return; }
 214  0
                 x.back();
 215  0
                 break;
 216  
             case '}':
 217  0
                 return;
 218  
             default:
 219  0
                 throw x.syntaxError("Expected a ',' or '}'");
 220  
             }
 221  
         }
 222  
     }
 223  
 
 224  
     /**
 225  
      * Construct a JSONObject from a Map.
 226  
      * 
 227  
      * @param map
 228  
      *            A map object that can be used to initialize the contents of
 229  
      *            the JSONObject.
 230  
      */
 231  
     public JSONObject(Map map)
 232  0
     {
 233  0
         this.myHashMap = new HashMap(map);
 234  0
     }
 235  
 
 236  
     /**
 237  
      * Construct a JSONObject from a string. This is the most commonly used
 238  
      * JSONObject constructor.
 239  
      * 
 240  
      * @param string
 241  
      *            A string beginning with <code>{</code>&nbsp;<small>(left
 242  
      *            brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 243  
      *            brace)</small>.
 244  
      * @exception ParseException
 245  
      *                The string must be properly formatted.
 246  
      */
 247  
     public JSONObject(String string)
 248  
         throws ParseException
 249  
     {
 250  0
         this(new JSONTokener(string));
 251  0
     }
 252  
 
 253  
     public JSONObject accumulate(String key, Object value)
 254  
     {
 255  
         JSONArray a;
 256  0
         Object o = opt(key);
 257  0
         if (o == null)
 258  
         {
 259  0
             a = new JSONArray();
 260  0
             a.put(value);
 261  0
             put(key, a);
 262  
         }
 263  0
         else if (o instanceof JSONArray)
 264  
         {
 265  0
             a = (JSONArray) o;
 266  0
             a.put(value);
 267  
         }
 268  
         
 269  0
         return this;
 270  
     }
 271  
 
 272  
     public Object get(String key)
 273  
     {
 274  0
         Object o = opt(key);
 275  0
         if (o == null) { throw new NoSuchElementException("JSONObject["
 276  
                 + quote(key) + "] not found."); }
 277  0
         return o;
 278  
     }
 279  
 
 280  
     public boolean getBoolean(String key)
 281  
     {
 282  0
         Object o = get(key);
 283  0
         if (o.equals(Boolean.FALSE)
 284  
                 || (o instanceof String && ((String) o)
 285  
                         .equalsIgnoreCase("false")))
 286  
         {
 287  0
             return false;
 288  
         }
 289  0
         else if (o.equals(Boolean.TRUE)
 290  
                 || (o instanceof String && ((String) o)
 291  0
                         .equalsIgnoreCase("true"))) { return true; }
 292  0
         throw new ClassCastException("JSONObject[" + quote(key)
 293  
                 + "] is not a Boolean.");
 294  
     }
 295  
 
 296  
     public double getDouble(String key)
 297  
     {
 298  0
         Object o = get(key);
 299  0
         if (o instanceof Number) { return ((Number) o).doubleValue(); }
 300  0
         if (o instanceof String) { return new Double((String) o).doubleValue(); }
 301  0
         throw new NumberFormatException("JSONObject[" + quote(key)
 302  
                 + "] is not a number.");
 303  
     }
 304  
 
 305  
     /**
 306  
      * Get the Map the holds that contents of the JSONObject.
 307  
      * 
 308  
      * @return The getHashMap.
 309  
      */
 310  
     Map getMap()
 311  
     {
 312  0
         return this.myHashMap;
 313  
     }
 314  
 
 315  
     public int getInt(String key)
 316  
     {
 317  0
         Object o = get(key);
 318  0
         return o instanceof Number ? ((Number) o).intValue()
 319  
                 : (int) getDouble(key);
 320  
     }
 321  
 
 322  
     public JSONArray getJSONArray(String key)
 323  
     {
 324  0
         Object o = get(key);
 325  0
         if (o instanceof JSONArray) { return (JSONArray) o; }
 326  0
         throw new NoSuchElementException("JSONObject[" + quote(key)
 327  
                 + "] is not a JSONArray.");
 328  
     }
 329  
 
 330  
     public JSONObject getJSONObject(String key)
 331  
     {
 332  0
         Object o = get(key);
 333  0
         if (o instanceof JSONObject) { return (JSONObject) o; }
 334  0
         throw new NoSuchElementException("JSONObject[" + quote(key)
 335  
                 + "] is not a JSONObject.");
 336  
     }
 337  
 
 338  
     public String getString(String key)
 339  
     {
 340  0
         return get(key).toString();
 341  
     }
 342  
 
 343  
     public boolean has(String key)
 344  
     {
 345  0
         return this.myHashMap.containsKey(key);
 346  
     }
 347  
 
 348  
     public boolean isNull(String key)
 349  
     {
 350  0
         return JSONObject.NULL.equals(opt(key));
 351  
     }
 352  
 
 353  
     public Iterator keys()
 354  
     {
 355  0
         return this.myHashMap.keySet().iterator();
 356  
     }
 357  
 
 358  
     public int length()
 359  
     {
 360  0
         return this.myHashMap.size();
 361  
     }
 362  
 
 363  
     public JSONArray names()
 364  
     {
 365  0
         JSONArray ja = new JSONArray();
 366  0
         Iterator keys = keys();
 367  0
         while(keys.hasNext())
 368  
         {
 369  0
             ja.put(keys.next());
 370  
         }
 371  0
         return ja.length() == 0 ? null : ja;
 372  
     }
 373  
 
 374  
     /**
 375  
      * Produce a string from a number.
 376  
      * 
 377  
      * @param n A Number
 378  
      * @return A String.
 379  
      * @exception ArithmeticException
 380  
      *                JSON can only serialize finite numbers.
 381  
      */
 382  
     public static String numberToString(Number n)
 383  
     {
 384  0
         if ((n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN()))
 385  
                 || (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN()))) { 
 386  0
             throw new ArithmeticException("JSON can only serialize finite numbers."); 
 387  
         }
 388  
         
 389  
         // Shave off trailing zeros and decimal point, if possible.
 390  
         
 391  0
         String s = n.toString();
 392  0
         if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0)
 393  
         {
 394  0
             while(s.endsWith("0"))
 395  
             {
 396  0
                 s = s.substring(0, s.length() - 1);
 397  
             }
 398  0
             if (s.endsWith("."))
 399  
             {
 400  0
                 s = s.substring(0, s.length() - 1);
 401  
             }
 402  
         }
 403  0
         return s;
 404  
     }
 405  
 
 406  
     public Object opt(String key)
 407  
     {
 408  0
         if (key == null) { throw new NullPointerException("Null key"); }
 409  0
         return this.myHashMap.get(key);
 410  
     }
 411  
 
 412  
     public boolean optBoolean(String key)
 413  
     {
 414  0
         return optBoolean(key, false);
 415  
     }
 416  
 
 417  
     public boolean optBoolean(String key, boolean defaultValue)
 418  
     {
 419  0
         Object o = opt(key);
 420  0
         if (o != null)
 421  
         {
 422  0
             if (o.equals(Boolean.FALSE)
 423  
                     || (o instanceof String && ((String) o)
 424  
                             .equalsIgnoreCase("false")))
 425  
             {
 426  0
                 return false;
 427  
             }
 428  0
             else if (o.equals(Boolean.TRUE)
 429  
                     || (o instanceof String && ((String) o)
 430  0
                             .equalsIgnoreCase("true"))) { return true; }
 431  
         }
 432  0
         return defaultValue;
 433  
     }
 434  
 
 435  
     public double optDouble(String key)
 436  
     {
 437  0
         return optDouble(key, Double.NaN);
 438  
     }
 439  
 
 440  
     public double optDouble(String key, double defaultValue)
 441  
     {
 442  0
         Object o = opt(key);
 443  0
         if (o != null)
 444  
         {
 445  0
             if (o instanceof Number) { return ((Number) o).doubleValue(); }
 446  
             try
 447  
             {
 448  0
                 return new Double((String) o).doubleValue();
 449  
             }
 450  0
             catch (Exception e)
 451  
             {
 452  0
                 return defaultValue;
 453  
             }
 454  
         }
 455  0
         return defaultValue;
 456  
     }
 457  
 
 458  
     public int optInt(String key)
 459  
     {
 460  0
         return optInt(key, 0);
 461  
     }
 462  
 
 463  
     public int optInt(String key, int defaultValue)
 464  
     {
 465  0
         Object o = opt(key);
 466  0
         if (o != null)
 467  
         {
 468  0
             if (o instanceof Number) { return ((Number) o).intValue(); }
 469  
             try
 470  
             {
 471  0
                 return Integer.parseInt((String) o);
 472  
             }
 473  0
             catch (Exception e)
 474  
             {
 475  0
                 return defaultValue;
 476  
             }
 477  
         }
 478  0
         return defaultValue;
 479  
     }
 480  
 
 481  
     public JSONArray optJSONArray(String key)
 482  
     {
 483  0
         Object o = opt(key);
 484  0
         return o instanceof JSONArray ? (JSONArray) o : null;
 485  
     }
 486  
 
 487  
     public JSONObject optJSONObject(String key)
 488  
     {
 489  0
         Object o = opt(key);
 490  0
         return o instanceof JSONObject ? (JSONObject) o : null;
 491  
     }
 492  
 
 493  
     public String optString(String key)
 494  
     {
 495  0
         return optString(key, "");
 496  
     }
 497  
 
 498  
     public String optString(String key, String defaultValue)
 499  
     {
 500  0
         Object o = opt(key);
 501  0
         return o != null ? o.toString() : defaultValue;
 502  
     }
 503  
 
 504  
     public JSONObject put(Object key, boolean value)
 505  
     {
 506  0
         put(key, Boolean.valueOf(value));
 507  0
         return this;
 508  
     }
 509  
 
 510  
     public JSONObject put(Object key, double value)
 511  
     {
 512  0
         put(key, new Double(value));
 513  0
         return this;
 514  
     }
 515  
 
 516  
     public JSONObject put(Object key, long value)
 517  
     {
 518  0
         put(key, new Long(value));
 519  0
         return this;
 520  
     }
 521  
     
 522  
     public JSONObject put(Object key, int value)
 523  
     {
 524  0
         put(key, new Integer(value));
 525  0
         return this;
 526  
     }
 527  
     
 528  
     public JSONObject put(Object key, Object value)
 529  
     {
 530  0
         if (key == null)
 531  0
             throw new NullPointerException("Null key.");
 532  
         
 533  0
         if (value != null)
 534  
         {
 535  0
             this.myHashMap.put(key, value);
 536  
         }
 537  
         else
 538  
         {
 539  0
             remove(key.toString());
 540  
         }
 541  
         
 542  0
         return this;
 543  
     }
 544  
     
 545  
     public JSONObject putOpt(String key, Object value)
 546  
     {
 547  0
         if (value != null)
 548  
         {
 549  0
             put(key, value);
 550  
         }
 551  0
         return this;
 552  
     }
 553  
 
 554  
     /**
 555  
      * @see #quote(String) .
 556  
      * @param value
 557  
      * 
 558  
      * @return The character quoted.
 559  
      */
 560  
     public static String quote(char value)
 561  
     {
 562  0
         return quote(new String(new char[]{value}));
 563  
     }
 564  
     
 565  
     /**
 566  
      * Produce a string in double quotes with backslash sequences in all the
 567  
      * right places.
 568  
      * 
 569  
      * @param string
 570  
      *            A String
 571  
      * @return A String correctly formatted for insertion in a JSON message.
 572  
      */
 573  
     public static String quote(String string)
 574  
     {
 575  0
         if (string == null || string.length() == 0) { return "\"\""; }
 576  
 
 577  
         char b;
 578  0
         char c = 0;
 579  
         int i;
 580  0
         int len = string.length();
 581  0
         StringBuffer sb = new StringBuffer(len + 4);
 582  
         String t;
 583  
 
 584  0
         sb.append('"');
 585  0
         for(i = 0; i < len; i += 1)
 586  
         {
 587  0
             b = c;
 588  0
             c = string.charAt(i);
 589  0
             switch(c)
 590  
             {
 591  
             case '\\':
 592  
             case '"':
 593  0
                 sb.append('\\');
 594  0
                 sb.append(c);
 595  0
                 break;
 596  
             case '/':
 597  0
                 if (b == '<')
 598  
                 {
 599  0
                     sb.append('\\');
 600  
                 }
 601  0
                 sb.append(c);
 602  0
                 break;
 603  
             case '\b':
 604  0
                 sb.append("\\b");
 605  0
                 break;
 606  
             case '\t':
 607  0
                 sb.append("\\t");
 608  0
                 break;
 609  
             case '\n':
 610  0
                 sb.append("\\n");
 611  0
                 break;
 612  
             case '\f':
 613  0
                 sb.append("\\f");
 614  0
                 break;
 615  
             case '\r':
 616  0
                 sb.append("\\r");
 617  0
                 break;
 618  
             default:
 619  0
                 if (c < ' ')
 620  
                 {
 621  0
                     t = "000" + Integer.toHexString(c);
 622  0
                     sb.append("\\u" + t.substring(t.length() - 4));
 623  
                 }
 624  
                 else
 625  
                 {
 626  0
                     sb.append(c);
 627  
                 }
 628  
             }
 629  
         }
 630  0
         sb.append('"');
 631  0
         return sb.toString();
 632  
     }
 633  
 
 634  
     public Object remove(String key)
 635  
     {
 636  0
         return this.myHashMap.remove(key);
 637  
     }
 638  
 
 639  
     public JSONArray toJSONArray(JSONArray names)
 640  
     {
 641  0
         if (names == null || names.length() == 0) { return null; }
 642  0
         JSONArray ja = new JSONArray();
 643  0
         for(int i = 0; i < names.length(); i += 1)
 644  
         {
 645  0
             ja.put(this.opt(names.getString(i)));
 646  
         }
 647  0
         return ja;
 648  
     }
 649  
 
 650  
     /** 
 651  
      * {@inheritDoc}
 652  
      */
 653  
     public String toString()
 654  
     {
 655  0
         Iterator keys = keys();
 656  0
         StringBuffer sb = new StringBuffer("{");
 657  
 
 658  0
         while(keys.hasNext())
 659  
         {
 660  0
             if (sb.length() > 1)
 661  
             {
 662  0
                 sb.append(',');
 663  
             }
 664  0
             Object o = keys.next();
 665  0
             sb.append(valueToString(o));
 666  0
             sb.append(':');
 667  0
             sb.append(valueToString(this.myHashMap.get(o)));
 668  0
         }
 669  0
         sb.append('}');
 670  0
         return sb.toString();
 671  
     }
 672  
     
 673  
     public String toString(int indentFactor)
 674  
     {
 675  0
         return toString(indentFactor, 0);
 676  
     }
 677  
 
 678  
     /**
 679  
      * Make a prettyprinted JSON string of this JSONObject.
 680  
      * <p>
 681  
      * Warning: This method assumes that the data structure is acyclical.
 682  
      * 
 683  
      * @param indentFactor
 684  
      *            The number of spaces to add to each level of indentation.
 685  
      * @param indent
 686  
      *            The indentation of the top level.
 687  
      * @return a printable, displayable, transmittable representation of the
 688  
      *         object, beginning with <code>{</code>&nbsp;<small>(left
 689  
      *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 690  
      *         brace)</small>.
 691  
      */
 692  
     String toString(int indentFactor, int indent)
 693  
     {
 694  
         int i;
 695  0
         int n = length();
 696  0
         if (n == 0) { return "{}"; }
 697  0
         Iterator keys = keys();
 698  0
         StringBuffer sb = new StringBuffer("{");
 699  0
         int newindent = indent + indentFactor;
 700  
         Object o;
 701  0
         if (n == 1)
 702  
         {
 703  0
             o = keys.next();
 704  0
             sb.append(quote(o.toString()));
 705  0
             sb.append(": ");
 706  0
             sb
 707  
                     .append(valueToString(this.myHashMap.get(o), indentFactor,
 708  
                             indent));
 709  
         }
 710  
         else
 711  
         {
 712  0
             while(keys.hasNext())
 713  
             {
 714  0
                 o = keys.next();
 715  0
                 if (sb.length() > 1)
 716  
                 {
 717  0
                     sb.append(",\n");
 718  
                 }
 719  
                 else
 720  
                 {
 721  0
                     sb.append('\n');
 722  
                 }
 723  0
                 for(i = 0; i < newindent; i += 1)
 724  
                 {
 725  0
                     sb.append(' ');
 726  
                 }
 727  0
                 sb.append(quote(o.toString()));
 728  0
                 sb.append(": ");
 729  0
                 sb.append(valueToString(this.myHashMap.get(o), indentFactor,
 730  
                         newindent));
 731  
             }
 732  0
             if (sb.length() > 1)
 733  
             {
 734  0
                 sb.append('\n');
 735  0
                 for(i = 0; i < indent; i += 1)
 736  
                 {
 737  0
                     sb.append(' ');
 738  
                 }
 739  
             }
 740  
         }
 741  0
         sb.append('}');
 742  0
         return sb.toString();
 743  
     }
 744  
 
 745  
     /**
 746  
      * Make JSON string of an object value.
 747  
      * <p>
 748  
      * Warning: This method assumes that the data structure is acyclical.
 749  
      * 
 750  
      * @param value
 751  
      *            The value to be serialized.
 752  
      * @return a printable, displayable, transmittable representation of the
 753  
      *         object, beginning with <code>{</code>&nbsp;<small>(left
 754  
      *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 755  
      *         brace)</small>.
 756  
      */
 757  
     static String valueToString(Object value)
 758  
     {
 759  0
         if (value == null || value.equals(null)) { return "null"; }
 760  0
         if (value instanceof Number) { return numberToString((Number) value); }
 761  0
         if (value instanceof Boolean || value instanceof JSONObject
 762  
                 || value instanceof JSONArray
 763  0
                 || value instanceof JSONLiteral) { return value.toString(); }
 764  0
         return quote(value.toString());
 765  
     }
 766  
 
 767  
     /**
 768  
      * Make a prettyprinted JSON string of an object value.
 769  
      * <p>
 770  
      * Warning: This method assumes that the data structure is acyclical.
 771  
      * 
 772  
      * @param value
 773  
      *            The value to be serialized.
 774  
      * @param indentFactor
 775  
      *            The number of spaces to add to each level of indentation.
 776  
      * @param indent
 777  
      *            The indentation of the top level.
 778  
      * @return a printable, displayable, transmittable representation of the
 779  
      *         object, beginning with <code>{</code>&nbsp;<small>(left
 780  
      *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 781  
      *         brace)</small>.
 782  
      */
 783  
     static String valueToString(Object value, int indentFactor, int indent)
 784  
     {
 785  0
         if (value == null || value.equals(null)) { return "null"; }
 786  0
         if (value instanceof Number) { return numberToString((Number) value); }
 787  0
         if (value instanceof Boolean) { return value.toString(); }
 788  0
         if (value instanceof JSONObject) { return (((JSONObject) value)
 789  
                 .toString(indentFactor, indent)); }
 790  0
         if (value instanceof JSONArray) { return (((JSONArray) value).toString(
 791  
                 indentFactor, indent)); }
 792  0
         if (JSONLiteral.class.isAssignableFrom(value.getClass()))
 793  0
             return value.toString();
 794  0
         return quote(value.toString());
 795  
     }
 796  
 }