| /* | |
| * Copyright 2008 Google Inc. | |
| * | |
| * 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.google.gwt.json.client; | |
| import com.google.gwt.core.client.JavaScriptException; | |
| import com.google.gwt.core.client.JavaScriptObject; | |
| /** | |
| * Parses the string representation of a JSON object into a set of | |
| * JSONValue-derived objects. | |
| * | |
| * @see com.google.gwt.json.client.JSONValue | |
| */ | |
| public class JSONParser { | |
| static final JavaScriptObject typeMap = initTypeMap(); | |
| /** | |
| * Evaluates a trusted JSON string and returns its JSONValue representation. | |
| * CAUTION! This method calls the JavaScript <code>eval()</code> function, | |
| * which can execute arbitrary script. DO NOT pass an untrusted string into | |
| * this method. | |
| * | |
| * <p> | |
| * This method has been deprecated. Please call either | |
| * {@link #parseStrict(String)} (for inputs that strictly follow the JSON | |
| * specification) or {@link #parseLenient(String)}. The implementation of this | |
| * method calls parseLenient. | |
| * | |
| * @param jsonString a JSON object to parse | |
| * @return a JSONValue that has been built by parsing the JSON string | |
| * @throws NullPointerException if <code>jsonString</code> is | |
| * <code>null</code> | |
| * @throws IllegalArgumentException if <code>jsonString</code> is empty | |
| * | |
| * @deprecated use {@link #parseStrict(String)} or | |
| * {@link #parseLenient(String)} | |
| */ | |
| @Deprecated | |
| public static JSONValue parse(String jsonString) { | |
| return parseLenient(jsonString); | |
| } | |
| /** | |
| * Evaluates a trusted JSON string and returns its JSONValue representation. | |
| * CAUTION! This method calls the JavaScript {@code eval()} function, which | |
| * can execute arbitrary script. DO NOT pass an untrusted string into this | |
| * method. | |
| * | |
| * @param jsonString a JSON object to parse | |
| * @return a JSONValue that has been built by parsing the JSON string | |
| * @throws NullPointerException if <code>jsonString</code> is | |
| * <code>null</code> | |
| * @throws IllegalArgumentException if <code>jsonString</code> is empty | |
| */ | |
| public static JSONValue parseLenient(String jsonString) { | |
| return parse(jsonString, false); | |
| } | |
| /** | |
| * Evaluates a JSON string and returns its JSONValue representation. Where | |
| * possible, the browser's {@code JSON.parse function} is used. For older | |
| * browsers including IE6 and IE7 that lack a {@code JSON.parse} function, the | |
| * input is validated as described in RFC 4627 for safety and passed to | |
| * {@code eval()}. | |
| * | |
| * @param jsonString a JSON object to parse | |
| * @return a JSONValue that has been built by parsing the JSON string | |
| * @throws NullPointerException if <code>jsonString</code> is | |
| * <code>null</code> | |
| * @throws IllegalArgumentException if <code>jsonString</code> is empty | |
| */ | |
| public static JSONValue parseStrict(String jsonString) { | |
| return parse(jsonString, true); | |
| } | |
| static void throwJSONException(String message) { | |
| throw new JSONException(message); | |
| } | |
| static void throwUnknownTypeException(String typeString) { | |
| throw new JSONException("Unexpected typeof result '" + typeString | |
| + "'; please report this bug to the GWT team"); | |
| } | |
| /** | |
| * Called from {@link #initTypeMap()}. | |
| */ | |
| private static JSONValue createBoolean(boolean v) { | |
| return JSONBoolean.getInstance(v); | |
| } | |
| /** | |
| * Called from {@link #initTypeMap()}. | |
| */ | |
| private static JSONValue createNumber(double v) { | |
| return new JSONNumber(v); | |
| } | |
| /** | |
| * Called from {@link #initTypeMap()}. If we get here, <code>o</code> is | |
| * either <code>null</code> (not <code>undefined</code>) or a JavaScript | |
| * object. | |
| */ | |
| private static native JSONValue createObject(Object o) /*-{ | |
| if (!o) { | |
| return @com.google.gwt.json.client.JSONNull::getInstance()(); | |
| } | |
| var v = o.valueOf ? o.valueOf() : o; | |
| if (v !== o) { | |
| // It was a primitive wrapper, unwrap it and try again. | |
| var func = @com.google.gwt.json.client.JSONParser::typeMap[typeof v]; | |
| return func ? func(v) : @com.google.gwt.json.client.JSONParser::throwUnknownTypeException(Ljava/lang/String;)(typeof v); | |
| } else if (o instanceof Array || o instanceof $wnd.Array) { | |
| // Looks like an Array; wrap as JSONArray. | |
| // NOTE: this test can fail for objects coming from a different window, | |
| // but we know of no reliable tests to determine if something is an Array | |
| // in all cases. | |
| return @com.google.gwt.json.client.JSONArray::new(Lcom/google/gwt/core/client/JavaScriptObject;)(o); | |
| } else { | |
| // This is a basic JavaScript object; wrap as JSONObject. | |
| // Subobjects will be created on demand. | |
| return @com.google.gwt.json.client.JSONObject::new(Lcom/google/gwt/core/client/JavaScriptObject;)(o); | |
| } | |
| }-*/; | |
| /** | |
| * Called from {@link #initTypeMap()}. | |
| */ | |
| private static JSONValue createString(String v) { | |
| return new JSONString(v); | |
| } | |
| /** | |
| * Called from {@link #initTypeMap()}. This method returns a <code>null</code> | |
| * pointer, representing JavaScript <code>undefined</code>. | |
| */ | |
| private static JSONValue createUndefined() { | |
| return null; | |
| } | |
| /** | |
| * This method converts <code>jsonString</code> into a JSONValue. | |
| * In strict mode (strict == true), one of two code paths is taken: | |
| * 1) Call JSON.parse if available, or | |
| * 2) Validate the input and call eval() | |
| * | |
| * In lenient mode (strict == false), eval() is called without validation. | |
| * | |
| * @param strict if true, parse in strict mode. | |
| */ | |
| private static native JSONValue evaluate(String json, boolean strict) /*-{ | |
| // Note: we cannot simply call JsonUtils.unsafeEval because it is unable | |
| // to return a result for inputs whose outermost type is 'string' in | |
| // dev mode. | |
| var v; | |
| if (strict && @com.google.gwt.core.client.JsonUtils::hasJsonParse) { | |
| try { | |
| v = JSON.parse(json); | |
| } catch (e) { | |
| return @com.google.gwt.json.client.JSONParser::throwJSONException(Ljava/lang/String;)("Error parsing JSON: " + e); | |
| } | |
| } else { | |
| if (strict) { | |
| // Validate the input according to RFC 4627. | |
| if (!@com.google.gwt.core.client.JsonUtils::safeToEval(Ljava/lang/String;)(json)) { | |
| return @com.google.gwt.json.client.JSONParser::throwJSONException(Ljava/lang/String;)("Illegal character in JSON string"); | |
| } | |
| } | |
| json = @com.google.gwt.core.client.JsonUtils::escapeJsonForEval(Ljava/lang/String;)(json); | |
| try { | |
| v = eval('(' + json + ')'); | |
| } catch (e) { | |
| return @com.google.gwt.json.client.JSONParser::throwJSONException(Ljava/lang/String;)("Error parsing JSON: " + e); | |
| } | |
| } | |
| var func = @com.google.gwt.json.client.JSONParser::typeMap[typeof v]; | |
| return func ? func(v) : @com.google.gwt.json.client.JSONParser::throwUnknownTypeException(Ljava/lang/String;)(typeof v); | |
| }-*/; | |
| private static native JavaScriptObject initTypeMap() /*-{ | |
| return { | |
| "boolean": @com.google.gwt.json.client.JSONParser::createBoolean(Z), | |
| "number": @com.google.gwt.json.client.JSONParser::createNumber(D), | |
| "string": @com.google.gwt.json.client.JSONParser::createString(Ljava/lang/String;), | |
| "object": @com.google.gwt.json.client.JSONParser::createObject(Ljava/lang/Object;), | |
| "function": @com.google.gwt.json.client.JSONParser::createObject(Ljava/lang/Object;), | |
| "undefined": @com.google.gwt.json.client.JSONParser::createUndefined(), | |
| } | |
| }-*/; | |
| private static JSONValue parse(String jsonString, boolean strict) { | |
| if (jsonString == null) { | |
| throw new NullPointerException(); | |
| } | |
| if (jsonString.length() == 0) { | |
| throw new IllegalArgumentException("empty argument"); | |
| } | |
| try { | |
| return evaluate(jsonString, strict); | |
| } catch (JavaScriptException ex) { | |
| throw new JSONException(ex); | |
| } | |
| } | |
| /** | |
| * Not instantiable. | |
| */ | |
| private JSONParser() { | |
| } | |
| } |