Coverage Report - net.sf.json.util.JSONUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
JSONUtils
0%
0/288
0%
0/349
4.953
 
 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  0
    private static final MorpherRegistry morpherRegistry = new MorpherRegistry();
 62  
 
 63  
    static{
 64  
       // register standard morphers
 65  0
       MorphUtils.registerStandardMorphers( morpherRegistry );
 66  0
    }
 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  0
       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  0
          return jsonConfig.getJavaIdentifierTransformer()
 87  
                .transformToJavaIdentifier( key );
 88  0
       }catch( JSONException jsone ){
 89  0
          throw jsone;
 90  0
       }catch( Exception e ){
 91  0
          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  0
       if( Double.isInfinite( d ) || Double.isNaN( d ) ){
 104  0
          return "null";
 105  
       }
 106  
 
 107  
       // Shave off trailing zeros and decimal point, if possible.
 108  
 
 109  0
       String s = Double.toString( d );
 110  0
       if( s.indexOf( '.' ) > 0 && s.indexOf( 'e' ) < 0 && s.indexOf( 'E' ) < 0 ){
 111  0
          while( s.endsWith( "0" ) ){
 112  0
             s = s.substring( 0, s.length() - 1 );
 113  
          }
 114  0
          if( s.endsWith( "." ) ){
 115  0
             s = s.substring( 0, s.length() - 1 );
 116  
          }
 117  
       }
 118  0
       return s;
 119  
    }
 120  
 
 121  
    /**
 122  
     * Returns the body of a function literal.
 123  
     */
 124  
    public static String getFunctionBody( String function ) {
 125  0
       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  0
       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  0
       if( !type.isArray() ){
 140  0
          return type;
 141  
       }
 142  0
       return getInnerComponentType( type.getComponentType() );
 143  
    }
 144  
 
 145  
    /**
 146  
     * Returns the singleton MorpherRegistry.
 147  
     */
 148  
    public static MorpherRegistry getMorpherRegistry() {
 149  0
       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  0
       Map properties = new HashMap();
 157  0
       for( Iterator keys = jsonObject.keys(); keys.hasNext(); ){
 158  0
          String key = (String) keys.next();
 159  
          /*
 160  
           * String parsedKey = key; if( !JSONUtils.isJavaIdentifier( parsedKey ) ){
 161  
           * parsedKey = JSONUtils.convertToJavaIdentifier( key ); }
 162  
           */
 163  0
          properties.put( key, getTypeClass( jsonObject.get( key ) ) );
 164  0
       }
 165  0
       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  0
       if( isNull( obj ) ){
 174  0
          return Object.class;
 175  0
       }else if( isArray( obj ) ){
 176  0
          return List.class;
 177  0
       }else if( isFunction( obj ) ){
 178  0
          return JSONFunction.class;
 179  0
       }else if( isBoolean( obj ) ){
 180  0
          return Boolean.class;
 181  0
       }else if( isNumber( obj ) ){
 182  0
          Number n = (Number) obj;
 183  0
          if( isInteger( n ) ){
 184  0
             return Integer.class;
 185  0
          }else if( isLong( n ) ){
 186  0
             return Long.class;
 187  0
          }else if( isFloat( n ) ){
 188  0
             return Float.class;
 189  0
          }else if( isBigInteger( n ) ){
 190  0
             return BigInteger.class;
 191  0
          }else if( isBigDecimal( n ) ){
 192  0
             return BigDecimal.class;
 193  0
          }else if( isDouble( n ) ){
 194  0
             return Double.class;
 195  
          }else{
 196  0
             throw new JSONException( "Unsupported type" );
 197  
          }
 198  0
       }else if( isString( obj ) ){
 199  0
          return String.class;
 200  0
       }else if( isObject( obj ) ){
 201  0
          return Object.class;
 202  
       }else{
 203  0
          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  0
       if( value == null ){
 216  0
          return JSONNull.getInstance()
 217  
                .hashCode();
 218  0
       }else if( value instanceof JSON || value instanceof String || value instanceof JSONFunction ){
 219  0
          return value.hashCode();
 220  
       }else{
 221  0
          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  0
       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  0
       if( (obj != null && obj.getClass()
 239  
             .isArray()) || (obj instanceof Collection) || (obj instanceof JSONArray) ){
 240  0
          return true;
 241  
       }
 242  0
       return false;
 243  
    }
 244  
 
 245  
    /**
 246  
     * Tests if Class represents a Boolean or primitive boolean
 247  
     */
 248  
    public static boolean isBoolean( Class clazz ) {
 249  0
       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  0
       if( (obj instanceof Boolean) || (obj != null && obj.getClass() == Boolean.TYPE) ){
 258  0
          return true;
 259  
       }
 260  0
       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  0
       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  0
       if( obj instanceof String ){
 278  0
          String str = (String) obj;
 279  0
          return str.startsWith( FUNCTION_PREFIX ) && RegexpUtils.getMatcher( FUNCTION_PATTERN, true ).matches( str );
 280  
       }
 281  0
       if( obj instanceof JSONFunction ){
 282  0
          return true;
 283  
       }
 284  0
       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  0
       if( obj instanceof String ){
 293  0
          String str = (String) obj;
 294  0
          return str.startsWith( FUNCTION_PREFIX ) && RegexpUtils.getMatcher( FUNCTION_HEADER_PATTERN, true ).matches( str );
 295  
       }
 296  0
       return false;
 297  
    }
 298  
 
 299  
    /**
 300  
     * Returns trus if str represents a valid Java identifier.
 301  
     */
 302  
    public static boolean isJavaIdentifier( String str ) {
 303  0
       if( str.length() == 0 || !Character.isJavaIdentifierStart( str.charAt( 0 ) ) ){
 304  0
          return false;
 305  
       }
 306  0
       for( int i = 1; i < str.length(); i++ ){
 307  0
          if( !Character.isJavaIdentifierPart( str.charAt( i ) ) ){
 308  0
             return false;
 309  
          }
 310  
       }
 311  0
       return true;
 312  
    }
 313  
 
 314  
    /**
 315  
     * Tests if the obj is a javaScript null.
 316  
     */
 317  
    public static boolean isNull( Object obj ) {
 318  0
       if( obj instanceof JSONObject ){
 319  0
          return ((JSONObject) obj).isNullObject();
 320  
       }
 321  0
       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  0
       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  0
       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  0
          return true;
 346  
       }
 347  
 
 348  0
       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  0
       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  0
       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  0
       if( (obj instanceof String)
 372  
             || (obj instanceof Character)
 373  
             || (obj != null && (obj.getClass() == Character.TYPE || String.class.isAssignableFrom( obj.getClass() ))) ){
 374  0
          return true;
 375  
       }
 376  0
       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  0
       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  0
       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  0
       Map props = getProperties( jsonObject );
 410  0
       for( Iterator entries = props.entrySet()
 411  0
             .iterator(); entries.hasNext(); ){
 412  0
          Map.Entry entry = (Map.Entry) entries.next();
 413  0
          String key = (String) entry.getKey();
 414  0
          if( !JSONUtils.isJavaIdentifier( key ) ){
 415  0
             String parsedKey = JSONUtils.convertToJavaIdentifier( key, jsonConfig );
 416  0
             if( parsedKey.compareTo( key ) != 0 ){
 417  0
                props.put( parsedKey, props.remove( key ) );
 418  
             }
 419  
          }
 420  0
       }
 421  0
       MorphDynaClass dynaClass = new MorphDynaClass( props );
 422  0
       MorphDynaBean dynaBean = null;
 423  
       try{
 424  0
          dynaBean = (MorphDynaBean) dynaClass.newInstance();
 425  0
          dynaBean.setDynaBeanClass( dynaClass );
 426  0
       }catch( Exception e ){
 427  0
          throw new JSONException( e );
 428  0
       }
 429  0
       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  0
       if( n == null ){
 441  0
          throw new JSONException( "Null pointer" );
 442  
       }
 443  0
       testValidity( n );
 444  
 
 445  
       // Shave off trailing zeros and decimal point, if possible.
 446  
 
 447  0
       String s = n.toString();
 448  0
       if( s.indexOf( '.' ) > 0 && s.indexOf( 'e' ) < 0 && s.indexOf( 'E' ) < 0 ){
 449  0
          while( s.endsWith( "0" ) ){
 450  0
             s = s.substring( 0, s.length() - 1 );
 451  
          }
 452  0
          if( s.endsWith( "." ) ){
 453  0
             s = s.substring( 0, s.length() - 1 );
 454  
          }
 455  
       }
 456  0
       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  0
       if( isFunction( string ) ) {
 473  0
          return string;
 474  
       }
 475  0
       if( string == null || string.length() == 0 ) {
 476  0
          return "\"\"";
 477  
       }
 478  
 
 479  
       char b;
 480  0
       char c = 0;
 481  
       int i;
 482  0
       int len = string.length();
 483  0
       StringBuffer sb = new StringBuffer( len * 2 );
 484  
       String t;
 485  0
       char[] chars = string.toCharArray();
 486  0
       char[] buffer = new char[1030];
 487  0
       int bufferIndex = 0;
 488  0
       sb.append( '"' );
 489  0
       for( i = 0; i < len; i += 1 ) {
 490  0
          if( bufferIndex > 1024 ) {
 491  0
             sb.append( buffer, 0, bufferIndex );
 492  0
             bufferIndex = 0;
 493  
          }
 494  0
          b = c;
 495  0
          c = chars[i];
 496  0
          switch( c ) {
 497  
             case '\\':
 498  
             case '"':
 499  0
                buffer[bufferIndex++] = '\\';
 500  0
                buffer[bufferIndex++] = c;
 501  0
                break;
 502  
             case '/':
 503  0
                if( b == '<' ) {
 504  0
                   buffer[bufferIndex++] = '\\';
 505  
                }
 506  0
                buffer[bufferIndex++] = c;
 507  0
                break;
 508  
             default:
 509  0
                if( c < ' ' ) {
 510  0
                   switch( c ) {
 511  
                      case '\b':
 512  0
                         buffer[bufferIndex++] = '\\';
 513  0
                         buffer[bufferIndex++] = 'b';
 514  0
                         break;
 515  
                      case '\t':
 516  0
                         buffer[bufferIndex++] = '\\';
 517  0
                         buffer[bufferIndex++] = 't';
 518  0
                         break;
 519  
                      case '\n':
 520  0
                         buffer[bufferIndex++] = '\\';
 521  0
                         buffer[bufferIndex++] = 'n';
 522  0
                         break;
 523  
                      case '\f':
 524  0
                         buffer[bufferIndex++] = '\\';
 525  0
                         buffer[bufferIndex++] = 'f';
 526  0
                         break;
 527  
                      case '\r':
 528  0
                         buffer[bufferIndex++] = '\\';
 529  0
                         buffer[bufferIndex++] = 'r';
 530  0
                         break;
 531  
                      default:
 532  0
                         t = "000" + Integer.toHexString( c );
 533  0
                         int tLength = t.length();
 534  0
                         buffer[bufferIndex++] = '\\';
 535  0
                         buffer[bufferIndex++] = 'u';
 536  0
                         buffer[bufferIndex++] = t.charAt( tLength - 4 );
 537  0
                         buffer[bufferIndex++] = t.charAt( tLength - 3 );
 538  0
                         buffer[bufferIndex++] = t.charAt( tLength - 2 );
 539  0
                         buffer[bufferIndex++] = t.charAt( tLength - 1 );
 540  
                   }
 541  
                } else {
 542  0
                   buffer[bufferIndex++] = c;
 543  
                }
 544  
          }
 545  
       }
 546  0
       sb.append( buffer, 0, bufferIndex );
 547  0
       sb.append( '"' );
 548  0
       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  0
       if( input.length() < 2 ){
 556  0
          return input;
 557  0
       }else if( input.startsWith( SINGLE_QUOTE ) && input.endsWith( SINGLE_QUOTE ) ){
 558  0
          return input.substring( 1, input.length() - 1 );
 559  0
       }else if( input.startsWith( DOUBLE_QUOTE ) && input.endsWith( DOUBLE_QUOTE ) ){
 560  0
          return input.substring( 1, input.length() - 1 );
 561  
       }else{
 562  0
          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  0
       if( input == null || input.length() < 2 ){
 571  0
          return false;
 572  
       }
 573  0
       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  0
       if( input == null ){
 579  0
          return false;
 580  
       }
 581  0
       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  0
       if( o != null ){
 595  0
          if( o instanceof Double ){
 596  0
             if( ((Double) o).isInfinite() || ((Double) o).isNaN() ){
 597  0
                throw new JSONException( "JSON does not allow non-finite numbers" );
 598  
             }
 599  0
          }else if( o instanceof Float ){
 600  0
             if( ((Float) o).isInfinite() || ((Float) o).isNaN() ){
 601  0
                throw new JSONException( "JSON does not allow non-finite numbers." );
 602  
             }
 603  0
          }else if( o instanceof BigDecimal || o instanceof BigInteger ){
 604  
             // ok
 605  0
             return;
 606  
          }
 607  
       }
 608  0
    }
 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  0
       if( input instanceof Float ){
 618  0
          return new Double( input.toString() );
 619  0
       }else if( input instanceof Short ){
 620  0
          return new Integer( input.intValue() );
 621  0
       }else if( input instanceof Byte ){
 622  0
          return new Integer( input.intValue() );
 623  0
       }else if( input instanceof Long ){
 624  0
          Long max = new Long( Integer.MAX_VALUE );
 625  0
          if( input.longValue() <= max.longValue() && input.longValue() >= Integer.MIN_VALUE ){
 626  0
             return new Integer( input.intValue() );
 627  
          }
 628  
       }
 629  
 
 630  0
       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  0
       if( value == null || isNull( value ) ){
 650  0
          return "null";
 651  
       }
 652  0
       if( value instanceof JSONFunction ){
 653  0
          return ((JSONFunction) value).toString();
 654  
       }
 655  0
       if( value instanceof JSONString ){
 656  
          Object o;
 657  
          try{
 658  0
             o = ((JSONString) value).toJSONString();
 659  0
          }catch( Exception e ){
 660  0
             throw new JSONException( e );
 661  0
          }
 662  0
          if( o instanceof String ){
 663  0
             return (String) o;
 664  
          }
 665  0
          throw new JSONException( "Bad value from toJSONString: " + o );
 666  
       }
 667  0
       if( value instanceof Number ){
 668  0
          return numberToString( (Number) value );
 669  
       }
 670  0
       if( value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray ){
 671  0
          return value.toString();
 672  
       }
 673  0
       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  0
       if( value == null || isNull( value ) ){
 692  0
          return "null";
 693  
       }
 694  0
       if( value instanceof JSONFunction ){
 695  0
          return ((JSONFunction) value).toString();
 696  
       }
 697  0
       if( value instanceof JSONString ){
 698  0
          return ((JSONString) value).toJSONString();
 699  
       }
 700  0
       if( value instanceof Number ){
 701  0
          return numberToString( (Number) value );
 702  
       }
 703  0
       if( value instanceof Boolean ){
 704  0
          return value.toString();
 705  
       }
 706  0
       if( value instanceof JSONObject ){
 707  0
          return ((JSONObject) value).toString( indentFactor, indent );
 708  
       }
 709  0
       if( value instanceof JSONArray ){
 710  0
          return ((JSONArray) value).toString( indentFactor, indent );
 711  
       }
 712  0
       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  0
       if( n instanceof BigDecimal ){
 723  0
          return true;
 724  
       }
 725  
       try{
 726  0
          new BigDecimal( String.valueOf( n ) );
 727  0
          return true;
 728  0
       }catch( NumberFormatException e ){
 729  0
          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  0
       if( n instanceof BigInteger ){
 741  0
          return true;
 742  
       }
 743  
       try{
 744  0
          new BigInteger( String.valueOf( n ) );
 745  0
          return true;
 746  0
       }catch( NumberFormatException e ){
 747  0
          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  0
       if( n instanceof Double ){
 759  0
          return true;
 760  
       }
 761  
       try{
 762  0
          double d = Double.parseDouble( String.valueOf( n ) );
 763  0
          return !Double.isInfinite( d );
 764  0
       }catch( NumberFormatException e ){
 765  0
          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  0
       if( n instanceof Float ){
 777  0
          return true;
 778  
       }
 779  
       try{
 780  0
          float f = Float.parseFloat( String.valueOf( n ) );
 781  0
          return !Float.isInfinite( f );
 782  0
       }catch( NumberFormatException e ){
 783  0
          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  0
       if( n instanceof Integer ){
 795  0
          return true;
 796  
       }
 797  
       try{
 798  0
          Integer.parseInt( String.valueOf( n ) );
 799  0
          return true;
 800  0
       }catch( NumberFormatException e ){
 801  0
          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  0
       if( n instanceof Long ){
 813  0
          return true;
 814  
       }
 815  
       try{
 816  0
          Long.parseLong( String.valueOf( n ) );
 817  0
          return true;
 818  0
       }catch( NumberFormatException e ){
 819  0
          return false;
 820  
       }
 821  
    }
 822  
 
 823  
    private JSONUtils() {
 824  0
       super();
 825  0
    }
 826  
 }