All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.antiaction.common.json.JSONStreamUnmarshaller Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
/*
 * JSON library.
 * Copyright 2012-2013 Antiaction (http://antiaction.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.antiaction.common.json;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.CharBuffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * De-serialize a JSON data stream into Java Object(s).
 *
 * @author Nicholas
 * Created on 17/10/2013
 */
public class JSONStreamUnmarshaller {

	private static final int S_START = 0;
	private static final int S_OBJECT = 1;
	private static final int S_OBJECT_NAME = 20;
	private static final int S_OBJECT_COLON = 21;
	private static final int S_OBJECT_VALUE = 23;
	private static final int S_OBJECT_VALUE_NEXT = 24;
	private static final int S_OBJECT_NAME_NEXT = 25;
	private static final int S_ARRAY = 2;
	private static final int S_ARRAY_VALUE = 3;
	private static final int S_ARRAY_NEXT = 4;
	private static final int S_VALUE_START = 5;
	private static final int S_STRING = 6;
	private static final int S_STRING_UNESCAPE = 7;
	private static final int S_STRING_UNHEX = 8;
	private static final int S_CONSTANT = 9;
	private static final int S_NUMBER_MINUS = 10;
	private static final int S_NUMBER_ZERO = 11;
	private static final int S_NUMBER_INTEGER = 12;
	private static final int S_NUMBER_DECIMAL = 13;
	private static final int S_NUMBER_DECIMALS = 14;
	private static final int S_NUMBER_E = 15;
	private static final int S_NUMBER_EXPONENT = 16;
	private static final int S_NUMBER_EXPONENTS = 17;
	private static final int S_EOF = 18;

	private static final int T_NULL = 1;
	private static final int T_BOOLEAN = 2;
	private static final int T_STRING = 3;
	private static final int T_NUMBER = 4;
	private static final int T_OBJECT = 5;

	protected JSONObjectMappings objectMappings;

	protected Map classMappings;

	/** Temporary StringBuilder used to store JSON strings and values. */
	protected StringBuilder sbStr = new StringBuilder();

	private static final class StackEntry {
		Object curObj;
		Map fieldMappingsMap;
		JSONObjectFieldMapping fieldMapping;
		int rstate;
	}

	public JSONStreamUnmarshaller(JSONObjectMappings objectMappings) {
		this.objectMappings = objectMappings;
		this.classMappings = objectMappings.classMappings;
	}

	public  T toObject(InputStream in, JSONDecoder decoder, Class clazz) throws IOException, JSONException {
		return toObject( in, decoder, clazz, null );
	}

