Issue 797: update to gson 2.1

This commit is contained in:
Adrian Cole 2012-01-01 15:49:16 -08:00
parent 7e21b0ad05
commit ef7b91bfea
5 changed files with 213 additions and 228 deletions

View File

@ -104,7 +104,7 @@
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>1.7.2</version> <version>2.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>

View File

@ -20,8 +20,6 @@ package com.google.gson;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
/** /**
* The gson project use package to control access to their objects. However, this prevents us from * The gson project use package to control access to their objects. However, this prevents us from
* doing valid work, like controling the json emitted on a per-object basis. This is here to afford * doing valid work, like controling the json emitted on a per-object basis. This is here to afford
@ -38,8 +36,8 @@ public final class JsonLiteral extends JsonElement {
} }
@Override @Override
protected void toString(Appendable sb, Escaper escaper) throws IOException { public String toString() {
sb.append(literal); return literal.toString();
} }
} }

View File

@ -1,195 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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 com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
/**
* Reads and writes GSON parse trees over streams.
*/
final class Streams {
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return parseRecursive(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
* empty documents instead of throwing.
*/
if (isEmpty) {
return JsonNull.createJsonNull();
}
throw new JsonIOException(e);
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
private static JsonElement parseRecursive(JsonReader reader) throws IOException {
switch (reader.peek()) {
case STRING:
return new JsonPrimitive(reader.nextString());
case NUMBER:
String number = reader.nextString();
return new JsonPrimitive(JsonPrimitive.stringToNumber(number));
case BOOLEAN:
return new JsonPrimitive(reader.nextBoolean());
case NULL:
reader.nextNull();
return JsonNull.createJsonNull();
case BEGIN_ARRAY:
JsonArray array = new JsonArray();
reader.beginArray();
while (reader.hasNext()) {
array.add(parseRecursive(reader));
}
reader.endArray();
return array;
case BEGIN_OBJECT:
JsonObject object = new JsonObject();
reader.beginObject();
while (reader.hasNext()) {
object.add(reader.nextName(), parseRecursive(reader));
}
reader.endObject();
return object;
case END_DOCUMENT:
case NAME:
case END_OBJECT:
case END_ARRAY:
default:
throw new IllegalArgumentException();
}
}
/**
* Writes the JSON element to the writer, recursively.
*/
static void write(JsonElement element, boolean serializeNulls, JsonWriter writer)
throws IOException {
if (element == null || element.isJsonNull()) {
if (serializeNulls) {
writer.nullValue();
}
//BEGIN JCLOUDS PATCH
// * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
} else if (element instanceof JsonLiteral ) {
writer.value(JsonLiteral.class.cast(element));
//END JCLOUDS PATCH
} else if (element.isJsonPrimitive()) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isNumber()) {
writer.value(primitive.getAsNumber());
} else if (primitive.isBoolean()) {
writer.value(primitive.getAsBoolean());
} else {
writer.value(primitive.getAsString());
}
} else if (element.isJsonArray()) {
writer.beginArray();
for (JsonElement e : element.getAsJsonArray()) {
/* always print null when its parent element is an array! */
if (e.isJsonNull()) {
writer.nullValue();
continue;
}
write(e, serializeNulls, writer);
}
writer.endArray();
} else if (element.isJsonObject()) {
writer.beginObject();
for (Map.Entry<String, JsonElement> e : element.getAsJsonObject().entrySet()) {
JsonElement value = e.getValue();
if (!serializeNulls && value.isJsonNull()) {
continue;
}
writer.name(e.getKey());
write(value, serializeNulls, writer);
}
writer.endObject();
} else {
throw new IllegalArgumentException("Couldn't write " + element.getClass());
}
}
static Writer writerForAppendable(Appendable appendable) {
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
}
/**
* Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
* is used.
*/
private static class AppendableWriter extends Writer {
private final Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite();
private AppendableWriter(Appendable appendable) {
this.appendable = appendable;
}
@Override public void write(char[] chars, int offset, int length) throws IOException {
currentWrite.chars = chars;
appendable.append(currentWrite, offset, offset + length);
}
@Override public void write(int i) throws IOException {
appendable.append((char) i);
}
@Override public void flush() {}
@Override public void close() {}
/**
* A mutable char sequence pointing at a single char[].
*/
static class CurrentWrite implements CharSequence {
char[] chars;
public int length() {
return chars.length;
}
public char charAt(int i) {
return chars[i];
}
public CharSequence subSequence(int start, int end) {
return new String(chars, start, end - start);
}
}
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2010 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.internal;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonLiteral;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Writer;
/**
* Reads and writes GSON parse trees over streams.
*/
public final class Streams {
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
public static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return TypeAdapters.JSON_ELEMENT.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
* empty documents instead of throwing.
*/
if (isEmpty) {
return JsonNull.INSTANCE;
}
throw new JsonIOException(e);
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Writes the JSON element to the writer, recursively.
*/
public static void write(JsonElement element, JsonWriter writer) throws IOException {
//BEGIN JCLOUDS PATCH
// * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
if (element instanceof JsonLiteral ) {
writer.value(JsonLiteral.class.cast(element));
//END JCLOUDS PATCH
} else {
TypeAdapters.JSON_ELEMENT.write(writer, element);
}
}
public static Writer writerForAppendable(Appendable appendable) {
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
}
/**
* Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
* is used.
*/
private static class AppendableWriter extends Writer {
private final Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite();
private AppendableWriter(Appendable appendable) {
this.appendable = appendable;
}
@Override public void write(char[] chars, int offset, int length) throws IOException {
currentWrite.chars = chars;
appendable.append(currentWrite, offset, offset + length);
}
@Override public void write(int i) throws IOException {
appendable.append((char) i);
}
@Override public void flush() {}
@Override public void close() {}
/**
* A mutable char sequence pointing at a single char[].
*/
static class CurrentWrite implements CharSequence {
char[] chars;
public int length() {
return chars.length;
}
public char charAt(int i) {
return chars[i];
}
public CharSequence subSequence(int start, int end) {
return new String(chars, start, end - start);
}
}
}
}

View File

@ -1,21 +1,19 @@
/** /*
* Licensed to jclouds, Inc. (jclouds) under one or more * Copyright (C) 2010 Google Inc.
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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 * 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
* *
* Unless required by applicable law or agreed to in writing, * http://www.apache.org/licenses/LICENSE-2.0
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * Unless required by applicable law or agreed to in writing, software
* KIND, either express or implied. See the License for the * distributed under the License is distributed on an "AS IS" BASIS,
* specific language governing permissions and limitations * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* under the License. * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package com.google.gson.stream; package com.google.gson.stream;
import java.io.Closeable; import java.io.Closeable;
@ -124,7 +122,7 @@ import com.google.gson.JsonLiteral;
* @author Jesse Wilson * @author Jesse Wilson
* @since 1.6 * @since 1.6
*/ */
public final class JsonWriter implements Closeable { public class JsonWriter implements Closeable {
/** The output data, containing at most one top-level array or object. */ /** The output data, containing at most one top-level array or object. */
private final Writer out; private final Writer out;
@ -149,6 +147,10 @@ public final class JsonWriter implements Closeable {
private boolean htmlSafe; private boolean htmlSafe;
private String deferredName;
private boolean serializeNulls = true;
/** /**
* Creates a new instance that writes a JSON-encoded stream to {@code out}. * Creates a new instance that writes a JSON-encoded stream to {@code out}.
* For best performance, ensure {@link Writer} is buffered; wrapping in * For best performance, ensure {@link Writer} is buffered; wrapping in
@ -169,7 +171,7 @@ public final class JsonWriter implements Closeable {
* *
* @param indent a string containing only whitespace. * @param indent a string containing only whitespace.
*/ */
public void setIndent(String indent) { public final void setIndent(String indent) {
if (indent.length() == 0) { if (indent.length() == 0) {
this.indent = null; this.indent = null;
this.separator = ":"; this.separator = ":";
@ -191,7 +193,7 @@ public final class JsonWriter implements Closeable {
* Double#isInfinite() infinities}. * Double#isInfinite() infinities}.
* </ul> * </ul>
*/ */
public void setLenient(boolean lenient) { public final void setLenient(boolean lenient) {
this.lenient = lenient; this.lenient = lenient;
} }
@ -209,7 +211,7 @@ public final class JsonWriter implements Closeable {
* setting, your XML/HTML encoder should replace these characters with the * setting, your XML/HTML encoder should replace these characters with the
* corresponding escape sequences. * corresponding escape sequences.
*/ */
public void setHtmlSafe(boolean htmlSafe) { public final void setHtmlSafe(boolean htmlSafe) {
this.htmlSafe = htmlSafe; this.htmlSafe = htmlSafe;
} }
@ -217,10 +219,26 @@ public final class JsonWriter implements Closeable {
* Returns true if this writer writes JSON that's safe for inclusion in HTML * Returns true if this writer writes JSON that's safe for inclusion in HTML
* and XML documents. * and XML documents.
*/ */
public boolean isHtmlSafe() { public final boolean isHtmlSafe() {
return htmlSafe; return htmlSafe;
} }
/**
* Sets whether object members are serialized when their value is null.
* This has no impact on array elements. The default is true.
*/
public final void setSerializeNulls(boolean serializeNulls) {
this.serializeNulls = serializeNulls;
}
/**
* Returns true if object members are serialized when their value is null.
* This has no impact on array elements. The default is true.
*/
public final boolean getSerializeNulls() {
return serializeNulls;
}
/** /**
* Begins encoding a new array. Each call to this method must be paired with * Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}. * a call to {@link #endArray}.
@ -228,6 +246,7 @@ public final class JsonWriter implements Closeable {
* @return this writer. * @return this writer.
*/ */
public JsonWriter beginArray() throws IOException { public JsonWriter beginArray() throws IOException {
writeDeferredName();
return open(JsonScope.EMPTY_ARRAY, "["); return open(JsonScope.EMPTY_ARRAY, "[");
} }
@ -247,6 +266,7 @@ public final class JsonWriter implements Closeable {
* @return this writer. * @return this writer.
*/ */
public JsonWriter beginObject() throws IOException { public JsonWriter beginObject() throws IOException {
writeDeferredName();
return open(JsonScope.EMPTY_OBJECT, "{"); return open(JsonScope.EMPTY_OBJECT, "{");
} }
@ -280,6 +300,9 @@ public final class JsonWriter implements Closeable {
if (context != nonempty && context != empty) { if (context != nonempty && context != empty) {
throw new IllegalStateException("Nesting problem: " + stack); throw new IllegalStateException("Nesting problem: " + stack);
} }
if (deferredName != null) {
throw new IllegalStateException("Dangling name: " + deferredName);
}
stack.remove(stack.size() - 1); stack.remove(stack.size() - 1);
if (context == nonempty) { if (context == nonempty) {
@ -313,11 +336,21 @@ public final class JsonWriter implements Closeable {
if (name == null) { if (name == null) {
throw new NullPointerException("name == null"); throw new NullPointerException("name == null");
} }
beforeName(); if (deferredName != null) {
string(name); throw new IllegalStateException();
}
deferredName = name;
return this; return this;
} }
private void writeDeferredName() throws IOException {
if (deferredName != null) {
beforeName();
string(deferredName);
deferredName = null;
}
}
/** /**
* Encodes {@code value}. * Encodes {@code value}.
* *
@ -328,6 +361,7 @@ public final class JsonWriter implements Closeable {
if (value == null) { if (value == null) {
return nullValue(); return nullValue();
} }
writeDeferredName();
beforeValue(false); beforeValue(false);
string(value); string(value);
return this; return this;
@ -339,29 +373,42 @@ public final class JsonWriter implements Closeable {
* @return this writer. * @return this writer.
*/ */
public JsonWriter nullValue() throws IOException { public JsonWriter nullValue() throws IOException {
if (deferredName != null) {
if (serializeNulls) {
writeDeferredName();
} else {
deferredName = null;
return this; // skip the name and the value
}
}
beforeValue(false); beforeValue(false);
out.write("null"); out.write("null");
return this; return this;
} }
//BEGIN JCLOUDS PATCH //BEGIN JCLOUDS PATCH
// * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/> //* @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
/** /**
* Writes {@code value} literally * Writes {@code value} literally
* *
* @return this writer. * @return this writer.
*/ */
public JsonWriter value(JsonLiteral value) throws IOException { public JsonWriter value(JsonLiteral value) throws IOException {
beforeValue(false); if (value == null) {
out.write(value.toString()); return nullValue();
return this; }
writeDeferredName();
beforeValue(false);
out.write(value.toString());
return this;
} }
//END JCLOUDS PATCH //END JCLOUDS PATCH
/** /**
* Encodes {@code value}. * Encodes {@code value}.
* *
* @return this writer. * @return this writer.
*/ */
public JsonWriter value(boolean value) throws IOException { public JsonWriter value(boolean value) throws IOException {
writeDeferredName();
beforeValue(false); beforeValue(false);
out.write(value ? "true" : "false"); out.write(value ? "true" : "false");
return this; return this;
@ -378,6 +425,7 @@ public final class JsonWriter implements Closeable {
if (Double.isNaN(value) || Double.isInfinite(value)) { if (Double.isNaN(value) || Double.isInfinite(value)) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value); throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
} }
writeDeferredName();
beforeValue(false); beforeValue(false);
out.append(Double.toString(value)); out.append(Double.toString(value));
return this; return this;
@ -389,6 +437,7 @@ public final class JsonWriter implements Closeable {
* @return this writer. * @return this writer.
*/ */
public JsonWriter value(long value) throws IOException { public JsonWriter value(long value) throws IOException {
writeDeferredName();
beforeValue(false); beforeValue(false);
out.write(Long.toString(value)); out.write(Long.toString(value));
return this; return this;
@ -406,6 +455,7 @@ public final class JsonWriter implements Closeable {
return nullValue(); return nullValue();
} }
writeDeferredName();
String string = value.toString(); String string = value.toString();
if (!lenient if (!lenient
&& (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
@ -447,6 +497,10 @@ public final class JsonWriter implements Closeable {
* quotation marks except for the characters that must be escaped: * quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters * quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)." * (U+0000 through U+001F)."
*
* We also escape '\u2028' and '\u2029', which JavaScript interprets as
* newline characters. This prevents eval() from failing with a syntax
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/ */
switch (c) { switch (c) {
case '"': case '"':
@ -487,6 +541,11 @@ public final class JsonWriter implements Closeable {
} }
break; break;
case '\u2028':
case '\u2029':
out.write(String.format("\\u%04x", (int) c));
break;
default: default:
if (c <= 0x1F) { if (c <= 0x1F) {
out.write(String.format("\\u%04x", (int) c)); out.write(String.format("\\u%04x", (int) c));