View Javadoc

1   /*
2    * Copyright 2002-2009 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package net.sf.json.util;
18  
19  import java.math.BigDecimal;
20  import java.math.BigInteger;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import net.sf.ezmorph.MorphUtils;
28  import net.sf.ezmorph.MorpherRegistry;
29  import net.sf.ezmorph.bean.MorphDynaBean;
30  import net.sf.ezmorph.bean.MorphDynaClass;
31  import net.sf.json.JSON;
32  import net.sf.json.JSONArray;
33  import net.sf.json.JSONException;
34  import net.sf.json.JSONFunction;
35  import net.sf.json.JSONNull;
36  import net.sf.json.JSONObject;
37  import net.sf.json.JSONString;
38  import net.sf.json.JsonConfig;
39  import net.sf.json.regexp.RegexpUtils;
40  
41  import org.apache.commons.beanutils.DynaBean;
42  
43  /**
44   * Provides useful methods on java objects and JSON values.
45   *
46   * @author Andres Almiray <aalmiray@users.sourceforge.net>
47   * @version 7
48   */
49  public final class JSONUtils {
50     /** Constant for char " */
51     public static final String DOUBLE_QUOTE = "\"";
52     /** Constant for char ' */
53     public static final String SINGLE_QUOTE = "'";
54  
55     private static final String FUNCTION_BODY_PATTERN = "^function[ ]?\\(.*?\\)[ \n\t]*\\{(.*?)\\}$";
56     private static final String FUNCTION_HEADER_PATTERN = "^function[ ]?\\(.*?\\)$";
57     private static final String FUNCTION_PARAMS_PATTERN = "^function[ ]?\\((.*?)\\).*";
58     private static final String FUNCTION_PATTERN = "^function[ ]?\\(.*?\\)[ \n\t]*\\{.*?\\}$";
59     private static final String FUNCTION_PREFIX = "function";
60  
61     private static final MorpherRegistry morpherRegistry = new MorpherRegistry();
62  
63     static{
64        // register standard morphers
65        MorphUtils.registerStandardMorphers( morpherRegistry );
66     }
67  
68     /**
69      * Transforms the string into a valid Java Identifier.<br>
70      * The default strategy is JavaIdentifierTransformer.NOOP
71      *
72      * @throws JSONException if the string can not be transformed.
73      */
74     public static String convertToJavaIdentifier( String key ) {
75        return convertToJavaIdentifier( key, new JsonConfig() );
76     }
77  
78     /**
79      * Transforms the string into a valid Java Identifier.<br>
80      * The default strategy is JavaIdentifierTransformer.NOOP
81      *
82      * @throws JSONException if the string can not be transformed.
83      */
84     public static String convertToJavaIdentifier( String key, JsonConfig jsonConfig ) {
85        try{
86           return jsonConfig.getJavaIdentifierTransformer()
87                 .transformToJavaIdentifier( key );
88        }catch( JSONException jsone ){
89           throw jsone;
90        }catch( Exception e ){
91           throw new JSONException( e );
92        }
93     }
94  
95     /**
96      * Produce a string from a double. The string "null" will be returned if the
97      * number is not finite.
98      *
99      * @param d A double.
100     * @return A String.
101     */
102    public static String doubleToString( double d ) {
103       if( Double.isInfinite( d ) || Double.isNaN( d ) ){
104          return "null";
105       }
106 
107       // Shave off trailing zeros and decimal point, if possible.
108 
109       String s = Double.toString( d );
110       if( s.indexOf( '.' ) > 0 && s.indexOf( 'e' ) < 0 && s.indexOf( 'E' ) < 0 ){
111          while( s.endsWith( "0" ) ){
112             s = s.substring( 0, s.length() - 1 );
113          }
114          if( s.endsWith( "." ) ){
115             s = s.substring( 0, s.length() - 1 );
116          }
117       }
118       return s;
119    }
120 
121    /**
122     * Returns the body of a function literal.
123     */
124    public static String getFunctionBody( String function ) {
125       return RegexpUtils.getMatcher( FUNCTION_BODY_PATTERN, true ).getGroupIfMatches( function, 1 );
126    }
127    
128    /**
129     * Returns the params of a function literal.
130     */
131    public static String getFunctionParams( String function ) {
132       return RegexpUtils.getMatcher( FUNCTION_PARAMS_PATTERN, true ).getGroupIfMatches( function, 1 );
133    }
134 
135    /**
136     * Returns the inner-most component type of an Array.
137     */
138    public static Class getInnerComponentType( Class type ) {
139       if( !type.isArray() ){
140          return type;
141       }
142       return getInnerComponentType( type.getComponentType() );
143    }
144 
145    /**
146     * Returns the singleton MorpherRegistry.
147     */
148    public static MorpherRegistry getMorpherRegistry() {
149       return morpherRegistry;
150    }
151 
152    /**
153     * Creates a Map with all the properties of the JSONObject.
154     */
155    public static Map getProperties( JSONObject jsonObject ) {
156       Map properties = new HashMap();
157       for( Iterator keys = jsonObject.keys(); keys.hasNext(); ){
158          String key = (String) keys.next();
159          /*
160           * String parsedKey = key; if( !JSONUtils.isJavaIdentifier( parsedKey ) ){
161           * parsedKey = JSONUtils.convertToJavaIdentifier( key ); }
162           */
163          properties.put( key, getTypeClass( jsonObject.get( key ) ) );
164       }
165       return properties;
166    }
167 
168    /**
169     * Returns the JSON type.<br>
170     * Values are Object, String, Boolean, Number(subclasses) &amp; JSONFunction.
171     */
172    public static Class getTypeClass( Object obj ) {
173       if( isNull( obj ) ){
174          return Object.class;
175       }else if( isArray( obj ) ){
176          return List.class;
177       }else if( isFunction( obj ) ){
178          return JSONFunction.class;
179       }else if( isBoolean( obj ) ){
180          return Boolean.class;
181       }else if( isNumber( obj ) ){
182          Number n = (Number) obj;
183          if( isInteger( n ) ){
184             return Integer.class;
185          }else if( isLong( n ) ){
186             return Long.class;
187          }else if( isFloat( n ) ){
188             return Float.class;
189          }else if( isBigInteger( n ) ){
190             return BigInteger.class;
191          }else if( isBigDecimal( n ) ){
192             return BigDecimal.class;
193          }else if( isDouble( n ) ){
194             return Double.class;
195          }else{
196             throw new JSONException( "Unsupported type" );
197          }
198       }else if( isString( obj ) ){
199          return String.class;
200       }else if( isObject( obj ) ){
201          return Object.class;
202       }else{
203          throw new JSONException( "Unsupported type" );
204       }
205    }
206 
207    /**
208     * Returns the hashcode of value.<br>
209     * If null it will return JSONNull.getInstance().hashCode().<br>
210     * If value is JSON, JSONFunction or String, value.hashCode is returned,
211     * otherwise the value is transformed to a String an its hashcode is
212     * returned.
213     */
214    public static int hashCode( Object value ) {
215       if( value == null ){
216          return JSONNull.getInstance()
217                .hashCode();
218       }else if( value instanceof JSON || value instanceof String || value instanceof JSONFunction ){
219          return value.hashCode();
220       }else{
221          return String.valueOf( value )
222                .hashCode();
223       }
224    }
225 
226    /**
227     * Tests if a Class represents an array or Collection.
228     */
229    public static boolean isArray( Class clazz ) {
230       return clazz != null
231             && (clazz.isArray() || Collection.class.isAssignableFrom( clazz ) || (JSONArray.class.isAssignableFrom( clazz )));
232    }
233 
234    /**
235     * Tests if obj is an array or Collection.
236     */
237    public static boolean isArray( Object obj ) {
238       if( (obj != null && obj.getClass()
239             .isArray()) || (obj instanceof Collection) || (obj instanceof JSONArray) ){
240          return true;
241       }
242       return false;
243    }
244 
245    /**
246     * Tests if Class represents a Boolean or primitive boolean
247     */
248    public static boolean isBoolean( Class clazz ) {
249       return clazz != null
250             && (Boolean.TYPE.isAssignableFrom( clazz ) || Boolean.class.isAssignableFrom( clazz ));
251    }
252 
253    /**
254     * Tests if obj is a Boolean or primitive boolean
255     */
256    public static boolean isBoolean( Object obj ) {
257       if( (obj instanceof Boolean) || (obj != null && obj.getClass() == Boolean.TYPE) ){
258          return true;
259       }
260       return false;
261    }
262 
263    /**
264     * Tests if Class represents a primitive double or wrapper.<br>
265     */
266    public static boolean isDouble( Class clazz ) {
267       return clazz != null
268             && (Double.TYPE.isAssignableFrom( clazz ) || Double.class.isAssignableFrom( clazz ));
269    }
270 
271    /**
272     * Tests if obj is javaScript function.<br>
273     * Obj must be a non-null String and match <nowrap>"^function[ ]?\\(.*\\)[
274     * ]?\\{.*\\}$"</nowrap>
275     */
276    public static boolean isFunction( Object obj ) {
277       if( obj instanceof String ){
278          String str = (String) obj;
279          return str.startsWith( FUNCTION_PREFIX ) && RegexpUtils.getMatcher( FUNCTION_PATTERN, true ).matches( str );
280       }
281       if( obj instanceof JSONFunction ){
282          return true;
283       }
284       return false;
285    }
286 
287    /**
288     * Tests if obj is javaScript function header.<br>
289     * Obj must be a non-null String and match "^function[ ]?\\(.*\\)$"
290     */
291    public static boolean isFunctionHeader( Object obj ) {
292       if( obj instanceof String ){
293          String str = (String) obj;
294          return str.startsWith( FUNCTION_PREFIX ) && RegexpUtils.getMatcher( FUNCTION_HEADER_PATTERN, true ).matches( str );
295       }
296       return false;
297    }
298 
299    /**
300     * Returns trus if str represents a valid Java identifier.
301     */
302    public static boolean isJavaIdentifier( String str ) {
303       if( str.length() == 0 || !Character.isJavaIdentifierStart( str.charAt( 0 ) ) ){
304          return false;
305       }
306       for( int i = 1; i < str.length(); i++ ){
307          if( !Character.isJavaIdentifierPart( str.charAt( i ) ) ){
308             return false;
309          }
310       }
311       return true;
312    }
313 
314    /**
315     * Tests if the obj is a javaScript null.
316     */
317    public static boolean isNull( Object obj ) {
318       if( obj instanceof JSONObject ){
319          return ((JSONObject) obj).isNullObject();
320       }
321       return JSONNull.getInstance()
322             .equals( obj );
323    }
324 
325    /**
326     * Tests if Class represents a primitive number or wrapper.<br>
327     */
328    public static boolean isNumber( Class clazz ) {
329       return clazz != null
330             && (Byte.TYPE.isAssignableFrom( clazz ) || Short.TYPE.isAssignableFrom( clazz )
331                   || Integer.TYPE.isAssignableFrom( clazz ) || Long.TYPE.isAssignableFrom( clazz )
332                   || Float.TYPE.isAssignableFrom( clazz ) || Double.TYPE.isAssignableFrom( clazz ) || Number.class.isAssignableFrom( clazz ));
333    }
334 
335    /**
336     * Tests if obj is a primitive number or wrapper.<br>
337     */
338    public static boolean isNumber( Object obj ) {
339       if( (obj != null && obj.getClass() == Byte.TYPE)
340             || (obj != null && obj.getClass() == Short.TYPE)
341             || (obj != null && obj.getClass() == Integer.TYPE)
342             || (obj != null && obj.getClass() == Long.TYPE)
343             || (obj != null && obj.getClass() == Float.TYPE)
344             || (obj != null && obj.getClass() == Double.TYPE) ){
345          return true;
346       }
347 
348       return obj instanceof Number;
349    }
350 
351    /**
352     * Tests if obj is not a boolean, number, string or array.
353     */
354    public static boolean isObject( Object obj ) {
355       return !isNumber( obj ) && !isString( obj ) && !isBoolean( obj ) && !isArray( obj )
356             && !isFunction( obj ) || isNull( obj );
357    }
358 
359    /**
360     * Tests if Class represents a String or a char
361     */
362    public static boolean isString( Class clazz ) {
363       return clazz != null
364             && (String.class.isAssignableFrom( clazz ) || (Character.TYPE.isAssignableFrom( clazz ) || Character.class.isAssignableFrom( clazz )));
365    }
366 
367    /**
368     * Tests if obj is a String or a char
369     */
370    public static boolean isString( Object obj ) {
371       if( (obj instanceof String)
372             || (obj instanceof Character)
373             || (obj != null && (obj.getClass() == Character.TYPE || String.class.isAssignableFrom( obj.getClass() ))) ){
374          return true;
375       }
376       return false;
377    }
378 
379    /**
380     * Tests if the String possibly represents a valid JSON String.<br>
381     * Valid JSON strings are:
382     * <ul>
383     * <li>"null"</li>
384     * <li>starts with "[" and ends with "]"</li>
385     * <li>starts with "{" and ends with "}"</li>
386     * </ul>
387     */
388    public static boolean mayBeJSON( String string ) {
389       return string != null
390             && ("null".equals( string )
391                   || (string.startsWith( "[" ) && string.endsWith( "]" )) || (string.startsWith( "{" ) && string.endsWith( "}" )));
392    }
393 
394    /**
395     * Creates a new MorphDynaBean from a JSONObject. The MorphDynaBean will have
396     * all the properties of the original JSONObject with the most accurate type.
397     * Values of properties are not copied.
398     */
399    public static DynaBean newDynaBean( JSONObject jsonObject ) {
400       return newDynaBean( jsonObject, new JsonConfig() );
401    }
402 
403    /**
404     * Creates a new MorphDynaBean from a JSONObject. The MorphDynaBean will have
405     * all the properties of the original JSONObject with the most accurate type.
406     * Values of properties are not copied.
407     */
408    public static DynaBean newDynaBean( JSONObject jsonObject, JsonConfig jsonConfig ) {
409       Map props = getProperties( jsonObject );
410       for( Iterator entries = props.entrySet()
411             .iterator(); entries.hasNext(); ){
412          Map.Entry entry = (Map.Entry) entries.next();
413          String key = (String) entry.getKey();
414          if( !JSONUtils.isJavaIdentifier( key ) ){
415             String parsedKey = JSONUtils.convertToJavaIdentifier( key, jsonConfig );
416             if( parsedKey.compareTo( key ) != 0 ){
417                props.put( parsedKey, props.remove( key ) );
418             }
419          }
420       }
421       MorphDynaClass dynaClass = new MorphDynaClass( props );
422       MorphDynaBean dynaBean = null;
423       try{
424          dynaBean = (MorphDynaBean) dynaClass.newInstance();
425          dynaBean.setDynaBeanClass( dynaClass );
426       }catch( Exception e ){
427          throw new JSONException( e );
428       }
429       return dynaBean;
430    }
431 
432    /**
433     * Produce a string from a Number.
434     *
435     * @param n A Number
436     * @return A String.
437     * @throws JSONException If n is a non-finite number.
438     */
439    public static String numberToString( Number n ) {
440       if( n == null ){
441          throw new JSONException( "Null pointer" );
442       }
443       testValidity( n );
444 
445       // Shave off trailing zeros and decimal point, if possible.
446 
447       String s = n.toString();
448       if( s.indexOf( '.' ) > 0 && s.indexOf( 'e' ) < 0 && s.indexOf( 'E' ) < 0 ){
449          while( s.endsWith( "0" ) ){
450             s = s.substring( 0, s.length() - 1 );
451          }
452          if( s.endsWith( "." ) ){
453             s = s.substring( 0, s.length() - 1 );
454          }
455       }
456       return s;
457    }
458 
459    /**
460     * Produce a string in double quotes with backslash sequences in all the
461     * right places. A backslash will be inserted within </, allowing JSON text
462     * to be delivered in HTML. In JSON text, a string cannot contain a control
463     * character or an unescaped quote or backslash.<br>
464     * <strong>CAUTION:</strong> if <code>string</code> represents a
465     * javascript function, translation of characters will not take place. This
466     * will produce a non-conformant JSON text.
467     *
468     * @param string A String
469     * @return A String correctly formatted for insertion in a JSON text.
470     */
471    public static String quote( String string ) {
472       if( isFunction( string ) ) {
473          return string;
474       }
475       if( string == null || string.length() == 0 ) {
476          return "\"\"";
477       }
478 
479       char b;
480       char c = 0;
481       int i;
482       int len = string.length();
483       StringBuffer sb = new StringBuffer( len * 2 );
484       String t;
485       char[] chars = string.toCharArray();
486       char[] buffer = new char[1030];
487       int bufferIndex = 0;
488       sb.append( '"' );
489       for( i = 0; i < len; i += 1 ) {
490          if( bufferIndex > 1024 ) {
491             sb.append( buffer, 0, bufferIndex );
492             bufferIndex = 0;
493          }
494          b = c;
495          c = chars[i];
496          switch( c ) {
497             case '\\':
498             case '"':
499                buffer[bufferIndex++] = '\\';
500                buffer[bufferIndex++] = c;
501                break;
502             case '/':
503                if( b == '<' ) {
504                   buffer[bufferIndex++] = '\\';
505                }
506                buffer[bufferIndex++] = c;
507                break;
508             default:
509                if( c < ' ' ) {
510                   switch( c ) {
511                      case '\b':
512                         buffer[bufferIndex++] = '\\';
513                         buffer[bufferIndex++] = 'b';
514                         break;
515                      case '\t':
516                         buffer[bufferIndex++] = '\\';
517                         buffer[bufferIndex++] = 't';
518                         break;
519                      case '\n':
520                         buffer[bufferIndex++] = '\\';
521                         buffer[bufferIndex++] = 'n';
522                         break;
523                      case '\f':
524                         buffer[bufferIndex++] = '\\';
525                         buffer[bufferIndex++] = 'f';
526                         break;
527                      case '\r':
528                         buffer[bufferIndex++] = '\\';
529                         buffer[bufferIndex++] = 'r';
530                         break;
531                      default:
532                         t = "000" + Integer.toHexString( c );
533                         int tLength = t.length();
534                         buffer[bufferIndex++] = '\\';
535                         buffer[bufferIndex++] = 'u';
536                         buffer[bufferIndex++] = t.charAt( tLength - 4 );
537                         buffer[bufferIndex++] = t.charAt( tLength - 3 );
538                         buffer[bufferIndex++] = t.charAt( tLength - 2 );
539                         buffer[bufferIndex++] = t.charAt( tLength - 1 );
540                   }
541                } else {
542                   buffer[bufferIndex++] = c;
543                }
544          }
545       }
546       sb.append( buffer, 0, bufferIndex );
547       sb.append( '"' );
548       return sb.toString();
549    }
550 
551    /**
552     * Strips any single-quotes or double-quotes from both sides of the string.
553     */
554    public static String stripQuotes( String input ) {
555       if( input.length() < 2 ){
556          return input;
557       }else if( input.startsWith( SINGLE_QUOTE ) && input.endsWith( SINGLE_QUOTE ) ){
558          return input.substring( 1, input.length() - 1 );
559       }else if( input.startsWith( DOUBLE_QUOTE ) && input.endsWith( DOUBLE_QUOTE ) ){
560          return input.substring( 1, input.length() - 1 );
561       }else{
562          return input;
563       }
564    }
565 
566    /**
567     * Returns true if the input has single-quotes or double-quotes at both sides.
568     */
569    public static boolean hasQuotes( String input ) {
570       if( input == null || input.length() < 2 ){
571          return false;
572       }
573       return input.startsWith( SINGLE_QUOTE ) && input.endsWith( SINGLE_QUOTE ) ||
574          input.startsWith( DOUBLE_QUOTE ) && input.endsWith( DOUBLE_QUOTE );
575    }
576    
577    public static boolean isJsonKeyword( String input, JsonConfig jsonConfig ) {
578       if( input == null ){
579          return false;
580       }
581       return "null".equals( input ) ||
582               "true".equals( input ) ||
583               "false".equals( input ) ||
584               (jsonConfig.isJavascriptCompliant() && "undefined".equals( input ));
585    }
586    
587    /**
588     * Throw an exception if the object is an NaN or infinite number.
589     *
590     * @param o The object to test.
591     * @throws JSONException If o is a non-finite number.
592     */
593    public static void testValidity( Object o ) {
594       if( o != null ){
595          if( o instanceof Double ){
596             if( ((Double) o).isInfinite() || ((Double) o).isNaN() ){
597                throw new JSONException( "JSON does not allow non-finite numbers" );
598             }
599          }else if( o instanceof Float ){
600             if( ((Float) o).isInfinite() || ((Float) o).isNaN() ){
601                throw new JSONException( "JSON does not allow non-finite numbers." );
602             }
603          }else if( o instanceof BigDecimal || o instanceof BigInteger ){
604             // ok
605             return;
606          }
607       }
608    }
609 
610    /**
611     * Transforms a Number into a valid javascript number.<br>
612     * Float gets promoted to Double.<br>
613     * Byte and Short get promoted to Integer.<br>
614     * Long gets downgraded to Integer if possible.<br>
615     */
616    public static Number transformNumber( Number input ) {
617       if( input instanceof Float ){
618          return new Double( input.toString() );
619       }else if( input instanceof Short ){
620          return new Integer( input.intValue() );
621       }else if( input instanceof Byte ){
622          return new Integer( input.intValue() );
623       }else if( input instanceof Long ){
624          Long max = new Long( Integer.MAX_VALUE );
625          if( input.longValue() <= max.longValue() && input.longValue() >= Integer.MIN_VALUE ){
626             return new Integer( input.intValue() );
627          }
628       }
629 
630       return input;
631    }
632 
633    /**
634     * Make a JSON text of an Object value. If the object has an
635     * value.toJSONString() method, then that method will be used to produce the
636     * JSON text. The method is required to produce a strictly conforming text.
637     * If the object does not contain a toJSONString method (which is the most
638     * common case), then a text will be produced by the rules.
639     * <p>
640     * Warning: This method assumes that the data structure is acyclical.
641     *
642     * @param value The value to be serialized.
643     * @return a printable, displayable, transmittable representation of the
644     *         object, beginning with <code>{</code>&nbsp;<small>(left brace)</small>
645     *         and ending with <code>}</code>&nbsp;<small>(right brace)</small>.
646     * @throws JSONException If the value is or contains an invalid number.
647     */
648    public static String valueToString( Object value ) {
649       if( value == null || isNull( value ) ){
650          return "null";
651       }
652       if( value instanceof JSONFunction ){
653          return ((JSONFunction) value).toString();
654       }
655       if( value instanceof JSONString ){
656          Object o;
657          try{
658             o = ((JSONString) value).toJSONString();
659          }catch( Exception e ){
660             throw new JSONException( e );
661          }
662          if( o instanceof String ){
663             return (String) o;
664          }
665          throw new JSONException( "Bad value from toJSONString: " + o );
666       }
667       if( value instanceof Number ){
668          return numberToString( (Number) value );
669       }
670       if( value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray ){
671          return value.toString();
672       }
673       return quote( value.toString() );
674    }
675 
676    /**
677     * Make a prettyprinted JSON text of an object value.
678     * <p>
679     * Warning: This method assumes that the data structure is acyclical.
680     *
681     * @param value The value to be serialized.
682     * @param indentFactor The number of spaces to add to each level of
683     *        indentation.
684     * @param indent The indentation of the top level.
685     * @return a printable, displayable, transmittable representation of the
686     *         object, beginning with <code>{</code>&nbsp;<small>(left brace)</small>
687     *         and ending with <code>}</code>&nbsp;<small>(right brace)</small>.
688     * @throws JSONException If the object contains an invalid number.
689     */
690    public static String valueToString( Object value, int indentFactor, int indent ) {
691       if( value == null || isNull( value ) ){
692          return "null";
693       }
694       if( value instanceof JSONFunction ){
695          return ((JSONFunction) value).toString();
696       }
697       if( value instanceof JSONString ){
698          return ((JSONString) value).toJSONString();
699       }
700       if( value instanceof Number ){
701          return numberToString( (Number) value );
702       }
703       if( value instanceof Boolean ){
704          return value.toString();
705       }
706       if( value instanceof JSONObject ){
707          return ((JSONObject) value).toString( indentFactor, indent );
708       }
709       if( value instanceof JSONArray ){
710          return ((JSONArray) value).toString( indentFactor, indent );
711       }
712       return quote( value.toString() );
713    }
714 
715    /**
716     * Finds out if n represents a BigInteger
717     *
718     * @return true if n is instanceOf BigInteger or the literal value can be
719     *         evaluated as a BigInteger
720     */
721    private static boolean isBigDecimal( Number n ) {
722       if( n instanceof BigDecimal ){
723          return true;
724       }
725       try{
726          new BigDecimal( String.valueOf( n ) );
727          return true;
728       }catch( NumberFormatException e ){
729          return false;
730       }
731    }
732 
733    /**
734     * Finds out if n represents a BigInteger
735     *
736     * @return true if n is instanceOf BigInteger or the literal value can be
737     *         evaluated as a BigInteger
738     */
739    private static boolean isBigInteger( Number n ) {
740       if( n instanceof BigInteger ){
741          return true;
742       }
743       try{
744          new BigInteger( String.valueOf( n ) );
745          return true;
746       }catch( NumberFormatException e ){
747          return false;
748       }
749    }
750 
751    /**
752     * Finds out if n represents a Double.
753     *
754     * @return true if n is instanceOf Double or the literal value can be
755     *         evaluated as a Double.
756     */
757    private static boolean isDouble( Number n ) {
758       if( n instanceof Double ){
759          return true;
760       }
761       try{
762          double d = Double.parseDouble( String.valueOf( n ) );
763          return !Double.isInfinite( d );
764       }catch( NumberFormatException e ){
765          return false;
766       }
767    }
768 
769    /**
770     * Finds out if n represents a Float.
771     *
772     * @return true if n is instanceOf Float or the literal value can be
773     *         evaluated as a Float.
774     */
775    private static boolean isFloat( Number n ) {
776       if( n instanceof Float ){
777          return true;
778       }
779       try{
780          float f = Float.parseFloat( String.valueOf( n ) );
781          return !Float.isInfinite( f );
782       }catch( NumberFormatException e ){
783          return false;
784       }
785    }
786 
787    /**
788     * Finds out if n represents an Integer.
789     *
790     * @return true if n is instanceOf Integer or the literal value can be
791     *         evaluated as an Integer.
792     */
793    private static boolean isInteger( Number n ) {
794       if( n instanceof Integer ){
795          return true;
796       }
797       try{
798          Integer.parseInt( String.valueOf( n ) );
799          return true;
800       }catch( NumberFormatException e ){
801          return false;
802       }
803    }
804 
805    /**
806     * Finds out if n represents a Long.
807     *
808     * @return true if n is instanceOf Long or the literal value can be evaluated
809     *         as a Long.
810     */
811    private static boolean isLong( Number n ) {
812       if( n instanceof Long ){
813          return true;
814       }
815       try{
816          Long.parseLong( String.valueOf( n ) );
817          return true;
818       }catch( NumberFormatException e ){
819          return false;
820       }
821    }
822 
823    private JSONUtils() {
824       super();
825    }
826 }