| /******************************************************************************* |
| * Copyright (c) 2007 IBM Corporation and Others |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Hisashi MIYASHITA - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.actf.model.flash.as; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * Class for Deserializing a string in JSON notation to a Java object. Note that |
| * a property name should be quoted in JSON object notation. E.g. |
| * <code>{"id":123}</code>. In this deserializer, a JSON object is |
| * deserialized to an {@link ASObject} instance. |
| * |
| * @see ASObject |
| */ |
| public class ASDeserializer { |
| /** |
| * Token dictionary used to convert reserved words to Java constants. |
| */ |
| private static final Map<String, Object> tokenDic = new HashMap<String, Object>(); |
| /** |
| * String constant to denote JSON <code>undefined</code> value. |
| */ |
| private static final Object UNDEFINED = "undefined".intern(); //$NON-NLS-1$ |
| /** |
| * String constant to denote JSON <code>null</code> value. |
| */ |
| private static final Object NULL = "null".intern(); //$NON-NLS-1$ |
| |
| /** |
| * Initializes the token dictionary. |
| */ |
| static { |
| tokenDic.put("true", Boolean.valueOf(true)); //$NON-NLS-1$ |
| tokenDic.put("false", Boolean.valueOf(false)); //$NON-NLS-1$ |
| tokenDic.put("undefined", UNDEFINED); //$NON-NLS-1$ |
| tokenDic.put("null", NULL); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Internal variable to hold cursor position in parsing. <code>0</code> |
| * means the first character of the string. |
| */ |
| private int idx; |
| /** |
| * Internal variable to hold a string to be serialized. Set by the |
| * constructor. |
| */ |
| private String str; |
| |
| /** |
| * Skips a sequence of whitespace characters. |
| * |
| * @return Cursor position after skipping. It returns <code>-1</code> if |
| * it reaches the end of the string. |
| */ |
| private int skipSP() { |
| while (idx < str.length()) { |
| char ch = str.charAt(idx); |
| switch (ch) { |
| case ' ': |
| case '\t': |
| case '\n': |
| case '\r': |
| idx++; |
| break; |
| default: |
| return idx; |
| } |
| } |
| return -1; |
| |
| } |
| |
| /** |
| * Seatch the end of a token starts from the current cursor position. |
| * |
| * @return Cursor position after search. It points the next character to the |
| * end of given token. |
| */ |
| private int getTokenEndIdx() { |
| int idx2 = idx; |
| while (idx2 < str.length()) { |
| char ch = str.charAt(idx2); |
| switch (ch) { |
| case ' ': |
| case '\t': |
| case '\n': |
| case '\r': |
| case ',': |
| case ':': |
| case '}': |
| case ']': |
| return idx2; |
| |
| default: |
| idx2++; |
| } |
| } |
| return idx2; |
| } |
| |
| /** |
| * Deserializes a JSON string to Java string. Note that JSON string should |
| * be quoted as <code>"Hello, Java"</code>. This method is internally |
| * called from {@link #deserialize()}. |
| * |
| * @return Deserialized Java string. |
| * @throws IllegalArgumentException |
| * when given JSON string is ill-formed. |
| */ |
| private String deserializeString() { |
| StringBuffer ret = new StringBuffer(); |
| idx++; |
| while (idx < str.length()) { |
| char ch = str.charAt(idx); |
| switch (ch) { |
| case '"': |
| idx++; |
| return ret.toString(); |
| case '\\': |
| idx++; |
| if (idx == str.length()) { |
| throw new IllegalArgumentException( |
| "Abnormal end of the string:" + str); //$NON-NLS-1$ |
| } |
| ret.append(str.charAt(idx)); |
| idx++; |
| break; |
| default: |
| ret.append(ch); |
| idx++; |
| } |
| } |
| throw new IllegalArgumentException("Invalid String:" + str); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Deserializes a JSON array string to Java array. This method is internally |
| * called from {@link #deserialize()}. |
| * |
| * @return Deserialized Java array. |
| * @throws IllegalArgumentException |
| * when given JSON string is ill-formed. |
| */ |
| private Object[] deserializeArray() { |
| idx++; |
| ArrayList<Object> ret = new ArrayList<Object>(); |
| while (idx < str.length()) { |
| skipSP(); |
| char ch = str.charAt(idx); |
| if (ch == ']') { |
| idx++; |
| return ret.toArray(new Object[ret.size()]); |
| } else { |
| ret.add(deserialize()); |
| } |
| skipSP(); |
| ch = str.charAt(idx); |
| if (ch == ']') { |
| idx++; |
| return ret.toArray(new Object[ret.size()]); |
| } |
| if (ch != ',') { |
| throw new IllegalArgumentException("Missing ',':" + str); //$NON-NLS-1$ |
| } |
| idx++; |
| } |
| throw new IllegalArgumentException("Abnormal end of the array:" + str); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Deserializes a JSON object string to {@link ASObject} instance. This |
| * method is internally called from {@link #deserialize()}. |
| * |
| * @return Deserialized {@link ASObject} instance. |
| * @throws IllegalArgumentException |
| * when given JSON string is ill-formed. |
| */ |
| private ASObject deserializeASObject() { |
| idx++; |
| ASObject ret = new ASObject(); |
| while (idx < str.length()) { |
| skipSP(); |
| char ch = str.charAt(idx); |
| if (ch == '}') { |
| idx++; |
| return ret; |
| } else { |
| String prop = deserializeString(); |
| skipSP(); |
| if (str.charAt(idx) != ':') { |
| throw new IllegalArgumentException("Missing ':':" + str); //$NON-NLS-1$ |
| } |
| idx++; |
| Object o = deserialize(); |
| ret.put(prop, o); |
| } |
| skipSP(); |
| ch = str.charAt(idx); |
| if (ch == '}') { |
| idx++; |
| return ret; |
| } |
| if (str.charAt(idx) != ',') { |
| throw new IllegalArgumentException("Missing ',':" + str); //$NON-NLS-1$ |
| } |
| idx++; |
| } |
| throw new IllegalArgumentException("Abnormal end of the array:" + str); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Deserializes a given JSON string. Especially a JSON object string is |
| * deserialized to an {@link ASObject} instance. |
| * |
| * Note that a property name should be quoted in JSON object notation. E.g. |
| * <code>{"id":123}</code>. |
| * |
| * @return Deserialized object. |
| * @throws IllegalArgumentException |
| * when given JSON string is not in valid format. |
| */ |
| public Object deserialize() { |
| int r = skipSP(); |
| if (r < 0) |
| return null; |
| char ch = str.charAt(idx); |
| switch (ch) { |
| case '[': |
| return deserializeArray(); |
| case '{': |
| return deserializeASObject(); |
| case '"': |
| return deserializeString(); |
| default: |
| int idx2 = getTokenEndIdx(); |
| String tok = str.substring(idx, idx2); |
| Object o = tokenDic.get(tok); |
| this.idx = idx2; |
| if (o == NULL) { |
| return null; |
| } |
| if (o != null) { |
| return o; |
| } |
| try { |
| int i = Integer.parseInt(tok); |
| return Integer.valueOf(i); |
| } catch (NumberFormatException e) { |
| } |
| try { |
| double d = Double.parseDouble(tok); |
| return new Double(d); |
| } catch (NumberFormatException e) { |
| } |
| throw new IllegalArgumentException(tok + " is not a valid token."); //$NON-NLS-1$ |
| } |
| |
| } |
| |
| /** |
| * Creates an {@link ASDeserializer} instance. |
| * |
| * @param str |
| * JSON string to be deserialized |
| */ |
| public ASDeserializer(String str) { |
| this.idx = 0; |
| this.str = str; |
| } |
| } |