| /* |
| * Copyright 2009 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.core.client; |
| |
| /** |
| * Provides JSON-related utility methods. |
| */ |
| public class JsonUtils { |
| |
| /** |
| * Converts a value to JSON. |
| */ |
| public static native String stringify(JavaScriptObject obj) /*-{ |
| return JSON.stringify(obj); |
| }-*/; |
| |
| /** |
| * Converts a value to JSON. |
| * |
| * @param space controls the spacing in the final string. Successive levels in the stringification |
| * will each be indented by this string (or the first ten characters of it). |
| */ |
| public static native String stringify(JavaScriptObject obj, String space) /*-{ |
| return JSON.stringify(obj, null, space); |
| }-*/; |
| |
| /** |
| * Escapes characters within a JSON string than cannot be passed directly to |
| * eval(). Control characters, quotes and backslashes are not affected. |
| */ |
| public static native String escapeJsonForEval(String toEscape) /*-{ |
| var escapeTable = @JsonUtils::getEscapeTable()(); |
| var s = toEscape.replace(/[\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200b-\u200f\u2028-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb]/g, function(x) { |
| return @JsonUtils::escapeChar(*)(x, escapeTable); |
| }); |
| return s; |
| }-*/; |
| |
| /** |
| * Returns a quoted, escaped JSON String. |
| */ |
| public static native String escapeValue(String toEscape) /*-{ |
| var escapeTable = @JsonUtils::getEscapeTable()(); |
| var s = toEscape.replace(/[\x00-\x1f\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200b-\u200f\u2028-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb"\\]/g, function(x) { |
| return @JsonUtils::escapeChar(*)(x, escapeTable); |
| }); |
| return "\"" + s + "\""; |
| }-*/; |
| |
| /** |
| * Evaluates a JSON expression safely. The payload must evaluate to an Object |
| * or an Array (not a primitive or a String). |
| * |
| * @param <T> The type of JavaScriptObject that should be returned |
| * @param json The source JSON text |
| * @return The evaluated object |
| * |
| * @throws IllegalArgumentException if the input is not valid JSON |
| */ |
| public static native <T extends JavaScriptObject> T safeEval(String json) /*-{ |
| try { |
| return JSON.parse(json); |
| } catch (e) { |
| return @JsonUtils::throwIllegalArgumentException(*)("Error parsing JSON: " + e, json); |
| } |
| }-*/; |
| |
| /** |
| * Returns true if the given JSON string may be safely evaluated by {@code |
| * eval()} without undesired side effects or security risks. Note that a true |
| * result from this method does not guarantee that the input string is valid |
| * JSON. This method does not consider the contents of quoted strings; it |
| * may still be necessary to perform escaping prior to evaluation for correct |
| * results. |
| * <p> The technique used is taken from <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. |
| * |
| * <p> Note that this function may fail in sufficiently large text in some |
| * browsers (e.g. Chrome). It is always better to use {@link #safeEval} |
| * instead which is safer, faster and also works with large texts but less |
| * lenient than this one for invalid JSON. |
| */ |
| public static native boolean safeToEval(String text) /*-{ |
| // Remove quoted strings and disallow anything except: |
| // |
| // 1) symbols and brackets ,:{}[] |
| // 2) numbers: digits 0-9, ., -, +, e, and E |
| // 3) literal values: 'null', 'true' and 'false' = [aeflnr-u] |
| // 4) whitespace: ' ', '\n', '\r', and '\t' |
| return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(text.replace(/"(\\.|[^"\\])*"/g, ''))); |
| }-*/; |
| |
| /** |
| * Evaluates a JSON expression using {@code eval()}. This method does not |
| * validate the JSON text and should only be used on JSON from trusted |
| * sources. The payload must evaluate to an Object or an Array (not a |
| * primitive or a String). |
| * |
| * @param <T> The type of JavaScriptObject that should be returned |
| * @param json The source JSON text |
| * @return The evaluated object |
| */ |
| public static native <T extends JavaScriptObject> T unsafeEval(String json) /*-{ |
| var escaped = @JsonUtils::escapeJsonForEval(Ljava/lang/String;)(json); |
| try { |
| return eval('(' + escaped + ')'); |
| } catch (e) { |
| return @JsonUtils::throwIllegalArgumentException(*)("Error parsing JSON: " + e, json); |
| } |
| }-*/; |
| |
| static void throwIllegalArgumentException(String message, String data) { |
| throw new IllegalArgumentException(message + "\n" + data); |
| } |
| |
| private static native String escapeChar(String c, JavaScriptObject escapeTable) /*-{ |
| var lookedUp = @JsonUtils::escapeTable[c.charCodeAt(0)]; |
| return (lookedUp == null) ? c : lookedUp; |
| }-*/; |
| |
| private static JavaScriptObject escapeTable; // Lazily initialized. |
| |
| private static JavaScriptObject getEscapeTable() { |
| if (escapeTable == null) { |
| escapeTable = initEscapeTable(); |
| } |
| return escapeTable; |
| } |
| |
| private static native JavaScriptObject initEscapeTable() /*-{ |
| var out = [ |
| "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", |
| "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\u000B", |
| "\\f", "\\r", "\\u000E", "\\u000F", "\\u0010", "\\u0011", |
| "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", |
| "\\u0018", "\\u0019", "\\u001A", "\\u001B", "\\u001C", "\\u001D", |
| "\\u001E", "\\u001F"]; |
| out[34] = '\\"'; |
| out[92] = '\\\\'; |
| out[0xad] = '\\u00ad'; // Soft hyphen |
| out[0x600] = '\\u0600'; // Arabic number sign |
| out[0x601] = '\\u0601'; // Arabic sign sanah |
| out[0x602] = '\\u0602'; // Arabic footnote marker |
| out[0x603] = '\\u0603'; // Arabic sign safha |
| out[0x6dd] = '\\u06dd'; // Arabic and of ayah |
| out[0x70f] = '\\u070f'; // Syriac abbreviation mark |
| out[0x17b4] = '\\u17b4'; // Khmer vowel inherent aq |
| out[0x17b5] = '\\u17b5'; // Khmer vowel inherent aa |
| out[0x200b] = '\\u200b'; // Zero width space |
| out[0x200c] = '\\u200c'; // Zero width non-joiner |
| out[0x200d] = '\\u200d'; // Zero width joiner |
| out[0x200e] = '\\u200e'; // Left-to-right mark |
| out[0x200f] = '\\u200f'; // Right-to-left mark |
| out[0x2028] = '\\u2028'; // Line separator |
| out[0x2029] = '\\u2029'; // Paragraph separator |
| out[0x202a] = '\\u202a'; // Left-to-right embedding |
| out[0x202b] = '\\u202b'; // Right-to-left embedding |
| out[0x202c] = '\\u202c'; // Pop directional formatting |
| out[0x202d] = '\\u202d'; // Left-to-right override |
| out[0x202e] = '\\u202e'; // Right-to-left override |
| out[0x2060] = '\\u2060'; // Word joiner |
| out[0x2061] = '\\u2061'; // Function application |
| out[0x2062] = '\\u2062'; // Invisible times |
| out[0x2063] = '\\u2063'; // Invisible separator |
| out[0x2064] = '\\u2064'; // Invisible plus |
| out[0x206a] = '\\u206a'; // Inhibit symmetric swapping |
| out[0x206b] = '\\u206b'; // Activate symmetric swapping |
| out[0x206c] = '\\u206c'; // Inherent Arabic form shaping |
| out[0x206d] = '\\u206d'; // Activate Arabic form shaping |
| out[0x206e] = '\\u206e'; // National digit shapes |
| out[0x206f] = '\\u206f'; // Nominal digit shapes |
| out[0xfeff] = '\\ufeff'; // Zero width no-break space |
| out[0xfff9] = '\\ufff9'; // Intralinear annotation anchor |
| out[0xfffa] = '\\ufffa'; // Intralinear annotation separator |
| out[0xfffb] = '\\ufffb'; // Intralinear annotation terminator |
| return out; |
| }-*/; |
| |
| private JsonUtils() { |
| } |
| } |