package com.google.protobuf.util;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.stream.JsonReader;
import com.google.protobuf.Any;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import com.google.protobuf.BytesValue;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.Type;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.DoubleValue;
import com.google.protobuf.Duration;
import com.google.protobuf.DynamicMessage;
import com.google.protobuf.FieldMask;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.NullValue;
import com.google.protobuf.StringValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Timestamp;
import com.google.protobuf.UInt32Value;
import com.google.protobuf.UInt64Value;
import com.google.protobuf.Value;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Logger;
public class JsonFormat {
private static final Logger logger = Logger.getLogger(JsonFormat.class.getName());
private JsonFormat() {}
public static Printer printer() {
return new Printer(
com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
TypeRegistry.getEmptyTypeRegistry(),
false,
Collections.<FieldDescriptor>emptySet(),
false,
false,
false,
false);
}
public static class Printer {
private final com.google.protobuf.TypeRegistry registry;
private final TypeRegistry oldRegistry;
private boolean alwaysOutputDefaultValueFields;
private Set<FieldDescriptor> includingDefaultValueFields;
private final boolean preservingProtoFieldNames;
private final boolean omittingInsignificantWhitespace;
private final boolean printingEnumsAsInts;
private final boolean sortingMapKeys;
private Printer(
com.google.protobuf.TypeRegistry registry,
TypeRegistry oldRegistry,
boolean alwaysOutputDefaultValueFields,
Set<FieldDescriptor> includingDefaultValueFields,
boolean preservingProtoFieldNames,
boolean omittingInsignificantWhitespace,
boolean printingEnumsAsInts,
boolean sortingMapKeys) {
this.registry = registry;
this.oldRegistry = oldRegistry;
this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
this.includingDefaultValueFields = includingDefaultValueFields;
this.preservingProtoFieldNames = preservingProtoFieldNames;
this.omittingInsignificantWhitespace = omittingInsignificantWhitespace;
this.printingEnumsAsInts = printingEnumsAsInts;
this.sortingMapKeys = sortingMapKeys;
}
public Printer usingTypeRegistry(TypeRegistry oldRegistry) {
if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
|| this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
return new Printer(
com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
omittingInsignificantWhitespace,
printingEnumsAsInts,
sortingMapKeys);
}
public Printer usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
|| this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
return new Printer(
registry,
oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
omittingInsignificantWhitespace,
printingEnumsAsInts,
sortingMapKeys);
}
public Printer includingDefaultValueFields() {
checkUnsetIncludingDefaultValueFields();
return new Printer(
registry,
oldRegistry,
true,
Collections.<FieldDescriptor>emptySet(),
preservingProtoFieldNames,
omittingInsignificantWhitespace,
printingEnumsAsInts,
sortingMapKeys);
}
public Printer printingEnumsAsInts() {
checkUnsetPrintingEnumsAsInts();
return new Printer(
registry,
oldRegistry,
alwaysOutputDefaultValueFields,
Collections.<FieldDescriptor>emptySet(),
preservingProtoFieldNames,
omittingInsignificantWhitespace,
true,
sortingMapKeys);
}
private void checkUnsetPrintingEnumsAsInts() {
if (printingEnumsAsInts) {
throw new IllegalStateException("JsonFormat printingEnumsAsInts has already been set.");
}
}
public Printer includingDefaultValueFields(Set<FieldDescriptor> fieldsToAlwaysOutput) {
Preconditions.checkArgument(
null != fieldsToAlwaysOutput && !fieldsToAlwaysOutput.isEmpty(),
"Non-empty Set must be supplied for includingDefaultValueFields.");
checkUnsetIncludingDefaultValueFields();
return new Printer(
registry,
oldRegistry,
false,
Collections.unmodifiableSet(new HashSet<>(fieldsToAlwaysOutput)),
preservingProtoFieldNames,
omittingInsignificantWhitespace,
printingEnumsAsInts,
sortingMapKeys);
}
private void checkUnsetIncludingDefaultValueFields() {
if (alwaysOutputDefaultValueFields || !includingDefaultValueFields.isEmpty()) {
throw new IllegalStateException(
"JsonFormat includingDefaultValueFields has already been set.");
}
}
public Printer preservingProtoFieldNames() {
return new Printer(
registry,
oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
true,
omittingInsignificantWhitespace,
printingEnumsAsInts,
sortingMapKeys);
}
public Printer omittingInsignificantWhitespace() {
return new Printer(
registry,
oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
true,
printingEnumsAsInts,
sortingMapKeys);
}
public Printer sortingMapKeys() {
return new Printer(
registry,
oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
omittingInsignificantWhitespace,
printingEnumsAsInts,
true);
}
public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
new PrinterImpl(
registry,
oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
output,
omittingInsignificantWhitespace,
printingEnumsAsInts,
sortingMapKeys)
.print(message);
}
public String print(MessageOrBuilder message) throws InvalidProtocolBufferException {
try {
StringBuilder builder = new StringBuilder();
appendTo(message, builder);
return builder.toString();
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
public static Parser parser() {
return new Parser(
com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
TypeRegistry.getEmptyTypeRegistry(),
false,
Parser.DEFAULT_RECURSION_LIMIT);
}
public static class Parser {
private final com.google.protobuf.TypeRegistry registry;
private final TypeRegistry oldRegistry;
private final boolean ignoringUnknownFields;
private final int recursionLimit;
private static final int DEFAULT_RECURSION_LIMIT = 100;
private Parser(
com.google.protobuf.TypeRegistry registry,
TypeRegistry oldRegistry,
boolean ignoreUnknownFields,
int recursionLimit) {
this.registry = registry;
this.oldRegistry = oldRegistry;
this.ignoringUnknownFields = ignoreUnknownFields;
this.recursionLimit = recursionLimit;
}
public Parser usingTypeRegistry(TypeRegistry oldRegistry) {
if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
|| this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
return new Parser(
com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
oldRegistry,
ignoringUnknownFields,
recursionLimit);
}
public Parser usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
|| this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
}
public Parser ignoringUnknownFields() {
return new Parser(this.registry, oldRegistry, true, recursionLimit);
}
public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
.merge(json, builder);
}
public void merge(Reader json, Message.Builder builder) throws IOException {
new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
.merge(json, builder);
}
Parser usingRecursionLimit(int recursionLimit) {
return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
}
}
public static class TypeRegistry {
private static class EmptyTypeRegistryHolder {
private static final TypeRegistry EMPTY =
new TypeRegistry(Collections.<String, Descriptor>emptyMap());
}
public static TypeRegistry getEmptyTypeRegistry() {
return EmptyTypeRegistryHolder.EMPTY;
}
public static Builder newBuilder() {
return new Builder();
}
public Descriptor find(String name) {
return types.get(name);
}
Descriptor getDescriptorForTypeUrl(String typeUrl) throws InvalidProtocolBufferException {
return find(getTypeName(typeUrl));
}
private final Map<String, Descriptor> types;
private TypeRegistry(Map<String, Descriptor> types) {
this.types = types;
}
public static class Builder {
private Builder() {}
@CanIgnoreReturnValue
public Builder add(Descriptor messageType) {
if (types == null) {
throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
}
addFile(messageType.getFile());
return this;
}
@CanIgnoreReturnValue
public Builder add(Iterable<Descriptor> messageTypes) {
if (types == null) {
throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
}
for (Descriptor type : messageTypes) {
addFile(type.getFile());
}
return this;
}
public TypeRegistry build() {
TypeRegistry result = new TypeRegistry(types);
types = null;
return result;
}
private void addFile(FileDescriptor file) {
if (!files.add(file.getFullName())) {
return;
}
for (FileDescriptor dependency : file.getDependencies()) {
addFile(dependency);
}
for (Descriptor message : file.getMessageTypes()) {
addMessage(message);
}
}
private void addMessage(Descriptor message) {
for (Descriptor nestedType : message.getNestedTypes()) {
addMessage(nestedType);
}
if (types.containsKey(message.getFullName())) {
logger.warning("Type " + message.getFullName() + " is added multiple times.");
return;
}
types.put(message.getFullName(), message);
}
private final Set<String> files = new HashSet<String>();
private Map<String, Descriptor> types = new HashMap<String, Descriptor>();
}
}
interface TextGenerator {
void indent();
void outdent();
void print(final CharSequence text) throws IOException;
}
private static final class CompactTextGenerator implements TextGenerator {
private final Appendable output;
private CompactTextGenerator(final Appendable output) {
this.output = output;
}
@Override
public void indent() {}
@Override
public void outdent() {}
@Override
public void print(final CharSequence text) throws IOException {
output.append(text);
}
}
private static final class PrettyTextGenerator implements TextGenerator {
private final Appendable output;
private final StringBuilder indent = new StringBuilder();
private boolean atStartOfLine = true;
private PrettyTextGenerator(final Appendable output) {
this.output = output;
}
@Override
public void indent() {
indent.append(" ");
}
@Override
public void outdent() {
final int length = indent.length();
if (length < 2) {
throw new IllegalArgumentException(" Outdent() without matching Indent().");
}
indent.delete(length - 2, length);
}
@Override
public void print(final CharSequence text) throws IOException {
final int size = text.length();
int pos = 0;
for (int i = 0; i < size; i++) {
if (text.charAt(i) == '\n') {
write(text.subSequence(pos, i + 1));
pos = i + 1;
atStartOfLine = true;
}
}
write(text.subSequence(pos, size));
}
private void write(final CharSequence data) throws IOException {
if (data.length() == 0) {
return;
}
if (atStartOfLine) {
atStartOfLine = false;
output.append(indent);
}
output.append(data);
}
}
private static final class PrinterImpl {
private final com.google.protobuf.TypeRegistry registry;
private final TypeRegistry oldRegistry;
private final boolean alwaysOutputDefaultValueFields;
private final Set<FieldDescriptor> includingDefaultValueFields;
private final boolean preservingProtoFieldNames;
private final boolean printingEnumsAsInts;
private final boolean sortingMapKeys;
private final TextGenerator generator;
private final Gson gson;
private final CharSequence blankOrSpace;
private final CharSequence blankOrNewLine;
private static class GsonHolder {
private static final Gson DEFAULT_GSON = new GsonBuilder().create();
}
PrinterImpl(
com.google.protobuf.TypeRegistry registry,
TypeRegistry oldRegistry,
boolean alwaysOutputDefaultValueFields,
Set<FieldDescriptor> includingDefaultValueFields,
boolean preservingProtoFieldNames,
Appendable jsonOutput,
boolean omittingInsignificantWhitespace,
boolean printingEnumsAsInts,
boolean sortingMapKeys) {
this.registry = registry;
this.oldRegistry = oldRegistry;
this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
this.includingDefaultValueFields = includingDefaultValueFields;
this.preservingProtoFieldNames = preservingProtoFieldNames;
this.printingEnumsAsInts = printingEnumsAsInts;
this.sortingMapKeys = sortingMapKeys;
this.gson = GsonHolder.DEFAULT_GSON;
if (omittingInsignificantWhitespace) {
this.generator = new CompactTextGenerator(jsonOutput);
this.blankOrSpace = "";
this.blankOrNewLine = "";
} else {
this.generator = new PrettyTextGenerator(jsonOutput);
this.blankOrSpace = " ";
this.blankOrNewLine = "\n";
}
}
void print(MessageOrBuilder message) throws IOException {
WellKnownTypePrinter specialPrinter =
wellKnownTypePrinters.get(message.getDescriptorForType().getFullName());
if (specialPrinter != null) {
specialPrinter.print(this, message);
return;
}
print(message, null);
}
private interface WellKnownTypePrinter {
void print(PrinterImpl printer, MessageOrBuilder message) throws IOException;
}
private static final Map<String, WellKnownTypePrinter> wellKnownTypePrinters =
buildWellKnownTypePrinters();
private static Map<String, WellKnownTypePrinter> buildWellKnownTypePrinters() {
Map<String, WellKnownTypePrinter> printers = new HashMap<String, WellKnownTypePrinter>();
printers.put(
Any.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printAny(message);
}
});
WellKnownTypePrinter wrappersPrinter =
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printWrapper(message);
}
};
printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
printers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
printers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter);
printers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter);
printers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(
Timestamp.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printTimestamp(message);
}
});
printers.put(
Duration.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printDuration(message);
}
});
printers.put(
FieldMask.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printFieldMask(message);
}
});
printers.put(
Struct.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printStruct(message);
}
});
printers.put(
Value.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printValue(message);
}
});
printers.put(
ListValue.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
@Override
public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
printer.printListValue(message);
}
});
return printers;
}
private void printAny(MessageOrBuilder message) throws IOException {
if (Any.getDefaultInstance().equals(message)) {
generator.print("{}");
return;
}
Descriptor descriptor = message.getDescriptorForType();
FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
FieldDescriptor valueField = descriptor.findFieldByName("value");
if (typeUrlField == null
|| valueField == null
|| typeUrlField.getType() != FieldDescriptor.Type.STRING
|| valueField.getType() != FieldDescriptor.Type.BYTES) {
throw new InvalidProtocolBufferException("Invalid Any type.");
}
String typeUrl = (String) message.getField(typeUrlField);
Descriptor type = registry.getDescriptorForTypeUrl(typeUrl);
if (type == null) {
type = oldRegistry.getDescriptorForTypeUrl(typeUrl);
if (type == null) {
throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
}
}
ByteString content = (ByteString) message.getField(valueField);
Message contentMessage =
DynamicMessage.getDefaultInstance(type).getParserForType().parseFrom(content);
WellKnownTypePrinter printer = wellKnownTypePrinters.get(getTypeName(typeUrl));
if (printer != null) {
generator.print("{" + blankOrNewLine);
generator.indent();
generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl) + "," + blankOrNewLine);
generator.print("\"value\":" + blankOrSpace);
printer.print(this, contentMessage);
generator.print(blankOrNewLine);
generator.outdent();
generator.print("}");
} else {
print(contentMessage, typeUrl);
}
}
private void printWrapper(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
FieldDescriptor valueField = descriptor.findFieldByName("value");
if (valueField == null) {
throw new InvalidProtocolBufferException("Invalid Wrapper type.");
}
printSingleFieldValue(valueField, message.getField(valueField));
}
private ByteString toByteString(MessageOrBuilder message) {
if (message instanceof Message) {
return ((Message) message).toByteString();
} else {
return ((Message.Builder) message).build().toByteString();
}
}
private void printTimestamp(MessageOrBuilder message) throws IOException {
Timestamp value = Timestamp.parseFrom(toByteString(message));
generator.print("\"" + Timestamps.toString(value) + "\"");
}
private void printDuration(MessageOrBuilder message) throws IOException {
Duration value = Duration.parseFrom(toByteString(message));
generator.print("\"" + Durations.toString(value) + "\"");
}
private void printFieldMask(MessageOrBuilder message) throws IOException {
FieldMask value = FieldMask.parseFrom(toByteString(message));
generator.print("\"" + FieldMaskUtil.toJsonString(value) + "\"");
}
private void printStruct(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
FieldDescriptor field = descriptor.findFieldByName("fields");
if (field == null) {
throw new InvalidProtocolBufferException("Invalid Struct type.");
}
printMapFieldValue(field, message.getField(field));
}
private void printValue(MessageOrBuilder message) throws IOException {
Map<FieldDescriptor, Object> fields = message.getAllFields();
if (fields.isEmpty()) {
generator.print("null");
return;
}
if (fields.size() != 1) {
throw new InvalidProtocolBufferException("Invalid Value type.");
}
for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
printSingleFieldValue(entry.getKey(), entry.getValue());
}
}
private void printListValue(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
FieldDescriptor field = descriptor.findFieldByName("values");
if (field == null) {
throw new InvalidProtocolBufferException("Invalid ListValue type.");
}
printRepeatedFieldValue(field, message.getField(field));
}
private void print(MessageOrBuilder message, String typeUrl) throws IOException {
generator.print("{" + blankOrNewLine);
generator.indent();
boolean printedField = false;
if (typeUrl != null) {
generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl));
printedField = true;
}
Map<FieldDescriptor, Object> fieldsToPrint = null;
if (alwaysOutputDefaultValueFields || !includingDefaultValueFields.isEmpty()) {
fieldsToPrint = new TreeMap<FieldDescriptor, Object>(message.getAllFields());
for (FieldDescriptor field : message.getDescriptorForType().getFields()) {
if (field.isOptional()) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
&& !message.hasField(field)) {
continue;
}
OneofDescriptor oneof = field.getContainingOneof();
if (oneof != null && !message.hasField(field)) {
continue;
}
}
if (!fieldsToPrint.containsKey(field)
&& (alwaysOutputDefaultValueFields || includingDefaultValueFields.contains(field))) {
fieldsToPrint.put(field, message.getField(field));
}
}
} else {
fieldsToPrint = message.getAllFields();
}
for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
if (printedField) {
generator.print("," + blankOrNewLine);
} else {
printedField = true;
}
printField(field.getKey(), field.getValue());
}
if (printedField) {
generator.print(blankOrNewLine);
}
generator.outdent();
generator.print("}");
}
private void printField(FieldDescriptor field, Object value) throws IOException {
if (preservingProtoFieldNames) {
generator.print("\"" + field.getName() + "\":" + blankOrSpace);
} else {
generator.print("\"" + field.getJsonName() + "\":" + blankOrSpace);
}
if (field.isMapField()) {
printMapFieldValue(field, value);
} else if (field.isRepeated()) {
printRepeatedFieldValue(field, value);
} else {
printSingleFieldValue(field, value);
}
}
@SuppressWarnings("rawtypes")
private void printRepeatedFieldValue(FieldDescriptor field, Object value) throws IOException {
generator.print("[");
boolean printedElement = false;
for (Object element : (List) value) {
if (printedElement) {
generator.print("," + blankOrSpace);
} else {
printedElement = true;
}
printSingleFieldValue(field, element);
}
generator.print("]");
}
@SuppressWarnings("rawtypes")
private void printMapFieldValue(FieldDescriptor field, Object value) throws IOException {
Descriptor type = field.getMessageType();
FieldDescriptor keyField = type.findFieldByName("key");
FieldDescriptor valueField = type.findFieldByName("value");
if (keyField == null || valueField == null) {
throw new InvalidProtocolBufferException("Invalid map field.");
}
generator.print("{" + blankOrNewLine);
generator.indent();
@SuppressWarnings("unchecked")
Collection<Object> elements = (List<Object>) value;
if (sortingMapKeys && !elements.isEmpty()) {
Comparator<Object> cmp = null;
if (keyField.getType() == FieldDescriptor.Type.STRING) {
cmp = new Comparator<Object>() {
@Override
public int compare(final Object o1, final Object o2) {
ByteString s1 = ByteString.copyFromUtf8((String) o1);
ByteString s2 = ByteString.copyFromUtf8((String) o2);
return ByteString.unsignedLexicographicalComparator().compare(s1, s2);
}
};
}
TreeMap<Object, Object> tm = new TreeMap<Object, Object>(cmp);
for (Object element : elements) {
Message entry = (Message) element;
Object entryKey = entry.getField(keyField);
tm.put(entryKey, element);
}
elements = tm.values();
}
boolean printedElement = false;
for (Object element : elements) {
Message entry = (Message) element;
Object entryKey = entry.getField(keyField);
Object entryValue = entry.getField(valueField);
if (printedElement) {
generator.print("," + blankOrNewLine);
} else {
printedElement = true;
}
printSingleFieldValue(keyField, entryKey, true);
generator.print(":" + blankOrSpace);
printSingleFieldValue(valueField, entryValue);
}
if (printedElement) {
generator.print(blankOrNewLine);
}
generator.outdent();
generator.print("}");
}
private void printSingleFieldValue(FieldDescriptor field, Object value) throws IOException {
printSingleFieldValue(field, value, false);
}
private void printSingleFieldValue(
final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)
throws IOException {
switch (field.getType()) {
case INT32:
case SINT32:
case SFIXED32:
if (alwaysWithQuotes) {
generator.print("\"");
}
generator.print(((Integer) value).toString());
if (alwaysWithQuotes) {
generator.print("\"");
}
break;
case INT64:
case SINT64:
case SFIXED64:
generator.print("\"" + ((Long) value).toString() + "\"");
break;
case BOOL:
if (alwaysWithQuotes) {
generator.print("\"");
}
if (((Boolean) value).booleanValue()) {
generator.print("true");
} else {
generator.print("false");
}
if (alwaysWithQuotes) {
generator.print("\"");
}
break;
case FLOAT:
Float floatValue = (Float) value;
if (floatValue.isNaN()) {
generator.print("\"NaN\"");
} else if (floatValue.isInfinite()) {
if (floatValue < 0) {
generator.print("\"-Infinity\"");
} else {
generator.print("\"Infinity\"");
}
} else {
if (alwaysWithQuotes) {
generator.print("\"");
}
generator.print(floatValue.toString());
if (alwaysWithQuotes) {
generator.print("\"");
}
}
break;
case DOUBLE:
Double doubleValue = (Double) value;
if (doubleValue.isNaN()) {
generator.print("\"NaN\"");
} else if (doubleValue.isInfinite()) {
if (doubleValue < 0) {
generator.print("\"-Infinity\"");
} else {
generator.print("\"Infinity\"");
}
} else {
if (alwaysWithQuotes) {
generator.print("\"");
}
generator.print(doubleValue.toString());
if (alwaysWithQuotes) {
generator.print("\"");
}
}
break;
case UINT32:
case FIXED32:
if (alwaysWithQuotes) {
generator.print("\"");
}
generator.print(unsignedToString((Integer) value));
if (alwaysWithQuotes) {
generator.print("\"");
}
break;
case UINT64:
case FIXED64:
generator.print("\"" + unsignedToString((Long) value) + "\"");
break;
case STRING:
generator.print(gson.toJson(value));
break;
case BYTES:
generator.print("\"");
generator.print(BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
generator.print("\"");
break;
case ENUM:
if (field.getEnumType().getFullName().equals("google.protobuf.NullValue")) {
if (alwaysWithQuotes) {
generator.print("\"");
}
generator.print("null");
if (alwaysWithQuotes) {
generator.print("\"");
}
} else {
if (printingEnumsAsInts || ((EnumValueDescriptor) value).getIndex() == -1) {
generator.print(String.valueOf(((EnumValueDescriptor) value).getNumber()));
} else {
generator.print("\"" + ((EnumValueDescriptor) value).getName() + "\"");
}
}
break;
case MESSAGE:
case GROUP:
print((Message) value);
break;
}
}
}
private static String unsignedToString(final int value) {
if (value >= 0) {
return Integer.toString(value);
} else {
return Long.toString(value & 0x00000000FFFFFFFFL);
}
}
private static String unsignedToString(final long value) {
if (value >= 0) {
return Long.toString(value);
} else {
return BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1).toString();
}
}
private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
String[] parts = typeUrl.split("/");
if (parts.length == 1) {
throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
}
return parts[parts.length - 1];
}
private static class ParserImpl {
private final com.google.protobuf.TypeRegistry registry;
private final TypeRegistry oldRegistry;
private final JsonParser jsonParser;
private final boolean ignoringUnknownFields;
private final int recursionLimit;
private int currentDepth;
ParserImpl(
com.google.protobuf.TypeRegistry registry,
TypeRegistry oldRegistry,
boolean ignoreUnknownFields,
int recursionLimit) {
this.registry = registry;
this.oldRegistry = oldRegistry;
this.ignoringUnknownFields = ignoreUnknownFields;
this.jsonParser = new JsonParser();
this.recursionLimit = recursionLimit;
this.currentDepth = 0;
}
void merge(Reader json, Message.Builder builder) throws IOException {
try {
JsonReader reader = new JsonReader(json);
reader.setLenient(false);
merge(jsonParser.parse(reader), builder);
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (JsonIOException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw new InvalidProtocolBufferException(e.getMessage());
}
} catch (Exception e) {
throw new InvalidProtocolBufferException(e.getMessage());
}
}
void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
try {
JsonReader reader = new JsonReader(new StringReader(json));
reader.setLenient(false);
merge(jsonParser.parse(reader), builder);
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
throw new InvalidProtocolBufferException(e.getMessage());
}
}
private interface WellKnownTypeParser {
void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException;
}
private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers =
buildWellKnownTypeParsers();
private static Map<String, WellKnownTypeParser> buildWellKnownTypeParsers() {
Map<String, WellKnownTypeParser> parsers = new HashMap<String, WellKnownTypeParser>();
parsers.put(
Any.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeAny(json, builder);
}
});
WellKnownTypeParser wrappersPrinter =
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeWrapper(json, builder);
}
};
parsers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(
Timestamp.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeTimestamp(json, builder);
}
});
parsers.put(
Duration.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeDuration(json, builder);
}
});
parsers.put(
FieldMask.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeFieldMask(json, builder);
}
});
parsers.put(
Struct.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeStruct(json, builder);
}
});
parsers.put(
ListValue.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeListValue(json, builder);
}
});
parsers.put(
Value.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override
public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
parser.mergeValue(json, builder);
}
});
return parsers;
}
private void merge(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
WellKnownTypeParser specialParser =
wellKnownTypeParsers.get(builder.getDescriptorForType().getFullName());
if (specialParser != null) {
specialParser.merge(this, json, builder);
return;
}
mergeMessage(json, builder, false);
}
private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps =
new HashMap<Descriptor, Map<String, FieldDescriptor>>();
private Map<String, FieldDescriptor> getFieldNameMap(Descriptor descriptor) {
if (!fieldNameMaps.containsKey(descriptor)) {
Map<String, FieldDescriptor> fieldNameMap = new HashMap<String, FieldDescriptor>();
for (FieldDescriptor field : descriptor.getFields()) {
fieldNameMap.put(field.getName(), field);
fieldNameMap.put(field.getJsonName(), field);
}
fieldNameMaps.put(descriptor, fieldNameMap);
return fieldNameMap;
}
return fieldNameMaps.get(descriptor);
}
private void mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)
throws InvalidProtocolBufferException {
if (!(json instanceof JsonObject)) {
throw new InvalidProtocolBufferException("Expect message object but got: " + json);
}
JsonObject object = (JsonObject) json;
Map<String, FieldDescriptor> fieldNameMap = getFieldNameMap(builder.getDescriptorForType());
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
if (skipTypeUrl && entry.getKey().equals("@type")) {
continue;
}
FieldDescriptor field = fieldNameMap.get(entry.getKey());
if (field == null) {
if (ignoringUnknownFields) {
continue;
}
throw new InvalidProtocolBufferException(
"Cannot find field: "
+ entry.getKey()
+ " in message "
+ builder.getDescriptorForType().getFullName());
}
mergeField(field, entry.getValue(), builder);
}
}
private void mergeAny(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor descriptor = builder.getDescriptorForType();
FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
FieldDescriptor valueField = descriptor.findFieldByName("value");
if (typeUrlField == null
|| valueField == null
|| typeUrlField.getType() != FieldDescriptor.Type.STRING
|| valueField.getType() != FieldDescriptor.Type.BYTES) {
throw new InvalidProtocolBufferException("Invalid Any type.");
}
if (!(json instanceof JsonObject)) {
throw new InvalidProtocolBufferException("Expect message object but got: " + json);
}
JsonObject object = (JsonObject) json;
if (object.entrySet().isEmpty()) {
return;
}
JsonElement typeUrlElement = object.get("@type");
if (typeUrlElement == null) {
throw new InvalidProtocolBufferException("Missing type url when parsing: " + json);
}
String typeUrl = typeUrlElement.getAsString();
Descriptor contentType = registry.getDescriptorForTypeUrl(typeUrl);
if (contentType == null) {
contentType = oldRegistry.getDescriptorForTypeUrl(typeUrl);
if (contentType == null) {
throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
}
}
builder.setField(typeUrlField, typeUrl);
Message.Builder contentBuilder =
DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
WellKnownTypeParser specialParser = wellKnownTypeParsers.get(contentType.getFullName());
if (specialParser != null) {
JsonElement value = object.get("value");
if (value != null) {
specialParser.merge(this, value, contentBuilder);
}
} else {
mergeMessage(json, contentBuilder, true);
}
builder.setField(valueField, contentBuilder.build().toByteString());
}
private void mergeFieldMask(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
FieldMask value = FieldMaskUtil.fromJsonString(json.getAsString());
builder.mergeFrom(value.toByteString());
}
private void mergeTimestamp(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
try {
Timestamp value = Timestamps.parse(json.getAsString());
builder.mergeFrom(value.toByteString());
} catch (ParseException e) {
throw new InvalidProtocolBufferException("Failed to parse timestamp: " + json);
}
}
private void mergeDuration(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
try {
Duration value = Durations.parse(json.getAsString());
builder.mergeFrom(value.toByteString());
} catch (ParseException e) {
throw new InvalidProtocolBufferException("Failed to parse duration: " + json);
}
}
private void mergeStruct(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor descriptor = builder.getDescriptorForType();
FieldDescriptor field = descriptor.findFieldByName("fields");
if (field == null) {
throw new InvalidProtocolBufferException("Invalid Struct type.");
}
mergeMapField(field, json, builder);
}
private void mergeListValue(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor descriptor = builder.getDescriptorForType();
FieldDescriptor field = descriptor.findFieldByName("values");
if (field == null) {
throw new InvalidProtocolBufferException("Invalid ListValue type.");
}
mergeRepeatedField(field, json, builder);
}
private void mergeValue(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor type = builder.getDescriptorForType();
if (json instanceof JsonPrimitive) {
JsonPrimitive primitive = (JsonPrimitive) json;
if (primitive.isBoolean()) {
builder.setField(type.findFieldByName("bool_value"), primitive.getAsBoolean());
} else if (primitive.isNumber()) {
builder.setField(type.findFieldByName("number_value"), primitive.getAsDouble());
} else {
builder.setField(type.findFieldByName("string_value"), primitive.getAsString());
}
} else if (json instanceof JsonObject) {
FieldDescriptor field = type.findFieldByName("struct_value");
Message.Builder structBuilder = builder.newBuilderForField(field);
merge(json, structBuilder);
builder.setField(field, structBuilder.build());
} else if (json instanceof JsonArray) {
FieldDescriptor field = type.findFieldByName("list_value");
Message.Builder listBuilder = builder.newBuilderForField(field);
merge(json, listBuilder);
builder.setField(field, listBuilder.build());
} else if (json instanceof JsonNull) {
builder.setField(
type.findFieldByName("null_value"), NullValue.NULL_VALUE.getValueDescriptor());
} else {
throw new IllegalStateException("Unexpected json data: " + json);
}
}
private void mergeWrapper(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor type = builder.getDescriptorForType();
FieldDescriptor field = type.findFieldByName("value");
if (field == null) {
throw new InvalidProtocolBufferException("Invalid wrapper type: " + type.getFullName());
}
builder.setField(field, parseFieldValue(field, json, builder));
}
private void mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
if (field.isRepeated()) {
if (builder.getRepeatedFieldCount(field) > 0) {
throw new InvalidProtocolBufferException(
"Field " + field.getFullName() + " has already been set.");
}
} else {
if (builder.hasField(field)) {
throw new InvalidProtocolBufferException(
"Field " + field.getFullName() + " has already been set.");
}
}
if (field.isRepeated() && json instanceof JsonNull) {
return;
}
if (field.isMapField()) {
mergeMapField(field, json, builder);
} else if (field.isRepeated()) {
mergeRepeatedField(field, json, builder);
} else if (field.getContainingOneof() != null) {
mergeOneofField(field, json, builder);
} else {
Object value = parseFieldValue(field, json, builder);
if (value != null) {
builder.setField(field, value);
}
}
}
private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
if (!(json instanceof JsonObject)) {
throw new InvalidProtocolBufferException("Expect a map object but found: " + json);
}
Descriptor type = field.getMessageType();
FieldDescriptor keyField = type.findFieldByName("key");
FieldDescriptor valueField = type.findFieldByName("value");
if (keyField == null || valueField == null) {
throw new InvalidProtocolBufferException("Invalid map field: " + field.getFullName());
}
JsonObject object = (JsonObject) json;
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
Message.Builder entryBuilder = builder.newBuilderForField(field);
Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
if (value == null) {
if (ignoringUnknownFields && valueField.getType() == Type.ENUM) {
continue;
} else {
throw new InvalidProtocolBufferException("Map value cannot be null.");
}
}
entryBuilder.setField(keyField, key);
entryBuilder.setField(valueField, value);
builder.addRepeatedField(field, entryBuilder.build());
}
}
private void mergeOneofField(FieldDescriptor field, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Object value = parseFieldValue(field, json, builder);
if (value == null) {
return;
}
if (builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
throw new InvalidProtocolBufferException(
"Cannot set field "
+ field.getFullName()
+ " because another field "
+ builder.getOneofFieldDescriptor(field.getContainingOneof()).getFullName()
+ " belonging to the same oneof has already been set ");
}
builder.setField(field, value);
}
private void mergeRepeatedField(
FieldDescriptor field, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
if (!(json instanceof JsonArray)) {
throw new InvalidProtocolBufferException("Expect an array but found: " + json);
}
JsonArray array = (JsonArray) json;
for (int i = 0; i < array.size(); ++i) {
Object value = parseFieldValue(field, array.get(i), builder);
if (value == null) {
if (ignoringUnknownFields && field.getType() == Type.ENUM) {
continue;
} else {
throw new InvalidProtocolBufferException(
"Repeated field elements cannot be null in field: " + field.getFullName());
}
}
builder.addRepeatedField(field, value);
}
}
private int parseInt32(JsonElement json) throws InvalidProtocolBufferException {
try {
return Integer.parseInt(json.getAsString());
} catch (Exception e) {
}
try {
BigDecimal value = new BigDecimal(json.getAsString());
return value.intValueExact();
} catch (Exception e) {
throw new InvalidProtocolBufferException("Not an int32 value: " + json);
}
}
private long parseInt64(JsonElement json) throws InvalidProtocolBufferException {
try {
return Long.parseLong(json.getAsString());
} catch (Exception e) {
}
try {
BigDecimal value = new BigDecimal(json.getAsString());
return value.longValueExact();
} catch (Exception e) {
throw new InvalidProtocolBufferException("Not an int64 value: " + json);
}
}
private int parseUint32(JsonElement json) throws InvalidProtocolBufferException {
try {
long result = Long.parseLong(json.getAsString());
if (result < 0 || result > 0xFFFFFFFFL) {
throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
}
return (int) result;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
}
try {
BigDecimal decimalValue = new BigDecimal(json.getAsString());
BigInteger value = decimalValue.toBigIntegerExact();
if (value.signum() < 0 || value.compareTo(new BigInteger("FFFFFFFF", 16)) > 0) {
throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
}
return value.intValue();
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
}
}
private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
try {
BigDecimal decimalValue = new BigDecimal(json.getAsString());
BigInteger value = decimalValue.toBigIntegerExact();
if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
}
return value.longValue();
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
}
}
private boolean parseBool(JsonElement json) throws InvalidProtocolBufferException {
if (json.getAsString().equals("true")) {
return true;
}
if (json.getAsString().equals("false")) {
return false;
}
throw new InvalidProtocolBufferException("Invalid bool value: " + json);
}
private static final double EPSILON = 1e-6;
private float parseFloat(JsonElement json) throws InvalidProtocolBufferException {
if (json.getAsString().equals("NaN")) {
return Float.NaN;
} else if (json.getAsString().equals("Infinity")) {
return Float.POSITIVE_INFINITY;
} else if (json.getAsString().equals("-Infinity")) {
return Float.NEGATIVE_INFINITY;
}
try {
double value = Double.parseDouble(json.getAsString());
if (value > Float.MAX_VALUE * (1.0 + EPSILON)
|| value < -Float.MAX_VALUE * (1.0 + EPSILON)) {
throw new InvalidProtocolBufferException("Out of range float value: " + json);
}
return (float) value;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
throw new InvalidProtocolBufferException("Not a float value: " + json);
}
}
private static final BigDecimal MORE_THAN_ONE = new BigDecimal(String.valueOf(1.0 + EPSILON));
private static final BigDecimal MAX_DOUBLE =
new BigDecimal(String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
private static final BigDecimal MIN_DOUBLE =
new BigDecimal(String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
private double parseDouble(JsonElement json) throws InvalidProtocolBufferException {
if (json.getAsString().equals("NaN")) {
return Double.NaN;
} else if (json.getAsString().equals("Infinity")) {
return Double.POSITIVE_INFINITY;
} else if (json.getAsString().equals("-Infinity")) {
return Double.NEGATIVE_INFINITY;
}
try {
BigDecimal value = new BigDecimal(json.getAsString());
if (value.compareTo(MAX_DOUBLE) > 0 || value.compareTo(MIN_DOUBLE) < 0) {
throw new InvalidProtocolBufferException("Out of range double value: " + json);
}
return value.doubleValue();
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
throw new InvalidProtocolBufferException("Not an double value: " + json);
}
}
private String parseString(JsonElement json) {
return json.getAsString();
}
private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
try {
return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
} catch (IllegalArgumentException e) {
return ByteString.copyFrom(BaseEncoding.base64Url().decode(json.getAsString()));
}
}
private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
throws InvalidProtocolBufferException {
String value = json.getAsString();
EnumValueDescriptor result = enumDescriptor.findValueByName(value);
if (result == null) {
try {
int numericValue = parseInt32(json);
if (enumDescriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
result = enumDescriptor.findValueByNumberCreatingIfUnknown(numericValue);
} else {
result = enumDescriptor.findValueByNumber(numericValue);
}
} catch (InvalidProtocolBufferException e) {
}
if (result == null && !ignoringUnknownFields) {
throw new InvalidProtocolBufferException(
"Invalid enum value: " + value + " for enum type: " + enumDescriptor.getFullName());
}
}
return result;
}
private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
if (json instanceof JsonNull) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
&& field.getMessageType().getFullName().equals(Value.getDescriptor().getFullName())) {
Value value = Value.newBuilder().setNullValueValue(0).build();
return builder.newBuilderForField(field).mergeFrom(value.toByteString()).build();
} else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM
&& field.getEnumType().getFullName().equals(NullValue.getDescriptor().getFullName())) {
return field.getEnumType().findValueByNumber(0);
}
return null;
}
switch (field.getType()) {
case INT32:
case SINT32:
case SFIXED32:
return parseInt32(json);
case INT64:
case SINT64:
case SFIXED64:
return parseInt64(json);
case BOOL:
return parseBool(json);
case FLOAT:
return parseFloat(json);
case DOUBLE:
return parseDouble(json);
case UINT32:
case FIXED32:
return parseUint32(json);
case UINT64:
case FIXED64:
return parseUint64(json);
case STRING:
return parseString(json);
case BYTES:
return parseBytes(json);
case ENUM:
return parseEnum(field.getEnumType(), json);
case MESSAGE:
case GROUP:
if (currentDepth >= recursionLimit) {
throw new InvalidProtocolBufferException("Hit recursion limit.");
}
++currentDepth;
Message.Builder subBuilder = builder.newBuilderForField(field);
merge(json, subBuilder);
--currentDepth;
return subBuilder.build();
default:
throw new InvalidProtocolBufferException("Invalid field type: " + field.getType());
}
}
}
}