001: /*
002: * Copyright 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.json.client;
017:
018: import com.google.gwt.core.client.JavaScriptException;
019: import com.google.gwt.core.client.JavaScriptObject;
020:
021: /**
022: * Parses the string representation of a JSON object into a set of
023: * JSONValue-derived objects.
024: *
025: * @see com.google.gwt.json.client.JSONValue
026: */
027: public class JSONParser {
028:
029: /**
030: * Given a jsonString, returns the JSONObject representation. For efficiency,
031: * parsing occurs lazily as the structure is requested.
032: *
033: * @param jsonString
034: * @return a JSONObject that has been built by parsing the JSON string
035: * @throws NullPointerException if <code>jsonString</code> is
036: * <code>null</code>
037: * @throws IllegalArgumentException if <code>jsonString</code> is empty
038: */
039: public static JSONValue parse(String jsonString) {
040: // Create a JavaScriptObject from the JSON string.
041: //
042: if (jsonString == null) {
043: throw new NullPointerException();
044: }
045: if (jsonString == "") {
046: throw new IllegalArgumentException("empty argument");
047: }
048: try {
049: JavaScriptObject jsonObject = evaluate(jsonString);
050: return buildValue(jsonObject);
051: } catch (JavaScriptException ex) {
052: throw new JSONException(ex);
053: }
054: }
055:
056: /**
057: * Returns the {@link JSONValue} for a given {@link JavaScriptObject}.
058: *
059: * @param jsValue {@link JavaScriptObject} to build a {@link JSONValue} for,
060: * this object cannot be a primitive JavaScript type
061: * @return a {@link JSONValue} instance for the {@link JavaScriptObject}
062: */
063: static JSONValue buildValue(JavaScriptObject jsValue)
064: throws JSONException {
065:
066: if (isNull(jsValue)) {
067: return JSONNull.getInstance();
068: }
069:
070: if (isArray(jsValue)) {
071: return new JSONArray(jsValue);
072: }
073:
074: if (isBoolean(jsValue)) {
075: return JSONBoolean.getInstance(asBoolean(jsValue));
076: }
077:
078: if (isString(jsValue)) {
079: return new JSONString(asString(jsValue));
080: }
081:
082: if (isDouble(jsValue)) {
083: return new JSONNumber(asDouble(jsValue));
084: }
085:
086: if (isObject(jsValue)) {
087: return new JSONObject(jsValue);
088: }
089:
090: /*
091: * In practice we should never reach this point. If we do, we cannot make
092: * any assumptions about the jsValue.
093: */
094: throw new JSONException("Unknown JavaScriptObject type");
095: }
096:
097: /**
098: * Returns the boolean represented by the jsValue. This method
099: * assumes that {@link #isBoolean(JavaScriptObject)} returned
100: * <code>true</code>.
101: *
102: * @param jsValue JavaScript object to convert
103: * @return the boolean represented by the jsValue
104: */
105: private static native boolean asBoolean(JavaScriptObject jsValue) /*-{
106: return jsValue.valueOf();
107: }-*/;
108:
109: /**
110: * Returns the double represented by jsValue. This method assumes that
111: * {@link #isDouble(JavaScriptObject)} returned <code>true</code>.
112: *
113: * @param jsValue JavaScript object to convert
114: * @return the double represented by the jsValue
115: */
116: private static native double asDouble(JavaScriptObject jsValue) /*-{
117: return jsValue.valueOf();
118: }-*/;
119:
120: /**
121: * Returns the Javascript String as a Java String. This method assumes that
122: * {@link #isString(JavaScriptObject)} returned <code>true</code>.
123: *
124: * @param jsValue JavaScript object to convert
125: * @return the String represented by the jsValue
126: */
127: private static native String asString(JavaScriptObject jsValue) /*-{
128: return jsValue;
129: }-*/;
130:
131: /*
132: * This method converts the json string into a JavaScriptObject inside of JSNI
133: * method by simply evaluating the string in JavaScript.
134: */
135: private static native JavaScriptObject evaluate(String jsonString) /*-{
136: var x = eval('(' + jsonString + ')');
137: if (typeof x == 'number' || typeof x == 'string' || typeof x == 'array' || typeof x == 'boolean') {
138: x = (Object(x));
139: }
140: return x;
141: }-*/;
142:
143: /**
144: * Returns <code>true</code> if the {@link JavaScriptObject} is a wrapped
145: * JavaScript Array.
146: *
147: * @param jsValue JavaScript object to test
148: * @return <code>true</code> if jsValue is a wrapped JavaScript Array
149: */
150: private static native boolean isArray(JavaScriptObject jsValue) /*-{
151: return jsValue instanceof Array;
152: }-*/;
153:
154: /**
155: * Returns <code>true</code> if the {@link JavaScriptObject} is a wrapped
156: * JavaScript Boolean.
157: *
158: * @param jsValue JavaScript object to test
159: * @return <code>true</code> if jsValue is a wrapped JavaScript Boolean
160: */
161: private static native boolean isBoolean(JavaScriptObject jsValue) /*-{
162: return jsValue instanceof Boolean;
163: }-*/;
164:
165: /**
166: * Returns <code>true</code> if the {@link JavaScriptObject} is a wrapped
167: * JavaScript Double.
168: *
169: * @param jsValue JavaScript object to test
170: * @return <code>true</code> if jsValue is a wrapped JavaScript Double
171: */
172: private static native boolean isDouble(JavaScriptObject jsValue) /*-{
173: return jsValue instanceof Number;
174: }-*/;
175:
176: /**
177: * Returns <code>true</code> if the {@link JavaScriptObject} is <code>null</code>
178: * or <code>undefined</code>.
179: *
180: * @param jsValue JavaScript object to test
181: * @return <code>true</code> if jsValue is <code>null</code> or
182: * <code>undefined</code>
183: */
184: private static native boolean isNull(JavaScriptObject jsValue) /*-{
185: return jsValue == null;
186: }-*/;
187:
188: /**
189: * Returns <code>true</code> if the {@link JavaScriptObject} is a JavaScript
190: * Object.
191: *
192: * @param jsValue JavaScript object to test
193: * @return <code>true</code> if jsValue is a JavaScript Object
194: */
195: private static native boolean isObject(JavaScriptObject jsValue) /*-{
196: return jsValue instanceof Object;
197: }-*/;
198:
199: /**
200: * Returns <code>true</code> if the {@link JavaScriptObject} is a JavaScript
201: * String.
202: *
203: * @param jsValue JavaScript object to test
204: * @return <code>true</code> if jsValue is a JavaScript String
205: */
206: private static native boolean isString(JavaScriptObject jsValue) /*-{
207: return jsValue instanceof String;
208: }-*/;
209:
210: /**
211: * Not instantiable.
212: */
213: private JSONParser() {
214: }
215: }
|