	public  T toObject(InputStream in, JSONDecoder decoder, Class clazz, JSONConverterAbstract[] converters) throws IOException, JSONException {
		Boolean booleanVal = null;
		Integer intVal = null;
		Long longVal = null;
		Float floatVal = null;
		Double doubleVal = null;
		BigInteger bigIntegerVal = null;
		BigDecimal bigDecimalVal = null;
		String stringVal = null;
		byte[] byteArray = null;
		Object object = null;

		JSONArray json_array;
		List json_values;
		boolean[] arrayOf_boolean;
		int[] arrayOf_int;
		long[] arrayOf_long;
		float[] arrayOf_float;
		double[] arrayOf_double;
		Boolean[] arrayOf_Boolean;
		Integer[] arrayOf_Integer;
		Long[] arrayOf_Long;
		Float[] arrayOf_Float;
		Double[] arrayOf_Double;
		BigInteger[] arrayOf_BigInteger;
		BigDecimal[] arrayOf_BigDecimal;
		String[] arrayOf_String;
		Object[] arrayOf_Object;

		JSONObjectMapping json_om = classMappings.get( clazz.getName() );
		if ( json_om == null ) {
			throw new IllegalArgumentException( "Class '" + clazz.getName() + "' not registered." );
		}
		if ( json_om.converters == true && converters == null ) {
			throw new JSONException( "Class '" + clazz.getName() + "' may required converters!" );
		}

		T rootObj = null;

		LinkedList stack = new LinkedList();
		StackEntry stackEntry = null;

		char[] charArray = new char[ 1024 ];
		CharBuffer charBuffer = CharBuffer.wrap( charArray );

		decoder.init( in );
		decoder.fill( charBuffer );

		// Switch buffer to read mode.
		charBuffer.flip();

		int pos = charBuffer.position();
		int limit = charBuffer.limit();

		int hexValue = 0;
		int hexCount = 0;
		int i;
		String constant;

		int y = 1;
		int x = 1;

		int json_value_type = 0;

		Object curObj = null;
		Map fieldMappingsMap;
		JSONObjectFieldMapping fieldMapping = null;

		int state = S_START;
		int rstate = -1;
		boolean bLoop = true;
		char c;
		try {
			rootObj = clazz.newInstance();
			curObj = rootObj;

			fieldMappingsMap = json_om.fieldMappingsMap;

			while ( bLoop ) {
				while ( pos < limit ) {
					c = charArray[ pos ];
					++x;
					switch ( state ) {
					case S_START:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case '{':
							// TODO
							//current = new JSONObject();
							//stack.add( new StackEntry( current, json_name ) );
							state = S_OBJECT;
							break;
						case '[':
							// TODO
							//current = new JSONArray();
							//stack.add( new StackEntry( current, json_name ) );
							state = S_ARRAY;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_OBJECT:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case '}':
							/*
							if ( stack.size() == 0 ) {
								throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
							}
							*/
							stackEntry = stack.removeLast();
							// TODO
							//current = entry.json_structure;
							//json_name = stackEntry.json_name;
							//json_value = current;
							if ( stack.size() > 0 ) {
								//current = stack.getLast().json_structure;
								/*
								if (current.type == JSONConstants.VT_OBJECT) {
									state = S_OBJECT_VALUE;
								}
								else {
									state = S_ARRAY_VALUE;
								}
								*/
							}
							else {
								state = S_EOF;
							}
							break;
						case '"':
							sbStr.setLength( 0 );
							state = S_STRING;
							rstate = S_OBJECT_NAME;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_OBJECT_NAME:
						fieldMapping = fieldMappingsMap.get( stringVal );
						state = S_OBJECT_COLON;
						// debug
						//System.out.println( stringVal );
					case S_OBJECT_COLON:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case ':':
							state = S_VALUE_START;
							rstate = S_OBJECT_VALUE;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_OBJECT_VALUE:
						switch ( json_value_type ) {
						case T_NULL:
							if ( !fieldMapping.nullable ) {
								throw new JSONException( "Field '" + fieldMapping.fieldName + "'/'" + fieldMapping.jsonName + "' is not nullable." );
							}
							break;
						case T_BOOLEAN:
							switch ( fieldMapping.type ) {
							case JSONObjectMappingConstants.T_PRIMITIVE_BOOLEAN:
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//booleanVal = converters[ fieldMapping.converterId ].getBoolean( fieldMapping.fieldName, json_value );
								}
								if ( booleanVal == null ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
								}
								fieldMapping.field.setBoolean( curObj, booleanVal );
								break;
							case JSONObjectMappingConstants.T_BOOLEAN:
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//booleanVal = converters[ fieldMapping.converterId ].getBoolean( fieldMapping.fieldName, json_value );
								}
								if ( booleanVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, booleanVal );
								break;
							default:
								throw new JSONException( "Wrong type." );
							}
							break;
						case T_STRING:
							switch ( fieldMapping.type ) {
							case JSONObjectMappingConstants.T_STRING:
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//strVal = converters[ fieldMapping.converterId ].getString( fieldMapping.fieldName, json_string );
								}
								if ( stringVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, stringVal );
								break;
							case JSONObjectMappingConstants.T_BYTEARRAY:
								if ( fieldMapping.converterId == -1 ) {
									byteArray = stringVal.getBytes();
								}
								else {
									// TODO
									//byteArray = converters[ fieldMapping.converterId ].getBytes( fieldMapping.fieldName, json_string );
								}
								if ( byteArray == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, byteArray );
								break;
							default:
								throw new JSONException( "Wrong type." );
							}
							break;
						case T_OBJECT:
							switch ( fieldMapping.type ) {
							case JSONObjectMappingConstants.T_OBJECT:
								if ( object != null ) {
									// TODO
									//object = toObject( object, fieldMapping.clazz, converters );
								}
								if ( object == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, object );
								break;
							default:
								throw new JSONException( "Wrong type." );
							}
							break;
						case T_NUMBER:
							switch ( fieldMapping.type ) {
							case JSONObjectMappingConstants.T_PRIMITIVE_BOOLEAN:
								if ( "1".equals( stringVal ) ) {
									booleanVal = true;
								}
								else if ( "0".equals( stringVal ) ) {
									booleanVal = false;
								}
								else {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is a boolean and can not be '" + stringVal + "'." );
								}
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//booleanVal = converters[ fieldMapping.converterId ].getBoolean( fieldMapping.fieldName, json_value );
									booleanVal = null;
								}
								if ( booleanVal == null ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
								}
								fieldMapping.field.setBoolean( curObj, booleanVal );
								break;
							case JSONObjectMappingConstants.T_BOOLEAN:
								if ( "1".equals( stringVal ) ) {
									booleanVal = true;
								}
								else if ( "0".equals( stringVal ) ) {
									booleanVal = false;
								}
								else {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is a boolean and can not be '" + stringVal + "'." );
								}
								if ( fieldMapping.converterId != -1 ) {
									// TODO
									//booleanVal = converters[ fieldMapping.converterId ].getBoolean( fieldMapping.fieldName, json_value );
									booleanVal = null;
								}
								if ( booleanVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, booleanVal );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_INTEGER:
								if ( fieldMapping.converterId == -1 ) {
									intVal = Integer.parseInt( stringVal );
								}
								else {
									// TODO
									//intVal = converters[ fieldMapping.converterId ].getInteger( fieldMapping.fieldName, json_value );
								}
								if ( intVal == null ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
								}
								fieldMapping.field.setInt( curObj, intVal );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_LONG:
								if ( fieldMapping.converterId == -1 ) {
									longVal = Long.parseLong( stringVal );
								}
								else {
									// TODO
									//longVal = converters[ fieldMapping.converterId ].getLong( fieldMapping.fieldName, json_value );
								}
								if ( longVal == null ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
								}
								fieldMapping.field.setLong( curObj, longVal );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_FLOAT:
								if ( fieldMapping.converterId == -1 ) {
									floatVal = Float.parseFloat( stringVal );
								}
								else {
									// TODO
									//floatVal = converters[ fieldMapping.converterId ].getFloat( fieldMapping.fieldName, json_value );
								}
								if ( floatVal == null ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
								}
								fieldMapping.field.setFloat( curObj, floatVal );
								break;
							case JSONObjectMappingConstants.T_PRIMITIVE_DOUBLE:
								if ( fieldMapping.converterId == -1 ) {
									doubleVal = Double.parseDouble( stringVal );
								}
								else {
									// TODO
									//doubleVal = converters[ fieldMapping.converterId ].getDouble( fieldMapping.fieldName, json_value );
								}
								if ( doubleVal == null ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is primitive and can not be null." );
								}
								fieldMapping.field.setDouble( curObj, doubleVal );
								break;
							case JSONObjectMappingConstants.T_INTEGER:
								if ( fieldMapping.converterId == -1 ) {
									intVal = Integer.parseInt( stringVal );
								}
								else {
									// TODO
									//intVal = converters[ fieldMapping.converterId ].getInteger( fieldMapping.fieldName, json_value );
								}
								if ( intVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, intVal );
								break;
							case JSONObjectMappingConstants.T_LONG:
								if ( fieldMapping.converterId == -1 ) {
									longVal = Long.parseLong( stringVal );
								}
								else {
									// TODO
									//longVal = converters[ fieldMapping.converterId ].getLong( fieldMapping.fieldName, json_value );
								}
								if ( longVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, longVal );
								break;
							case JSONObjectMappingConstants.T_FLOAT:
								if ( fieldMapping.converterId == -1 ) {
									floatVal = Float.parseFloat( stringVal );
								}
								else {
									// TODO
									//floatVal = converters[ fieldMapping.converterId ].getFloat( fieldMapping.fieldName, json_value );
								}
								if ( floatVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, floatVal );
								break;
							case JSONObjectMappingConstants.T_DOUBLE:
								if ( fieldMapping.converterId == -1 ) {
									doubleVal = Double.parseDouble( stringVal );
								}
								else {
									// TODO
									//doubleVal = converters[ fieldMapping.converterId ].getDouble( fieldMapping.fieldName, json_value );
								}
								if ( doubleVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, doubleVal );
								break;
							case JSONObjectMappingConstants.T_BIGINTEGER:
								if ( fieldMapping.converterId == -1 ) {
									bigIntegerVal = new BigInteger( stringVal );
								}
								else {
									// TODO
									//bigIntegerVal = converters[ fieldMapping.converterId ].getBigInteger( fieldMapping.fieldName, json_value );
								}
								if ( bigIntegerVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, bigIntegerVal );
								break;
							case JSONObjectMappingConstants.T_BIGDECIMAL:
								if ( fieldMapping.converterId == -1 ) {
									bigDecimalVal = new BigDecimal( stringVal );
								}
								else {
									// TODO
									//bigDecimalVal = converters[ fieldMapping.converterId ].getBigDecimal( fieldMapping.fieldName, json_value );
								}
								if ( bigDecimalVal == null && !fieldMapping.nullable ) {
									throw new JSONException( "Field '" + fieldMapping.fieldName + "' is not nullable." );
								}
								fieldMapping.field.set( curObj, bigDecimalVal );
								break;
							default:
								throw new JSONException( "Wrong type." );
							}
							break;
						default:
							throw new IllegalStateException( "Unknown json value type: " + json_value_type );
						}
						// TODO
						//current.put( json_name, json_value );
						state = S_OBJECT_VALUE_NEXT;
						// debug
						//System.out.println( json_value.toString() );
					case S_OBJECT_VALUE_NEXT:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case '}':
							/*
							if ( stack.size() == 0 ) {
								throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
							}
							*/
							if ( stack.size() > 0 ) {
								json_value_type = T_OBJECT;
								object = curObj;
								// TODO
								stackEntry = stack.removeLast();
								curObj = stackEntry.curObj;
								fieldMappingsMap = stackEntry.fieldMappingsMap;
								fieldMapping = stackEntry.fieldMapping;
								state = stackEntry.rstate;
								/*
								if (current.type == JSONConstants.VT_OBJECT) {
									state = S_OBJECT_VALUE;
								}
								else {
									state = S_ARRAY_VALUE;
								}
								*/
							}
							else {
								state = S_EOF;
							}
							break;
						case ',':
							state = S_OBJECT_NAME_NEXT;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_OBJECT_NAME_NEXT:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case '"':
							sbStr.setLength( 0 );
							state = S_STRING;
							rstate = S_OBJECT_NAME;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_ARRAY:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case ']':
							/*
							if ( stack.size() == 0 ) {
								throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
							}
							*/
							stackEntry = stack.removeLast();
							// TODO
							//current = entry.json_structure;
							//json_name = stackEntry.json_name;
							//json_value = current;
							if ( stack.size() > 0 ) {
								//current = stack.getLast().json_structure;
								/*
								if (current.type == JSONConstants.VT_OBJECT) {
									state = S_OBJECT_VALUE;
								}
								else {
									state = S_ARRAY_VALUE;
								}
								*/
							}
							else {
								state = S_EOF;
							}
							break;
						case '{':
							// TODO
							//current = new JSONObject();
							//stack.add( new StackEntry( current, json_name ) );
							state = S_OBJECT;
							break;
						case '[':
							// TODO
							//current = new JSONArray();
							//stack.add( new StackEntry( current, json_name ) );
							state = S_ARRAY;
							break;
						case '"':
							sbStr.setLength( 0 );
							state = S_STRING;
							rstate = S_ARRAY_VALUE;
							break;
						case 'f':
						case 'n':
						case 't':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_CONSTANT;
							rstate = S_ARRAY_VALUE;
							break;
						case '-':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_NUMBER_MINUS;
							rstate = S_ARRAY_VALUE;
							break;
						case '0':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_NUMBER_ZERO;
							rstate = S_ARRAY_VALUE;
							break;
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_NUMBER_INTEGER;
							rstate = S_ARRAY_VALUE;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_ARRAY_VALUE:
						// TODO
						//current.add( json_value );
						state = S_ARRAY_NEXT;
						// debug
						//System.out.println( json_value.toString() );
					case S_ARRAY_NEXT:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case ']':
							/*
							if ( stack.size() == 0 ) {
								throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
							}
							*/
							stackEntry = stack.removeLast();
							// TODO
							//current = entry.json_structure;
							//json_name = stackEntry.json_name;
							//json_value = current;
							if ( stack.size() > 0 ) {
								//current = stack.getLast().json_structure;
								/*
								if (current.type == JSONConstants.VT_OBJECT) {
									state = S_OBJECT_VALUE;
								}
								else {
									state = S_ARRAY_VALUE;
								}
								*/
							}
							else {
								state = S_EOF;
							}
							break;
						case ',':
							state = S_VALUE_START;
							rstate = S_ARRAY_VALUE;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_VALUE_START:
						// rstate should be set prior to this state.
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						case '{':
							// TODO
							stackEntry = new StackEntry();
							stackEntry.curObj = curObj;
							stackEntry.fieldMappingsMap = fieldMappingsMap;
							stackEntry.fieldMapping = fieldMapping;
							stackEntry.rstate = rstate;
							stack.add( stackEntry );

							curObj = fieldMapping.clazz.newInstance();
							json_om = classMappings.get( fieldMapping.clazz.getName() );
							if ( json_om == null ) {
								throw new IllegalArgumentException( "Class '" + fieldMapping.clazz.getName() + "' not registered." );
							}
							if ( json_om.converters == true && converters == null ) {
								throw new JSONException( "Class '" + fieldMapping.clazz.getName() + "' may required converters!" );
							}
							fieldMappingsMap = json_om.fieldMappingsMap;

							state = S_OBJECT;
							break;
						case '[':
							// TODO
							//current = new JSONArray();
							//stack.add( new StackEntry( current, json_name ) );
							state = S_ARRAY;
							break;
						case '"':
							sbStr.setLength( 0 );
							state = S_STRING;
							break;
						case 'f':
						case 'n':
						case 't':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_CONSTANT;
							break;
						case '-':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_NUMBER_MINUS;
							break;
						case '0':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_NUMBER_ZERO;
							break;
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.setLength( 0 );
							sbStr.append( c );
							state = S_NUMBER_INTEGER;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_STRING:
						switch ( c ) {
						case '"':
							json_value_type = T_STRING;
							stringVal = sbStr.toString();
							state = rstate;
							break;
						case '\\':
							state = S_STRING_UNESCAPE;
							break;
						default:
							if ( c < 32 ) {
								throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
							}
							sbStr.append( c );
							break;
						}
						++pos;
						break;
					case S_STRING_UNESCAPE:
						switch ( c ) {
						case '"':
							sbStr.append( '"');
							state = S_STRING;
							break;
						case '/':
							sbStr.append( '/' );
							state = S_STRING;
							break;
						case '\\':
							sbStr.append( '\\' );
							state = S_STRING;
							break;
						case 'b':
							sbStr.append( (char)0x08 );
							state = S_STRING;
							break;
						case 't':
							sbStr.append( (char)0x09 );
							state = S_STRING;
							break;
						case 'n':
							sbStr.append( (char)0x0A );
							state = S_STRING;
							break;
						case 'f':
							sbStr.append( (char)0x0C );
							state = S_STRING;
							break;
						case 'r':
							sbStr.append( (char)0x0D );
							state = S_STRING;
							break;
						case 'u':
							hexValue = 0;
							hexCount = 4;
							state = S_STRING_UNHEX;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_STRING_UNHEX:
						if ( c > 255 ) {
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						i = asciiHexTab[ c ];
						if ( i == -1 ) {
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						hexValue <<= 4;
						hexValue |= i;
						--hexCount;
						if (hexCount == 0) {
							sbStr.append( (char)hexValue );
							state = S_STRING;
						}
						++pos;
						break;
					case S_CONSTANT:
						switch ( c ) {
						case 'a':
						case 'l':
						case 's':
						case 'e':
						case 'r':
						case 'u':
							sbStr.append( c );
							++pos;
							break;
						default:
							constant = sbStr.toString();
							if ( "false".equals( constant ) ) {
								json_value_type = T_BOOLEAN;
								booleanVal = false;
							}
							else if ( "true".equals( constant ) ) {
								json_value_type = T_BOOLEAN;
								booleanVal = true;
							}
							else if ( "null".equals( constant ) ) {
								json_value_type = T_NULL;
							}
							else {
								throw new JSONException( "Invalid JSON constant: '" + constant + "' at (" + y + ":" + x + ")!" );
							}
							state = rstate;
							break;
						}
						break;
					case S_NUMBER_MINUS:
						switch ( c ) {
						case '0':
							sbStr.append( c );
							state = S_NUMBER_ZERO;
							break;
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							state = S_NUMBER_INTEGER;
							break;
						default:
							throw new JSONException( "Invalid JSON number structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_NUMBER_INTEGER:
						switch ( c ) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							++pos;
							break;
						case '.':
							sbStr.append( c );
							state = S_NUMBER_DECIMAL;
							++pos;
							break;
						case 'e':
							sbStr.append( c );
							state = S_NUMBER_E;
							++pos;
							break;
						case 'E':
							sbStr.append( c );
							state = S_NUMBER_E;
							++pos;
							break;
						default:
							json_value_type = T_NUMBER;
							stringVal = sbStr.toString();
							state = rstate;
							break;
						}
						break;
					case S_NUMBER_ZERO:
						switch ( c ) {
						case '.':
							sbStr.append( c );
							state = S_NUMBER_DECIMAL;
							++pos;
							break;
						case 'e':
							sbStr.append( c );
							state = S_NUMBER_E;
							++pos;
							break;
						case 'E':
							sbStr.append( c );
							state = S_NUMBER_E;
							++pos;
							break;
						default:
							json_value_type = T_NUMBER;
							stringVal = sbStr.toString();
							state = rstate;
							break;
						}
						break;
					case S_NUMBER_DECIMAL:
						switch ( c ) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							state = S_NUMBER_DECIMALS;
							++pos;
							break;
						default:
							throw new JSONException( "Invalid JSON number structure at (" + y + ":" + x + ")!" );
						}
						break;
					case S_NUMBER_DECIMALS:
						switch ( c ) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							++pos;
							break;
						case 'e':
							sbStr.append( c );
							state = S_NUMBER_E;
							++pos;
							break;
						case 'E':
							sbStr.append( c );
							state = S_NUMBER_E;
							++pos;
							break;
						default:
							json_value_type = T_NUMBER;
							stringVal = sbStr.toString();
							state = rstate;
						}
						break;
					case S_NUMBER_E:
						switch ( c ) {
						case '+':
							sbStr.append( c );
							state = S_NUMBER_EXPONENT;
							break;
						case '-':
							sbStr.append( c );
							state = S_NUMBER_EXPONENT;
							break;
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							state = S_NUMBER_EXPONENTS;
							break;
						default:
							throw new JSONException( "Invalid JSON number structure at (" + y + ":" + x + ")!" );
						}
						++pos;
						break;
					case S_NUMBER_EXPONENT:
						switch ( c ) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							state = S_NUMBER_EXPONENTS;
							++pos;
							break;
						default:
							throw new JSONException( "Invalid JSON number structure at (" + y + ":" + x + ")!" );
						}
						break;
					case S_NUMBER_EXPONENTS:
						switch ( c ) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							sbStr.append( c );
							++pos;
							break;
						default:
							json_value_type = T_NUMBER;
							stringVal = sbStr.toString();
							state = rstate;
						}
						break;
					case S_EOF:
						switch ( c ) {
						case 0x20:
						case 0x09:
						case 0x0D:
							// Whitespace.
							break;
						case 0x0A:
							++y;
							x = 1;
							break;
						default:
							throw new JSONException( "Invalid JSON structure at (" + y + ":" + x + ")!" );
						}
						++pos;
					}
				}
				// Switch buffer to write mode.
				charBuffer.clear();
				decoder.fill( charBuffer );
				// Switch buffer to read mode.
				charBuffer.flip();

				pos = charBuffer.position();
				limit = charBuffer.limit();

				bLoop = !(pos == limit);
			}
		}
		catch (InstantiationException e) {
			throw new JSONException( e );
		}
		catch (IllegalAccessException e) {
			throw new JSONException( e );
		}
		if (curObj == null || stack.size() > 0) {
			throw new JSONException( "Invalid JSON structure!" );
		}
		return rootObj;
	}

	/** Integer to hex char conversion table. */
	//private static char[] hexTab = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	/** Hex char to integer conversion table. */
	private static int[] asciiHexTab = new int[256];

	/*
	 * Initialize ASCII hex table.
	 */
	static {
		String hex = "0123456789abcdef";
		for (int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy