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

com.antiaction.common.json.representation.JSONTextUnmarshaller Maven / Gradle / Ivy

/*
 * 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.representation;

import java.io.IOException;
import java.io.InputStream;
import java.nio.CharBuffer;
import java.util.LinkedList;

import com.antiaction.common.json.JSONConstants;
import com.antiaction.common.json.JSONDecoder;
import com.antiaction.common.json.JSONException;

/**
 * De-serialize a JSON data stream into a JSON structure.
 *
 * @author Nicholas
 * Created on 06/08/2012
 */
public class JSONTextUnmarshaller {

	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;

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

	private static final class StackEntry {
		public JSONCollection json_structure;
		public JSONString json_name;
		public StackEntry(JSONCollection json_structure, JSONString json_name) {
			this.json_structure = json_structure;
			this.json_name = json_name;
		}
	}

	public JSONCollection toJSONStructure(InputStream in, JSONDecoder decoder) throws IOException, JSONException {
		LinkedList stack = new LinkedList();
		StackEntry stackEntry = null;
		JSONCollection current = 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;

		JSONString json_string = null;
		JSONString json_name = null;
		JSONValue json_value = null;

		int y = 1;
		int x = 1;

		int state = S_START;
		int rstate = -1;
		boolean bLoop = true;
		char c;
		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 '{':
						current = new JSONObject();
						stack.add( new StackEntry( current, json_name ) );
						state = S_OBJECT;
						break;
					case '[':
						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();
						current = stackEntry.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:
					json_name = json_string;
					state = S_OBJECT_COLON;
					// debug
					//System.out.println( json_value.toString() );
				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:
					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 + ")!" );
						}
						*/
						stackEntry = stack.removeLast();
						current = stackEntry.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_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();
						current = stackEntry.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 '{':
						current = new JSONObject();
						stack.add( new StackEntry( current, json_name ) );
						state = S_OBJECT;
						break;
					case '[':
						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:
					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();
						current = stackEntry.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 '{':
						current = new JSONObject();
						stack.add( new StackEntry( current, json_name ) );
						state = S_OBJECT;
						break;
					case '[':
						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_string = JSONString.String( sbStr.toString() );
						json_value = json_string;
						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 = JSONBoolean.False;
						}
						else if ( "null".equals( constant ) ) {
							json_value = JSONNull.Null;
						}
						else if ( "true".equals( constant ) ) {
							json_value = JSONBoolean.True;
						}
						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 = new JSONNumber( 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 = new JSONNumber( 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 = new JSONNumber( 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 = new JSONNumber( 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);
		}
		if (current == null || stack.size() > 0) {
			throw new JSONException( "Invalid JSON structure!" );
		}
		return current;
	}

	/** 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