From 9cf579ef01e66dad2ffe907350d2d088497138a8 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Tue, 12 Jul 2011 23:50:00 +0000 Subject: [PATCH] Use Streams instead of Escaper. Fixes issue 345. --- .../main/java/com/google/gson/Escaper.java | 160 --------------- .../main/java/com/google/gson/JsonArray.java | 16 -- .../java/com/google/gson/JsonElement.java | 18 +- .../main/java/com/google/gson/JsonNull.java | 7 - .../main/java/com/google/gson/JsonObject.java | 20 -- .../java/com/google/gson/JsonPrimitive.java | 13 -- .../java/com/google/gson/EscaperTest.java | 189 ------------------ 7 files changed, 8 insertions(+), 415 deletions(-) delete mode 100644 gson/src/main/java/com/google/gson/Escaper.java delete mode 100644 gson/src/test/java/com/google/gson/EscaperTest.java diff --git a/gson/src/main/java/com/google/gson/Escaper.java b/gson/src/main/java/com/google/gson/Escaper.java deleted file mode 100644 index 24636534..00000000 --- a/gson/src/main/java/com/google/gson/Escaper.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 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.gson; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * A utility class that is used to perform JSON escaping so that ", <, >, etc. characters are - * properly encoded in the JSON string representation before returning to the client code. - * - *

This class contains a single method to escape a passed in string value: - *

- *   String jsonStringValue = "beforeQuote\"afterQuote";
- *   String escapedValue = Escaper.escapeJsonString(jsonStringValue);
- * 

- * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class Escaper { - - private static final char[] HEX_CHARS = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - private static final Set JS_ESCAPE_CHARS; - private static final Set HTML_ESCAPE_CHARS; - - static { - Set mandatoryEscapeSet = new HashSet(); - mandatoryEscapeSet.add('"'); - mandatoryEscapeSet.add('\\'); - JS_ESCAPE_CHARS = Collections.unmodifiableSet(mandatoryEscapeSet); - - Set htmlEscapeSet = new HashSet(); - htmlEscapeSet.add('<'); - htmlEscapeSet.add('>'); - htmlEscapeSet.add('&'); - htmlEscapeSet.add('='); - htmlEscapeSet.add('\''); -// htmlEscapeSet.add('/'); -- Removing slash for now since it causes some incompatibilities - HTML_ESCAPE_CHARS = Collections.unmodifiableSet(htmlEscapeSet); - } - - private final boolean escapeHtmlCharacters; - - Escaper(boolean escapeHtmlCharacters) { - this.escapeHtmlCharacters = escapeHtmlCharacters; - } - - public String escapeJsonString(CharSequence plainText) { - StringBuilder escapedString = new StringBuilder(plainText.length() + 20); - try { - escapeJsonString(plainText, escapedString); - } catch (IOException e) { - throw new RuntimeException(e); - } - return escapedString.toString(); - } - - private void escapeJsonString(CharSequence plainText, StringBuilder out) throws IOException { - int pos = 0; // Index just past the last char in plainText written to out. - int len = plainText.length(); - - for (int charCount, i = 0; i < len; i += charCount) { - int codePoint = Character.codePointAt(plainText, i); - charCount = Character.charCount(codePoint); - - if (!isControlCharacter(codePoint) && !mustEscapeCharInJsString(codePoint)) { - continue; - } - - out.append(plainText, pos, i); - pos = i + charCount; - switch (codePoint) { - case '\b': - out.append("\\b"); - break; - case '\t': - out.append("\\t"); - break; - case '\n': - out.append("\\n"); - break; - case '\f': - out.append("\\f"); - break; - case '\r': - out.append("\\r"); - break; - case '\\': - out.append("\\\\"); - break; - case '/': - out.append("\\/"); - break; - case '"': - out.append("\\\""); - break; - default: - appendHexJavaScriptRepresentation(codePoint, out); - break; - } - } - out.append(plainText, pos, len); - } - - private boolean mustEscapeCharInJsString(int codepoint) { - if (!Character.isSupplementaryCodePoint(codepoint)) { - char c = (char) codepoint; - return JS_ESCAPE_CHARS.contains(c) - || (escapeHtmlCharacters && HTML_ESCAPE_CHARS.contains(c)); - } - return false; - } - - private static boolean isControlCharacter(int codePoint) { - // JSON spec defines these code points as control characters, so they must be escaped - return codePoint < 0x20 - || codePoint == 0x2028 // Line separator - || codePoint == 0x2029 // Paragraph separator - || (codePoint >= 0x7f && codePoint <= 0x9f); - } - - private static void appendHexJavaScriptRepresentation(int codePoint, Appendable out) - throws IOException { - if (Character.isSupplementaryCodePoint(codePoint)) { - // Handle supplementary unicode values which are not representable in - // javascript. We deal with these by escaping them as two 4B sequences - // so that they will round-trip properly when sent from java to javascript - // and back. - char[] surrogates = Character.toChars(codePoint); - appendHexJavaScriptRepresentation(surrogates[0], out); - appendHexJavaScriptRepresentation(surrogates[1], out); - return; - } - out.append("\\u") - .append(HEX_CHARS[(codePoint >>> 12) & 0xf]) - .append(HEX_CHARS[(codePoint >>> 8) & 0xf]) - .append(HEX_CHARS[(codePoint >>> 4) & 0xf]) - .append(HEX_CHARS[codePoint & 0xf]); - } -} diff --git a/gson/src/main/java/com/google/gson/JsonArray.java b/gson/src/main/java/com/google/gson/JsonArray.java index a79f5df4..2e1f5501 100644 --- a/gson/src/main/java/com/google/gson/JsonArray.java +++ b/gson/src/main/java/com/google/gson/JsonArray.java @@ -16,7 +16,6 @@ package com.google.gson; -import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -316,19 +315,4 @@ public final class JsonArray extends JsonElement implements Iterable entry : members.entrySet()) { - if (first) { - first = false; - } else { - sb.append(','); - } - sb.append('\"'); - sb.append(escaper.escapeJsonString(entry.getKey())); - sb.append("\":"); - entry.getValue().toString(sb, escaper); - } - sb.append('}'); - } } diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index 4d2cedae..4a08b265 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -17,8 +17,6 @@ package com.google.gson; import com.google.gson.internal.$Gson$Preconditions; - -import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -294,17 +292,6 @@ public final class JsonPrimitive extends JsonElement { return value; } - @Override - protected void toString(Appendable sb, Escaper escaper) throws IOException { - if (isString()) { - sb.append('"'); - sb.append(escaper.escapeJsonString(value.toString())); - sb.append('"'); - } else { - sb.append(value.toString()); - } - } - private static boolean isPrimitiveOrString(Object target) { if (target instanceof String) { return true; diff --git a/gson/src/test/java/com/google/gson/EscaperTest.java b/gson/src/test/java/com/google/gson/EscaperTest.java deleted file mode 100644 index ff4a589e..00000000 --- a/gson/src/test/java/com/google/gson/EscaperTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 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.gson; - -import junit.framework.TestCase; - -/** - * Performs some unit testing for the {@link Escaper} class. - * - * @author Joel Leitch - */ -public class EscaperTest extends TestCase { - - private Escaper escapeHtmlChar; - private Escaper noEscapeHtmlChar; - - @Override - protected void setUp() throws Exception { - super.setUp(); - escapeHtmlChar = new Escaper(true); - noEscapeHtmlChar = new Escaper(false); - } - - public void testNoSpecialCharacters() { - String value = "Testing123"; - String escapedString = escapeHtmlChar.escapeJsonString(value); - assertEquals(value, escapedString); - } - - public void testNewlineEscaping() throws Exception { - String containsNewline = "123\n456"; - String escapedString = escapeHtmlChar.escapeJsonString(containsNewline); - assertEquals("123\\n456", escapedString); - } - - public void testCarrageReturnEscaping() throws Exception { - String containsCarrageReturn = "123\r456"; - String escapedString = escapeHtmlChar.escapeJsonString(containsCarrageReturn); - assertEquals("123\\r456", escapedString); - } - - public void testTabEscaping() throws Exception { - String containsTab = "123\t456"; - String escapedString = escapeHtmlChar.escapeJsonString(containsTab); - assertEquals("123\\t456", escapedString); - } - - public void testDoubleQuoteEscaping() throws Exception { - String containsQuote = "123\"456"; - String escapedString = escapeHtmlChar.escapeJsonString(containsQuote); - assertEquals("123\\\"456", escapedString); - } - - public void testSingleQuoteEscaping() throws Exception { - String containsQuote = "123'456"; - String escapedString = escapeHtmlChar.escapeJsonString(containsQuote); - assertEquals("123\\u0027456", escapedString); - } - - public void testLineSeparatorEscaping() throws Exception { - String src = "123\u2028 456"; - String escapedString = escapeHtmlChar.escapeJsonString(src); - assertEquals("123\\u2028 456", escapedString); - } - - public void testParagraphSeparatorEscaping() throws Exception { - String src = "123\u2029 456"; - String escapedString = escapeHtmlChar.escapeJsonString(src); - assertEquals("123\\u2029 456", escapedString); - } - - public void testControlCharBlockEscaping() throws Exception { - for (char c = '\u007f'; c <= '\u009f'; ++c) { - String src = "123 " + c + " 456"; - String escapedString = escapeHtmlChar.escapeJsonString(src); - assertFalse(src.equals(escapedString)); - } - } - - public void testEqualsEscaping() throws Exception { - String containsEquals = "123=456"; - int index = containsEquals.indexOf('='); - String unicodeValue = convertToUnicodeString(Character.codePointAt(containsEquals, index)); - String escapedString = escapeHtmlChar.escapeJsonString(containsEquals); - assertEquals("123" + unicodeValue + "456", escapedString); - - escapedString = noEscapeHtmlChar.escapeJsonString(containsEquals); - assertEquals(containsEquals, escapedString); - } - - public void testGreaterThanAndLessThanEscaping() throws Exception { - String containsLtGt = "123>456<"; - int gtIndex = containsLtGt.indexOf('>'); - int ltIndex = containsLtGt.indexOf('<'); - String gtAsUnicode = convertToUnicodeString(Character.codePointAt(containsLtGt, gtIndex)); - String ltAsUnicode = convertToUnicodeString(Character.codePointAt(containsLtGt, ltIndex)); - - String escapedString = escapeHtmlChar.escapeJsonString(containsLtGt); - assertEquals("123" + gtAsUnicode + "456" + ltAsUnicode, escapedString); - - escapedString = noEscapeHtmlChar.escapeJsonString(containsLtGt); - assertEquals(containsLtGt, escapedString); - } - - public void testAmpersandEscaping() throws Exception { - String containsAmp = "123&456"; - int ampIndex = containsAmp.indexOf('&'); - String ampAsUnicode = convertToUnicodeString(Character.codePointAt(containsAmp, ampIndex)); - - String escapedString = escapeHtmlChar.escapeJsonString(containsAmp); - assertEquals("123" + ampAsUnicode + "456", escapedString); - - escapedString = noEscapeHtmlChar.escapeJsonString(containsAmp); - assertEquals(containsAmp, escapedString); - - char ampCharAsUnicode = '\u0026'; - String containsAmpUnicode = "123" + ampCharAsUnicode + "456"; - escapedString = escapeHtmlChar.escapeJsonString(containsAmpUnicode); - assertEquals("123" + ampAsUnicode + "456", escapedString); - - escapedString = noEscapeHtmlChar.escapeJsonString(containsAmpUnicode); - assertEquals(containsAmp, escapedString); - } - - public void testSlashEscaping() throws Exception { - String containsSlash = "123\\456"; - String escapedString = escapeHtmlChar.escapeJsonString(containsSlash); - assertEquals("123\\\\456", escapedString); - } - - public void testSingleQuoteNotEscaped() throws Exception { - String containsSingleQuote = "123'456"; - String escapedString = noEscapeHtmlChar.escapeJsonString(containsSingleQuote); - assertEquals(containsSingleQuote, escapedString); - } - - public void testRequiredEscapingUnicodeCharacter() throws Exception { - char unicodeChar = '\u2028'; - String unicodeString = "Testing" + unicodeChar; - - String escapedString = escapeHtmlChar.escapeJsonString(unicodeString); - assertFalse(unicodeString.equals(escapedString)); - assertEquals("Testing\\u2028", escapedString); - } - - public void testUnicodeCharacterStringNoEscaping() throws Exception { - String unicodeString = "\u0065\u0066"; - - String escapedString = escapeHtmlChar.escapeJsonString(unicodeString); - assertEquals(unicodeString, escapedString); - } - - /* - public void testChineseCharacterEscaping() throws Exception { - String unicodeString = "\u597d\u597d\u597d"; - String chineseString = "好好好"; - assertEquals(unicodeString, chineseString); - - String expectedEscapedString = "\\u597d\\u597d\\u597d"; - String escapedString = Escaper.escapeJsonString(chineseString); - assertEquals(expectedEscapedString, escapedString); - } - */ - - private String convertToUnicodeString(int codepoint) { - String hexValue = Integer.toHexString(codepoint); - StringBuilder sb = new StringBuilder("\\u"); - for (int i = 0; i < 4 - hexValue.length(); i++) { - sb.append(0); - } - sb.append(hexValue); - - return sb.toString().toLowerCase(); - } -}