• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf.util;
32 
33 import com.google.common.base.Preconditions;
34 import com.google.common.io.BaseEncoding;
35 import com.google.errorprone.annotations.CanIgnoreReturnValue;
36 import com.google.gson.Gson;
37 import com.google.gson.GsonBuilder;
38 import com.google.gson.JsonArray;
39 import com.google.gson.JsonElement;
40 import com.google.gson.JsonIOException;
41 import com.google.gson.JsonNull;
42 import com.google.gson.JsonObject;
43 import com.google.gson.JsonParser;
44 import com.google.gson.JsonPrimitive;
45 import com.google.gson.stream.JsonReader;
46 import com.google.protobuf.Any;
47 import com.google.protobuf.BoolValue;
48 import com.google.protobuf.ByteString;
49 import com.google.protobuf.BytesValue;
50 import com.google.protobuf.Descriptors.Descriptor;
51 import com.google.protobuf.Descriptors.EnumDescriptor;
52 import com.google.protobuf.Descriptors.EnumValueDescriptor;
53 import com.google.protobuf.Descriptors.FieldDescriptor;
54 import com.google.protobuf.Descriptors.FieldDescriptor.Type;
55 import com.google.protobuf.Descriptors.FileDescriptor;
56 import com.google.protobuf.Descriptors.OneofDescriptor;
57 import com.google.protobuf.DoubleValue;
58 import com.google.protobuf.Duration;
59 import com.google.protobuf.DynamicMessage;
60 import com.google.protobuf.FieldMask;
61 import com.google.protobuf.FloatValue;
62 import com.google.protobuf.Int32Value;
63 import com.google.protobuf.Int64Value;
64 import com.google.protobuf.InvalidProtocolBufferException;
65 import com.google.protobuf.ListValue;
66 import com.google.protobuf.Message;
67 import com.google.protobuf.MessageOrBuilder;
68 import com.google.protobuf.NullValue;
69 import com.google.protobuf.StringValue;
70 import com.google.protobuf.Struct;
71 import com.google.protobuf.Timestamp;
72 import com.google.protobuf.UInt32Value;
73 import com.google.protobuf.UInt64Value;
74 import com.google.protobuf.Value;
75 import java.io.IOException;
76 import java.io.Reader;
77 import java.io.StringReader;
78 import java.math.BigDecimal;
79 import java.math.BigInteger;
80 import java.text.ParseException;
81 import java.util.Collection;
82 import java.util.Collections;
83 import java.util.Comparator;
84 import java.util.HashMap;
85 import java.util.HashSet;
86 import java.util.List;
87 import java.util.Map;
88 import java.util.Set;
89 import java.util.TreeMap;
90 import java.util.logging.Logger;
91 
92 /**
93  * Utility classes to convert protobuf messages to/from JSON format. The JSON
94  * format follows Proto3 JSON specification and only proto3 features are
95  * supported. Proto2 only features (e.g., extensions and unknown fields) will
96  * be discarded in the conversion. That is, when converting proto2 messages
97  * to JSON format, extensions and unknown fields will be treated as if they
98  * do not exist. This applies to proto2 messages embedded in proto3 messages
99  * as well.
100  */
101 public class JsonFormat {
102   private static final Logger logger = Logger.getLogger(JsonFormat.class.getName());
103 
JsonFormat()104   private JsonFormat() {}
105 
106   /**
107    * Creates a {@link Printer} with default configurations.
108    */
printer()109   public static Printer printer() {
110     return new Printer(
111         com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
112         TypeRegistry.getEmptyTypeRegistry(),
113         /* alwaysOutputDefaultValueFields */ false,
114         /* includingDefaultValueFields */ Collections.<FieldDescriptor>emptySet(),
115         /* preservingProtoFieldNames */ false,
116         /* omittingInsignificantWhitespace */ false,
117         /* printingEnumsAsInts */ false,
118         /* sortingMapKeys */ false);
119   }
120 
121   /**
122    * A Printer converts protobuf message to JSON format.
123    */
124   public static class Printer {
125     private final com.google.protobuf.TypeRegistry registry;
126     private final TypeRegistry oldRegistry;
127     // NOTE: There are 3 states for these *defaultValueFields variables:
128     // 1) Default - alwaysOutput is false & including is empty set. Fields only output if they are
129     //    set to non-default values.
130     // 2) No-args includingDefaultValueFields() called - alwaysOutput is true & including is
131     //    irrelevant (but set to empty set). All fields are output regardless of their values.
132     // 3) includingDefaultValueFields(Set<FieldDescriptor>) called - alwaysOutput is false &
133     //    including is set to the specified set. Fields in that set are always output & fields not
134     //    in that set are only output if set to non-default values.
135     private boolean alwaysOutputDefaultValueFields;
136     private Set<FieldDescriptor> includingDefaultValueFields;
137     private final boolean preservingProtoFieldNames;
138     private final boolean omittingInsignificantWhitespace;
139     private final boolean printingEnumsAsInts;
140     private final boolean sortingMapKeys;
141 
Printer( com.google.protobuf.TypeRegistry registry, TypeRegistry oldRegistry, boolean alwaysOutputDefaultValueFields, Set<FieldDescriptor> includingDefaultValueFields, boolean preservingProtoFieldNames, boolean omittingInsignificantWhitespace, boolean printingEnumsAsInts, boolean sortingMapKeys)142     private Printer(
143         com.google.protobuf.TypeRegistry registry,
144         TypeRegistry oldRegistry,
145         boolean alwaysOutputDefaultValueFields,
146         Set<FieldDescriptor> includingDefaultValueFields,
147         boolean preservingProtoFieldNames,
148         boolean omittingInsignificantWhitespace,
149         boolean printingEnumsAsInts,
150         boolean sortingMapKeys) {
151       this.registry = registry;
152       this.oldRegistry = oldRegistry;
153       this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
154       this.includingDefaultValueFields = includingDefaultValueFields;
155       this.preservingProtoFieldNames = preservingProtoFieldNames;
156       this.omittingInsignificantWhitespace = omittingInsignificantWhitespace;
157       this.printingEnumsAsInts = printingEnumsAsInts;
158       this.sortingMapKeys = sortingMapKeys;
159     }
160 
161     /**
162      * Creates a new {@link Printer} using the given registry. The new Printer clones all other
163      * configurations from the current {@link Printer}.
164      *
165      * @throws IllegalArgumentException if a registry is already set.
166      */
usingTypeRegistry(TypeRegistry oldRegistry)167     public Printer usingTypeRegistry(TypeRegistry oldRegistry) {
168       if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
169           || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
170         throw new IllegalArgumentException("Only one registry is allowed.");
171       }
172       return new Printer(
173           com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
174           oldRegistry,
175           alwaysOutputDefaultValueFields,
176           includingDefaultValueFields,
177           preservingProtoFieldNames,
178           omittingInsignificantWhitespace,
179           printingEnumsAsInts,
180           sortingMapKeys);
181     }
182 
183     /**
184      * Creates a new {@link Printer} using the given registry. The new Printer clones all other
185      * configurations from the current {@link Printer}.
186      *
187      * @throws IllegalArgumentException if a registry is already set.
188      */
usingTypeRegistry(com.google.protobuf.TypeRegistry registry)189     public Printer usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
190       if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
191           || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
192         throw new IllegalArgumentException("Only one registry is allowed.");
193       }
194       return new Printer(
195           registry,
196           oldRegistry,
197           alwaysOutputDefaultValueFields,
198           includingDefaultValueFields,
199           preservingProtoFieldNames,
200           omittingInsignificantWhitespace,
201           printingEnumsAsInts,
202           sortingMapKeys);
203     }
204 
205     /**
206      * Creates a new {@link Printer} that will also print fields set to their
207      * defaults. Empty repeated fields and map fields will be printed as well.
208      * The new Printer clones all other configurations from the current
209      * {@link Printer}.
210      */
includingDefaultValueFields()211     public Printer includingDefaultValueFields() {
212       checkUnsetIncludingDefaultValueFields();
213       return new Printer(
214           registry,
215           oldRegistry,
216           true,
217           Collections.<FieldDescriptor>emptySet(),
218           preservingProtoFieldNames,
219           omittingInsignificantWhitespace,
220           printingEnumsAsInts,
221           sortingMapKeys);
222     }
223 
224     /**
225      * Creates a new {@link Printer} that will print enum field values as integers instead of as
226      * string.
227      * The new Printer clones all other configurations from the current
228      * {@link Printer}.
229      */
printingEnumsAsInts()230     public Printer printingEnumsAsInts() {
231       checkUnsetPrintingEnumsAsInts();
232       return new Printer(
233           registry,
234           oldRegistry,
235           alwaysOutputDefaultValueFields,
236           includingDefaultValueFields,
237           preservingProtoFieldNames,
238           omittingInsignificantWhitespace,
239           true,
240           sortingMapKeys);
241     }
242 
checkUnsetPrintingEnumsAsInts()243     private void checkUnsetPrintingEnumsAsInts() {
244       if (printingEnumsAsInts) {
245         throw new IllegalStateException("JsonFormat printingEnumsAsInts has already been set.");
246       }
247     }
248 
249     /**
250      * Creates a new {@link Printer} that will also print default-valued fields if their
251      * FieldDescriptors are found in the supplied set. Empty repeated fields and map fields will be
252      * printed as well, if they match. The new Printer clones all other configurations from the
253      * current {@link Printer}. Call includingDefaultValueFields() with no args to unconditionally
254      * output all fields.
255      */
includingDefaultValueFields(Set<FieldDescriptor> fieldsToAlwaysOutput)256     public Printer includingDefaultValueFields(Set<FieldDescriptor> fieldsToAlwaysOutput) {
257       Preconditions.checkArgument(
258           null != fieldsToAlwaysOutput && !fieldsToAlwaysOutput.isEmpty(),
259           "Non-empty Set must be supplied for includingDefaultValueFields.");
260 
261       checkUnsetIncludingDefaultValueFields();
262       return new Printer(
263           registry,
264           oldRegistry,
265           false,
266           Collections.unmodifiableSet(new HashSet<>(fieldsToAlwaysOutput)),
267           preservingProtoFieldNames,
268           omittingInsignificantWhitespace,
269           printingEnumsAsInts,
270           sortingMapKeys);
271     }
272 
checkUnsetIncludingDefaultValueFields()273     private void checkUnsetIncludingDefaultValueFields() {
274       if (alwaysOutputDefaultValueFields || !includingDefaultValueFields.isEmpty()) {
275         throw new IllegalStateException(
276             "JsonFormat includingDefaultValueFields has already been set.");
277       }
278     }
279 
280     /**
281      * Creates a new {@link Printer} that is configured to use the original proto
282      * field names as defined in the .proto file rather than converting them to
283      * lowerCamelCase. The new Printer clones all other configurations from the
284      * current {@link Printer}.
285      */
preservingProtoFieldNames()286     public Printer preservingProtoFieldNames() {
287       return new Printer(
288           registry,
289           oldRegistry,
290           alwaysOutputDefaultValueFields,
291           includingDefaultValueFields,
292           true,
293           omittingInsignificantWhitespace,
294           printingEnumsAsInts,
295           sortingMapKeys);
296     }
297 
298 
299     /**
300      * Create a new {@link Printer} that will omit all insignificant whitespace in the JSON output.
301      * This new Printer clones all other configurations from the current Printer. Insignificant
302      * whitespace is defined by the JSON spec as whitespace that appear between JSON structural
303      * elements:
304      *
305      * <pre>
306      * ws = *(
307      * %x20 /              ; Space
308      * %x09 /              ; Horizontal tab
309      * %x0A /              ; Line feed or New line
310      * %x0D )              ; Carriage return
311      * </pre>
312      *
313      * See <a href="https://tools.ietf.org/html/rfc7159">https://tools.ietf.org/html/rfc7159</a>
314      * current {@link Printer}.
315      */
omittingInsignificantWhitespace()316     public Printer omittingInsignificantWhitespace() {
317       return new Printer(
318           registry,
319           oldRegistry,
320           alwaysOutputDefaultValueFields,
321           includingDefaultValueFields,
322           preservingProtoFieldNames,
323           true,
324           printingEnumsAsInts,
325           sortingMapKeys);
326     }
327 
328     /**
329      * Create a new {@link Printer} that will sort the map keys in the JSON output.
330      *
331      * <p>Use of this modifier is discouraged, the generated JSON messages are equivalent with and
332      * without this option set, but there are some corner use cases that demand a stable output,
333      * while order of map keys is otherwise arbitrary.
334      *
335      * <p>The generated order is not well-defined and should not be depended on, but it's stable.
336      *
337      * <p>This new Printer clones all other configurations from the current {@link Printer}.
338      */
sortingMapKeys()339     public Printer sortingMapKeys() {
340       return new Printer(
341           registry,
342           oldRegistry,
343           alwaysOutputDefaultValueFields,
344           includingDefaultValueFields,
345           preservingProtoFieldNames,
346           omittingInsignificantWhitespace,
347           printingEnumsAsInts,
348           true);
349     }
350 
351     /**
352      * Converts a protobuf message to JSON format.
353      *
354      * @throws InvalidProtocolBufferException if the message contains Any types that can't be
355      *     resolved.
356      * @throws IOException if writing to the output fails.
357      */
appendTo(MessageOrBuilder message, Appendable output)358     public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
359       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
360       // mobile.
361       new PrinterImpl(
362               registry,
363               oldRegistry,
364               alwaysOutputDefaultValueFields,
365               includingDefaultValueFields,
366               preservingProtoFieldNames,
367               output,
368               omittingInsignificantWhitespace,
369               printingEnumsAsInts,
370               sortingMapKeys)
371           .print(message);
372     }
373 
374     /**
375      * Converts a protobuf message to JSON format. Throws exceptions if there
376      * are unknown Any types in the message.
377      */
print(MessageOrBuilder message)378     public String print(MessageOrBuilder message) throws InvalidProtocolBufferException {
379       try {
380         StringBuilder builder = new StringBuilder();
381         appendTo(message, builder);
382         return builder.toString();
383       } catch (InvalidProtocolBufferException e) {
384         throw e;
385       } catch (IOException e) {
386         // Unexpected IOException.
387         throw new IllegalStateException(e);
388       }
389     }
390   }
391 
392   /**
393    * Creates a {@link Parser} with default configuration.
394    */
parser()395   public static Parser parser() {
396     return new Parser(
397         com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
398         TypeRegistry.getEmptyTypeRegistry(),
399         false,
400         Parser.DEFAULT_RECURSION_LIMIT);
401   }
402 
403   /**
404    * A Parser parses JSON to protobuf message.
405    */
406   public static class Parser {
407     private final com.google.protobuf.TypeRegistry registry;
408     private final TypeRegistry oldRegistry;
409     private final boolean ignoringUnknownFields;
410     private final int recursionLimit;
411 
412     // The default parsing recursion limit is aligned with the proto binary parser.
413     private static final int DEFAULT_RECURSION_LIMIT = 100;
414 
Parser( com.google.protobuf.TypeRegistry registry, TypeRegistry oldRegistry, boolean ignoreUnknownFields, int recursionLimit)415     private Parser(
416         com.google.protobuf.TypeRegistry registry,
417         TypeRegistry oldRegistry,
418         boolean ignoreUnknownFields,
419         int recursionLimit) {
420       this.registry = registry;
421       this.oldRegistry = oldRegistry;
422       this.ignoringUnknownFields = ignoreUnknownFields;
423       this.recursionLimit = recursionLimit;
424     }
425 
426     /**
427      * Creates a new {@link Parser} using the given registry. The new Parser clones all other
428      * configurations from this Parser.
429      *
430      * @throws IllegalArgumentException if a registry is already set.
431      */
usingTypeRegistry(TypeRegistry oldRegistry)432     public Parser usingTypeRegistry(TypeRegistry oldRegistry) {
433       if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
434           || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
435         throw new IllegalArgumentException("Only one registry is allowed.");
436       }
437       return new Parser(
438           com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
439           oldRegistry,
440           ignoringUnknownFields,
441           recursionLimit);
442     }
443 
444     /**
445      * Creates a new {@link Parser} using the given registry. The new Parser clones all other
446      * configurations from this Parser.
447      *
448      * @throws IllegalArgumentException if a registry is already set.
449      */
usingTypeRegistry(com.google.protobuf.TypeRegistry registry)450     public Parser usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
451       if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
452           || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
453         throw new IllegalArgumentException("Only one registry is allowed.");
454       }
455       return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
456     }
457 
458     /**
459      * Creates a new {@link Parser} configured to not throw an exception when an unknown field is
460      * encountered. The new Parser clones all other configurations from this Parser.
461      */
ignoringUnknownFields()462     public Parser ignoringUnknownFields() {
463       return new Parser(this.registry, oldRegistry, true, recursionLimit);
464     }
465 
466     /**
467      * Parses from JSON into a protobuf message.
468      *
469      * @throws InvalidProtocolBufferException if the input is not valid JSON
470      *         format or there are unknown fields in the input.
471      */
merge(String json, Message.Builder builder)472     public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
473       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
474       // mobile.
475       new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
476           .merge(json, builder);
477     }
478 
479     /**
480      * Parses from JSON into a protobuf message.
481      *
482      * @throws InvalidProtocolBufferException if the input is not valid JSON
483      *         format or there are unknown fields in the input.
484      * @throws IOException if reading from the input throws.
485      */
merge(Reader json, Message.Builder builder)486     public void merge(Reader json, Message.Builder builder) throws IOException {
487       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
488       // mobile.
489       new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
490           .merge(json, builder);
491     }
492 
493     // For testing only.
usingRecursionLimit(int recursionLimit)494     Parser usingRecursionLimit(int recursionLimit) {
495       return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
496     }
497   }
498 
499   /**
500    * A TypeRegistry is used to resolve Any messages in the JSON conversion.
501    * You must provide a TypeRegistry containing all message types used in
502    * Any message fields, or the JSON conversion will fail because data
503    * in Any message fields is unrecognizable. You don't need to supply a
504    * TypeRegistry if you don't use Any message fields.
505    */
506   public static class TypeRegistry {
507     private static class EmptyTypeRegistryHolder {
508       private static final TypeRegistry EMPTY =
509           new TypeRegistry(Collections.<String, Descriptor>emptyMap());
510     }
511 
getEmptyTypeRegistry()512     public static TypeRegistry getEmptyTypeRegistry() {
513       return EmptyTypeRegistryHolder.EMPTY;
514     }
515 
newBuilder()516     public static Builder newBuilder() {
517       return new Builder();
518     }
519 
520     /**
521      * Find a type by its full name. Returns null if it cannot be found in this {@link
522      * TypeRegistry}.
523      */
find(String name)524     public Descriptor find(String name) {
525       return types.get(name);
526     }
527 
getDescriptorForTypeUrl(String typeUrl)528     Descriptor getDescriptorForTypeUrl(String typeUrl) throws InvalidProtocolBufferException {
529       return find(getTypeName(typeUrl));
530     }
531 
532     private final Map<String, Descriptor> types;
533 
TypeRegistry(Map<String, Descriptor> types)534     private TypeRegistry(Map<String, Descriptor> types) {
535       this.types = types;
536     }
537 
538 
539     /** A Builder is used to build {@link TypeRegistry}. */
540     public static class Builder {
Builder()541       private Builder() {}
542 
543       /**
544        * Adds a message type and all types defined in the same .proto file as well as all
545        * transitively imported .proto files to this {@link Builder}.
546        */
547       @CanIgnoreReturnValue
add(Descriptor messageType)548       public Builder add(Descriptor messageType) {
549         if (types == null) {
550           throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
551         }
552         addFile(messageType.getFile());
553         return this;
554       }
555 
556       /**
557        * Adds message types and all types defined in the same .proto file as well as all
558        * transitively imported .proto files to this {@link Builder}.
559        */
560       @CanIgnoreReturnValue
add(Iterable<Descriptor> messageTypes)561       public Builder add(Iterable<Descriptor> messageTypes) {
562         if (types == null) {
563           throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
564         }
565         for (Descriptor type : messageTypes) {
566           addFile(type.getFile());
567         }
568         return this;
569       }
570 
571       /**
572        * Builds a {@link TypeRegistry}. This method can only be called once for
573        * one Builder.
574        */
build()575       public TypeRegistry build() {
576         TypeRegistry result = new TypeRegistry(types);
577         // Make sure the built {@link TypeRegistry} is immutable.
578         types = null;
579         return result;
580       }
581 
addFile(FileDescriptor file)582       private void addFile(FileDescriptor file) {
583         // Skip the file if it's already added.
584         if (!files.add(file.getFullName())) {
585           return;
586         }
587         for (FileDescriptor dependency : file.getDependencies()) {
588           addFile(dependency);
589         }
590         for (Descriptor message : file.getMessageTypes()) {
591           addMessage(message);
592         }
593       }
594 
addMessage(Descriptor message)595       private void addMessage(Descriptor message) {
596         for (Descriptor nestedType : message.getNestedTypes()) {
597           addMessage(nestedType);
598         }
599 
600         if (types.containsKey(message.getFullName())) {
601           logger.warning("Type " + message.getFullName() + " is added multiple times.");
602           return;
603         }
604 
605         types.put(message.getFullName(), message);
606       }
607 
608       private final Set<String> files = new HashSet<String>();
609       private Map<String, Descriptor> types = new HashMap<String, Descriptor>();
610     }
611   }
612 
613   /**
614    * An interface for json formatting that can be used in
615    * combination with the omittingInsignificantWhitespace() method
616    */
617   interface TextGenerator {
indent()618     void indent();
619 
outdent()620     void outdent();
621 
print(final CharSequence text)622     void print(final CharSequence text) throws IOException;
623   }
624 
625   /**
626    * Format the json without indentation
627    */
628   private static final class CompactTextGenerator implements TextGenerator {
629     private final Appendable output;
630 
CompactTextGenerator(final Appendable output)631     private CompactTextGenerator(final Appendable output) {
632       this.output = output;
633     }
634 
635     /** ignored by compact printer */
636     @Override
indent()637     public void indent() {}
638 
639     /** ignored by compact printer */
640     @Override
outdent()641     public void outdent() {}
642 
643     /** Print text to the output stream. */
644     @Override
print(final CharSequence text)645     public void print(final CharSequence text) throws IOException {
646       output.append(text);
647     }
648   }
649   /**
650    * A TextGenerator adds indentation when writing formatted text.
651    */
652   private static final class PrettyTextGenerator implements TextGenerator {
653     private final Appendable output;
654     private final StringBuilder indent = new StringBuilder();
655     private boolean atStartOfLine = true;
656 
PrettyTextGenerator(final Appendable output)657     private PrettyTextGenerator(final Appendable output) {
658       this.output = output;
659     }
660 
661     /**
662      * Indent text by two spaces. After calling Indent(), two spaces will be inserted at the
663      * beginning of each line of text. Indent() may be called multiple times to produce deeper
664      * indents.
665      */
666     @Override
indent()667     public void indent() {
668       indent.append("  ");
669     }
670 
671     /** Reduces the current indent level by two spaces, or crashes if the indent level is zero. */
672     @Override
outdent()673     public void outdent() {
674       final int length = indent.length();
675       if (length < 2) {
676         throw new IllegalArgumentException(" Outdent() without matching Indent().");
677       }
678       indent.delete(length - 2, length);
679     }
680 
681     /** Print text to the output stream. */
682     @Override
print(final CharSequence text)683     public void print(final CharSequence text) throws IOException {
684       final int size = text.length();
685       int pos = 0;
686 
687       for (int i = 0; i < size; i++) {
688         if (text.charAt(i) == '\n') {
689           write(text.subSequence(pos, i + 1));
690           pos = i + 1;
691           atStartOfLine = true;
692         }
693       }
694       write(text.subSequence(pos, size));
695     }
696 
write(final CharSequence data)697     private void write(final CharSequence data) throws IOException {
698       if (data.length() == 0) {
699         return;
700       }
701       if (atStartOfLine) {
702         atStartOfLine = false;
703         output.append(indent);
704       }
705       output.append(data);
706     }
707   }
708 
709   /**
710    * A Printer converts protobuf messages to JSON format.
711    */
712   private static final class PrinterImpl {
713     private final com.google.protobuf.TypeRegistry registry;
714     private final TypeRegistry oldRegistry;
715     private final boolean alwaysOutputDefaultValueFields;
716     private final Set<FieldDescriptor> includingDefaultValueFields;
717     private final boolean preservingProtoFieldNames;
718     private final boolean printingEnumsAsInts;
719     private final boolean sortingMapKeys;
720     private final TextGenerator generator;
721     // We use Gson to help handle string escapes.
722     private final Gson gson;
723     private final CharSequence blankOrSpace;
724     private final CharSequence blankOrNewLine;
725 
726     private static class GsonHolder {
727       private static final Gson DEFAULT_GSON = new GsonBuilder().create();
728     }
729 
PrinterImpl( com.google.protobuf.TypeRegistry registry, TypeRegistry oldRegistry, boolean alwaysOutputDefaultValueFields, Set<FieldDescriptor> includingDefaultValueFields, boolean preservingProtoFieldNames, Appendable jsonOutput, boolean omittingInsignificantWhitespace, boolean printingEnumsAsInts, boolean sortingMapKeys)730     PrinterImpl(
731         com.google.protobuf.TypeRegistry registry,
732         TypeRegistry oldRegistry,
733         boolean alwaysOutputDefaultValueFields,
734         Set<FieldDescriptor> includingDefaultValueFields,
735         boolean preservingProtoFieldNames,
736         Appendable jsonOutput,
737         boolean omittingInsignificantWhitespace,
738         boolean printingEnumsAsInts,
739         boolean sortingMapKeys) {
740       this.registry = registry;
741       this.oldRegistry = oldRegistry;
742       this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
743       this.includingDefaultValueFields = includingDefaultValueFields;
744       this.preservingProtoFieldNames = preservingProtoFieldNames;
745       this.printingEnumsAsInts = printingEnumsAsInts;
746       this.sortingMapKeys = sortingMapKeys;
747       this.gson = GsonHolder.DEFAULT_GSON;
748       // json format related properties, determined by printerType
749       if (omittingInsignificantWhitespace) {
750         this.generator = new CompactTextGenerator(jsonOutput);
751         this.blankOrSpace = "";
752         this.blankOrNewLine = "";
753       } else {
754         this.generator = new PrettyTextGenerator(jsonOutput);
755         this.blankOrSpace = " ";
756         this.blankOrNewLine = "\n";
757       }
758     }
759 
print(MessageOrBuilder message)760     void print(MessageOrBuilder message) throws IOException {
761       WellKnownTypePrinter specialPrinter =
762           wellKnownTypePrinters.get(message.getDescriptorForType().getFullName());
763       if (specialPrinter != null) {
764         specialPrinter.print(this, message);
765         return;
766       }
767       print(message, null);
768     }
769 
770     private interface WellKnownTypePrinter {
print(PrinterImpl printer, MessageOrBuilder message)771       void print(PrinterImpl printer, MessageOrBuilder message) throws IOException;
772     }
773 
774     private static final Map<String, WellKnownTypePrinter> wellKnownTypePrinters =
775         buildWellKnownTypePrinters();
776 
buildWellKnownTypePrinters()777     private static Map<String, WellKnownTypePrinter> buildWellKnownTypePrinters() {
778       Map<String, WellKnownTypePrinter> printers = new HashMap<String, WellKnownTypePrinter>();
779       // Special-case Any.
780       printers.put(
781           Any.getDescriptor().getFullName(),
782           new WellKnownTypePrinter() {
783             @Override
784             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
785               printer.printAny(message);
786             }
787           });
788       // Special-case wrapper types.
789       WellKnownTypePrinter wrappersPrinter =
790           new WellKnownTypePrinter() {
791             @Override
792             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
793               printer.printWrapper(message);
794             }
795           };
796       printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
797       printers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
798       printers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
799       printers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter);
800       printers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter);
801       printers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter);
802       printers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter);
803       printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
804       printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
805       // Special-case Timestamp.
806       printers.put(
807           Timestamp.getDescriptor().getFullName(),
808           new WellKnownTypePrinter() {
809             @Override
810             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
811               printer.printTimestamp(message);
812             }
813           });
814       // Special-case Duration.
815       printers.put(
816           Duration.getDescriptor().getFullName(),
817           new WellKnownTypePrinter() {
818             @Override
819             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
820               printer.printDuration(message);
821             }
822           });
823       // Special-case FieldMask.
824       printers.put(
825           FieldMask.getDescriptor().getFullName(),
826           new WellKnownTypePrinter() {
827             @Override
828             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
829               printer.printFieldMask(message);
830             }
831           });
832       // Special-case Struct.
833       printers.put(
834           Struct.getDescriptor().getFullName(),
835           new WellKnownTypePrinter() {
836             @Override
837             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
838               printer.printStruct(message);
839             }
840           });
841       // Special-case Value.
842       printers.put(
843           Value.getDescriptor().getFullName(),
844           new WellKnownTypePrinter() {
845             @Override
846             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
847               printer.printValue(message);
848             }
849           });
850       // Special-case ListValue.
851       printers.put(
852           ListValue.getDescriptor().getFullName(),
853           new WellKnownTypePrinter() {
854             @Override
855             public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
856               printer.printListValue(message);
857             }
858           });
859       return printers;
860     }
861 
862     /** Prints google.protobuf.Any */
printAny(MessageOrBuilder message)863     private void printAny(MessageOrBuilder message) throws IOException {
864       if (Any.getDefaultInstance().equals(message)) {
865         generator.print("{}");
866         return;
867       }
868       Descriptor descriptor = message.getDescriptorForType();
869       FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
870       FieldDescriptor valueField = descriptor.findFieldByName("value");
871       // Validates type of the message. Note that we can't just cast the message
872       // to com.google.protobuf.Any because it might be a DynamicMessage.
873       if (typeUrlField == null
874           || valueField == null
875           || typeUrlField.getType() != FieldDescriptor.Type.STRING
876           || valueField.getType() != FieldDescriptor.Type.BYTES) {
877         throw new InvalidProtocolBufferException("Invalid Any type.");
878       }
879       String typeUrl = (String) message.getField(typeUrlField);
880       Descriptor type = registry.getDescriptorForTypeUrl(typeUrl);
881       if (type == null) {
882         type = oldRegistry.getDescriptorForTypeUrl(typeUrl);
883         if (type == null) {
884           throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
885         }
886       }
887       ByteString content = (ByteString) message.getField(valueField);
888       Message contentMessage =
889           DynamicMessage.getDefaultInstance(type).getParserForType().parseFrom(content);
890       WellKnownTypePrinter printer = wellKnownTypePrinters.get(getTypeName(typeUrl));
891       if (printer != null) {
892         // If the type is one of the well-known types, we use a special
893         // formatting.
894         generator.print("{" + blankOrNewLine);
895         generator.indent();
896         generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl) + "," + blankOrNewLine);
897         generator.print("\"value\":" + blankOrSpace);
898         printer.print(this, contentMessage);
899         generator.print(blankOrNewLine);
900         generator.outdent();
901         generator.print("}");
902       } else {
903         // Print the content message instead (with a "@type" field added).
904         print(contentMessage, typeUrl);
905       }
906     }
907 
908     /** Prints wrapper types (e.g., google.protobuf.Int32Value) */
printWrapper(MessageOrBuilder message)909     private void printWrapper(MessageOrBuilder message) throws IOException {
910       Descriptor descriptor = message.getDescriptorForType();
911       FieldDescriptor valueField = descriptor.findFieldByName("value");
912       if (valueField == null) {
913         throw new InvalidProtocolBufferException("Invalid Wrapper type.");
914       }
915       // When formatting wrapper types, we just print its value field instead of
916       // the whole message.
917       printSingleFieldValue(valueField, message.getField(valueField));
918     }
919 
toByteString(MessageOrBuilder message)920     private ByteString toByteString(MessageOrBuilder message) {
921       if (message instanceof Message) {
922         return ((Message) message).toByteString();
923       } else {
924         return ((Message.Builder) message).build().toByteString();
925       }
926     }
927 
928     /** Prints google.protobuf.Timestamp */
printTimestamp(MessageOrBuilder message)929     private void printTimestamp(MessageOrBuilder message) throws IOException {
930       Timestamp value = Timestamp.parseFrom(toByteString(message));
931       generator.print("\"" + Timestamps.toString(value) + "\"");
932     }
933 
934     /** Prints google.protobuf.Duration */
printDuration(MessageOrBuilder message)935     private void printDuration(MessageOrBuilder message) throws IOException {
936       Duration value = Duration.parseFrom(toByteString(message));
937       generator.print("\"" + Durations.toString(value) + "\"");
938     }
939 
940     /** Prints google.protobuf.FieldMask */
printFieldMask(MessageOrBuilder message)941     private void printFieldMask(MessageOrBuilder message) throws IOException {
942       FieldMask value = FieldMask.parseFrom(toByteString(message));
943       generator.print("\"" + FieldMaskUtil.toJsonString(value) + "\"");
944     }
945 
946     /** Prints google.protobuf.Struct */
printStruct(MessageOrBuilder message)947     private void printStruct(MessageOrBuilder message) throws IOException {
948       Descriptor descriptor = message.getDescriptorForType();
949       FieldDescriptor field = descriptor.findFieldByName("fields");
950       if (field == null) {
951         throw new InvalidProtocolBufferException("Invalid Struct type.");
952       }
953       // Struct is formatted as a map object.
954       printMapFieldValue(field, message.getField(field));
955     }
956 
957     /** Prints google.protobuf.Value */
printValue(MessageOrBuilder message)958     private void printValue(MessageOrBuilder message) throws IOException {
959       // For a Value message, only the value of the field is formatted.
960       Map<FieldDescriptor, Object> fields = message.getAllFields();
961       if (fields.isEmpty()) {
962         // No value set.
963         generator.print("null");
964         return;
965       }
966       // A Value message can only have at most one field set (it only contains
967       // an oneof).
968       if (fields.size() != 1) {
969         throw new InvalidProtocolBufferException("Invalid Value type.");
970       }
971       for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
972         printSingleFieldValue(entry.getKey(), entry.getValue());
973       }
974     }
975 
976     /** Prints google.protobuf.ListValue */
printListValue(MessageOrBuilder message)977     private void printListValue(MessageOrBuilder message) throws IOException {
978       Descriptor descriptor = message.getDescriptorForType();
979       FieldDescriptor field = descriptor.findFieldByName("values");
980       if (field == null) {
981         throw new InvalidProtocolBufferException("Invalid ListValue type.");
982       }
983       printRepeatedFieldValue(field, message.getField(field));
984     }
985 
986     /** Prints a regular message with an optional type URL. */
print(MessageOrBuilder message, String typeUrl)987     private void print(MessageOrBuilder message, String typeUrl) throws IOException {
988       generator.print("{" + blankOrNewLine);
989       generator.indent();
990 
991       boolean printedField = false;
992       if (typeUrl != null) {
993         generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl));
994         printedField = true;
995       }
996       Map<FieldDescriptor, Object> fieldsToPrint = null;
997       if (alwaysOutputDefaultValueFields || !includingDefaultValueFields.isEmpty()) {
998         fieldsToPrint = new TreeMap<FieldDescriptor, Object>(message.getAllFields());
999         for (FieldDescriptor field : message.getDescriptorForType().getFields()) {
1000           if (field.isOptional()) {
1001             if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
1002                 && !message.hasField(field)) {
1003               // Always skip empty optional message fields. If not we will recurse indefinitely if
1004               // a message has itself as a sub-field.
1005               continue;
1006             }
1007             OneofDescriptor oneof = field.getContainingOneof();
1008             if (oneof != null && !message.hasField(field)) {
1009               // Skip all oneof fields except the one that is actually set
1010               continue;
1011             }
1012           }
1013           if (!fieldsToPrint.containsKey(field)
1014               && (alwaysOutputDefaultValueFields || includingDefaultValueFields.contains(field))) {
1015             fieldsToPrint.put(field, message.getField(field));
1016           }
1017         }
1018       } else {
1019         fieldsToPrint = message.getAllFields();
1020       }
1021       for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
1022         if (printedField) {
1023           // Add line-endings for the previous field.
1024           generator.print("," + blankOrNewLine);
1025         } else {
1026           printedField = true;
1027         }
1028         printField(field.getKey(), field.getValue());
1029       }
1030 
1031       // Add line-endings for the last field.
1032       if (printedField) {
1033         generator.print(blankOrNewLine);
1034       }
1035       generator.outdent();
1036       generator.print("}");
1037     }
1038 
printField(FieldDescriptor field, Object value)1039     private void printField(FieldDescriptor field, Object value) throws IOException {
1040       if (preservingProtoFieldNames) {
1041         generator.print("\"" + field.getName() + "\":" + blankOrSpace);
1042       } else {
1043         generator.print("\"" + field.getJsonName() + "\":" + blankOrSpace);
1044       }
1045       if (field.isMapField()) {
1046         printMapFieldValue(field, value);
1047       } else if (field.isRepeated()) {
1048         printRepeatedFieldValue(field, value);
1049       } else {
1050         printSingleFieldValue(field, value);
1051       }
1052     }
1053 
1054     @SuppressWarnings("rawtypes")
printRepeatedFieldValue(FieldDescriptor field, Object value)1055     private void printRepeatedFieldValue(FieldDescriptor field, Object value) throws IOException {
1056       generator.print("[");
1057       boolean printedElement = false;
1058       for (Object element : (List) value) {
1059         if (printedElement) {
1060           generator.print("," + blankOrSpace);
1061         } else {
1062           printedElement = true;
1063         }
1064         printSingleFieldValue(field, element);
1065       }
1066       generator.print("]");
1067     }
1068 
1069     @SuppressWarnings("rawtypes")
printMapFieldValue(FieldDescriptor field, Object value)1070     private void printMapFieldValue(FieldDescriptor field, Object value) throws IOException {
1071       Descriptor type = field.getMessageType();
1072       FieldDescriptor keyField = type.findFieldByName("key");
1073       FieldDescriptor valueField = type.findFieldByName("value");
1074       if (keyField == null || valueField == null) {
1075         throw new InvalidProtocolBufferException("Invalid map field.");
1076       }
1077       generator.print("{" + blankOrNewLine);
1078       generator.indent();
1079 
1080       @SuppressWarnings("unchecked") // Object guaranteed to be a List for a map field.
1081       Collection<Object> elements = (List<Object>) value;
1082       if (sortingMapKeys && !elements.isEmpty()) {
1083         Comparator<Object> cmp = null;
1084         if (keyField.getType() == FieldDescriptor.Type.STRING) {
1085           cmp = new Comparator<Object>() {
1086             @Override
1087             public int compare(final Object o1, final Object o2) {
1088               ByteString s1 = ByteString.copyFromUtf8((String) o1);
1089               ByteString s2 = ByteString.copyFromUtf8((String) o2);
1090               return ByteString.unsignedLexicographicalComparator().compare(s1, s2);
1091             }
1092           };
1093         }
1094         TreeMap<Object, Object> tm = new TreeMap<Object, Object>(cmp);
1095         for (Object element : elements) {
1096           Message entry = (Message) element;
1097           Object entryKey = entry.getField(keyField);
1098           tm.put(entryKey, element);
1099         }
1100         elements = tm.values();
1101       }
1102 
1103       boolean printedElement = false;
1104       for (Object element : elements) {
1105         Message entry = (Message) element;
1106         Object entryKey = entry.getField(keyField);
1107         Object entryValue = entry.getField(valueField);
1108         if (printedElement) {
1109           generator.print("," + blankOrNewLine);
1110         } else {
1111           printedElement = true;
1112         }
1113         // Key fields are always double-quoted.
1114         printSingleFieldValue(keyField, entryKey, true);
1115         generator.print(":" + blankOrSpace);
1116         printSingleFieldValue(valueField, entryValue);
1117       }
1118       if (printedElement) {
1119         generator.print(blankOrNewLine);
1120       }
1121       generator.outdent();
1122       generator.print("}");
1123     }
1124 
printSingleFieldValue(FieldDescriptor field, Object value)1125     private void printSingleFieldValue(FieldDescriptor field, Object value) throws IOException {
1126       printSingleFieldValue(field, value, false);
1127     }
1128 
1129     /**
1130      * Prints a field's value in JSON format.
1131      *
1132      * @param alwaysWithQuotes whether to always add double-quotes to primitive
1133      *        types.
1134      */
printSingleFieldValue( final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)1135     private void printSingleFieldValue(
1136         final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)
1137         throws IOException {
1138       switch (field.getType()) {
1139         case INT32:
1140         case SINT32:
1141         case SFIXED32:
1142           if (alwaysWithQuotes) {
1143             generator.print("\"");
1144           }
1145           generator.print(((Integer) value).toString());
1146           if (alwaysWithQuotes) {
1147             generator.print("\"");
1148           }
1149           break;
1150 
1151         case INT64:
1152         case SINT64:
1153         case SFIXED64:
1154           generator.print("\"" + ((Long) value).toString() + "\"");
1155           break;
1156 
1157         case BOOL:
1158           if (alwaysWithQuotes) {
1159             generator.print("\"");
1160           }
1161           if (((Boolean) value).booleanValue()) {
1162             generator.print("true");
1163           } else {
1164             generator.print("false");
1165           }
1166           if (alwaysWithQuotes) {
1167             generator.print("\"");
1168           }
1169           break;
1170 
1171         case FLOAT:
1172           Float floatValue = (Float) value;
1173           if (floatValue.isNaN()) {
1174             generator.print("\"NaN\"");
1175           } else if (floatValue.isInfinite()) {
1176             if (floatValue < 0) {
1177               generator.print("\"-Infinity\"");
1178             } else {
1179               generator.print("\"Infinity\"");
1180             }
1181           } else {
1182             if (alwaysWithQuotes) {
1183               generator.print("\"");
1184             }
1185             generator.print(floatValue.toString());
1186             if (alwaysWithQuotes) {
1187               generator.print("\"");
1188             }
1189           }
1190           break;
1191 
1192         case DOUBLE:
1193           Double doubleValue = (Double) value;
1194           if (doubleValue.isNaN()) {
1195             generator.print("\"NaN\"");
1196           } else if (doubleValue.isInfinite()) {
1197             if (doubleValue < 0) {
1198               generator.print("\"-Infinity\"");
1199             } else {
1200               generator.print("\"Infinity\"");
1201             }
1202           } else {
1203             if (alwaysWithQuotes) {
1204               generator.print("\"");
1205             }
1206             generator.print(doubleValue.toString());
1207             if (alwaysWithQuotes) {
1208               generator.print("\"");
1209             }
1210           }
1211           break;
1212 
1213         case UINT32:
1214         case FIXED32:
1215           if (alwaysWithQuotes) {
1216             generator.print("\"");
1217           }
1218           generator.print(unsignedToString((Integer) value));
1219           if (alwaysWithQuotes) {
1220             generator.print("\"");
1221           }
1222           break;
1223 
1224         case UINT64:
1225         case FIXED64:
1226           generator.print("\"" + unsignedToString((Long) value) + "\"");
1227           break;
1228 
1229         case STRING:
1230           generator.print(gson.toJson(value));
1231           break;
1232 
1233         case BYTES:
1234           generator.print("\"");
1235           generator.print(BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
1236           generator.print("\"");
1237           break;
1238 
1239         case ENUM:
1240           // Special-case google.protobuf.NullValue (it's an Enum).
1241           if (field.getEnumType().getFullName().equals("google.protobuf.NullValue")) {
1242             // No matter what value it contains, we always print it as "null".
1243             if (alwaysWithQuotes) {
1244               generator.print("\"");
1245             }
1246             generator.print("null");
1247             if (alwaysWithQuotes) {
1248               generator.print("\"");
1249             }
1250           } else {
1251             if (printingEnumsAsInts || ((EnumValueDescriptor) value).getIndex() == -1) {
1252               generator.print(String.valueOf(((EnumValueDescriptor) value).getNumber()));
1253             } else {
1254               generator.print("\"" + ((EnumValueDescriptor) value).getName() + "\"");
1255             }
1256           }
1257           break;
1258 
1259         case MESSAGE:
1260         case GROUP:
1261           print((Message) value);
1262           break;
1263       }
1264     }
1265   }
1266 
1267   /** Convert an unsigned 32-bit integer to a string. */
unsignedToString(final int value)1268   private static String unsignedToString(final int value) {
1269     if (value >= 0) {
1270       return Integer.toString(value);
1271     } else {
1272       return Long.toString(value & 0x00000000FFFFFFFFL);
1273     }
1274   }
1275 
1276   /** Convert an unsigned 64-bit integer to a string. */
unsignedToString(final long value)1277   private static String unsignedToString(final long value) {
1278     if (value >= 0) {
1279       return Long.toString(value);
1280     } else {
1281       // Pull off the most-significant bit so that BigInteger doesn't think
1282       // the number is negative, then set it again using setBit().
1283       return BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1).toString();
1284     }
1285   }
1286 
getTypeName(String typeUrl)1287   private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
1288     String[] parts = typeUrl.split("/");
1289     if (parts.length == 1) {
1290       throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
1291     }
1292     return parts[parts.length - 1];
1293   }
1294 
1295   private static class ParserImpl {
1296     private final com.google.protobuf.TypeRegistry registry;
1297     private final TypeRegistry oldRegistry;
1298     private final JsonParser jsonParser;
1299     private final boolean ignoringUnknownFields;
1300     private final int recursionLimit;
1301     private int currentDepth;
1302 
ParserImpl( com.google.protobuf.TypeRegistry registry, TypeRegistry oldRegistry, boolean ignoreUnknownFields, int recursionLimit)1303     ParserImpl(
1304         com.google.protobuf.TypeRegistry registry,
1305         TypeRegistry oldRegistry,
1306         boolean ignoreUnknownFields,
1307         int recursionLimit) {
1308       this.registry = registry;
1309       this.oldRegistry = oldRegistry;
1310       this.ignoringUnknownFields = ignoreUnknownFields;
1311       this.jsonParser = new JsonParser();
1312       this.recursionLimit = recursionLimit;
1313       this.currentDepth = 0;
1314     }
1315 
merge(Reader json, Message.Builder builder)1316     void merge(Reader json, Message.Builder builder) throws IOException {
1317       try {
1318         JsonReader reader = new JsonReader(json);
1319         reader.setLenient(false);
1320         merge(jsonParser.parse(reader), builder);
1321       } catch (InvalidProtocolBufferException e) {
1322         throw e;
1323       } catch (JsonIOException e) {
1324         // Unwrap IOException.
1325         if (e.getCause() instanceof IOException) {
1326           throw (IOException) e.getCause();
1327         } else {
1328           throw new InvalidProtocolBufferException(e.getMessage());
1329         }
1330       } catch (Exception e) {
1331         // We convert all exceptions from JSON parsing to our own exceptions.
1332         throw new InvalidProtocolBufferException(e.getMessage());
1333       }
1334     }
1335 
merge(String json, Message.Builder builder)1336     void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
1337       try {
1338         JsonReader reader = new JsonReader(new StringReader(json));
1339         reader.setLenient(false);
1340         merge(jsonParser.parse(reader), builder);
1341       } catch (InvalidProtocolBufferException e) {
1342         throw e;
1343       } catch (Exception e) {
1344         // We convert all exceptions from JSON parsing to our own exceptions.
1345         throw new InvalidProtocolBufferException(e.getMessage());
1346       }
1347     }
1348 
1349     private interface WellKnownTypeParser {
merge(ParserImpl parser, JsonElement json, Message.Builder builder)1350       void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1351           throws InvalidProtocolBufferException;
1352     }
1353 
1354     private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers =
1355         buildWellKnownTypeParsers();
1356 
buildWellKnownTypeParsers()1357     private static Map<String, WellKnownTypeParser> buildWellKnownTypeParsers() {
1358       Map<String, WellKnownTypeParser> parsers = new HashMap<String, WellKnownTypeParser>();
1359       // Special-case Any.
1360       parsers.put(
1361           Any.getDescriptor().getFullName(),
1362           new WellKnownTypeParser() {
1363             @Override
1364             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1365                 throws InvalidProtocolBufferException {
1366               parser.mergeAny(json, builder);
1367             }
1368           });
1369       // Special-case wrapper types.
1370       WellKnownTypeParser wrappersPrinter =
1371           new WellKnownTypeParser() {
1372             @Override
1373             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1374                 throws InvalidProtocolBufferException {
1375               parser.mergeWrapper(json, builder);
1376             }
1377           };
1378       parsers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
1379       parsers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
1380       parsers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
1381       parsers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter);
1382       parsers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter);
1383       parsers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter);
1384       parsers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter);
1385       parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
1386       parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
1387       // Special-case Timestamp.
1388       parsers.put(
1389           Timestamp.getDescriptor().getFullName(),
1390           new WellKnownTypeParser() {
1391             @Override
1392             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1393                 throws InvalidProtocolBufferException {
1394               parser.mergeTimestamp(json, builder);
1395             }
1396           });
1397       // Special-case Duration.
1398       parsers.put(
1399           Duration.getDescriptor().getFullName(),
1400           new WellKnownTypeParser() {
1401             @Override
1402             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1403                 throws InvalidProtocolBufferException {
1404               parser.mergeDuration(json, builder);
1405             }
1406           });
1407       // Special-case FieldMask.
1408       parsers.put(
1409           FieldMask.getDescriptor().getFullName(),
1410           new WellKnownTypeParser() {
1411             @Override
1412             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1413                 throws InvalidProtocolBufferException {
1414               parser.mergeFieldMask(json, builder);
1415             }
1416           });
1417       // Special-case Struct.
1418       parsers.put(
1419           Struct.getDescriptor().getFullName(),
1420           new WellKnownTypeParser() {
1421             @Override
1422             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1423                 throws InvalidProtocolBufferException {
1424               parser.mergeStruct(json, builder);
1425             }
1426           });
1427       // Special-case ListValue.
1428       parsers.put(
1429           ListValue.getDescriptor().getFullName(),
1430           new WellKnownTypeParser() {
1431             @Override
1432             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1433                 throws InvalidProtocolBufferException {
1434               parser.mergeListValue(json, builder);
1435             }
1436           });
1437       // Special-case Value.
1438       parsers.put(
1439           Value.getDescriptor().getFullName(),
1440           new WellKnownTypeParser() {
1441             @Override
1442             public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
1443                 throws InvalidProtocolBufferException {
1444               parser.mergeValue(json, builder);
1445             }
1446           });
1447       return parsers;
1448     }
1449 
merge(JsonElement json, Message.Builder builder)1450     private void merge(JsonElement json, Message.Builder builder)
1451         throws InvalidProtocolBufferException {
1452       WellKnownTypeParser specialParser =
1453           wellKnownTypeParsers.get(builder.getDescriptorForType().getFullName());
1454       if (specialParser != null) {
1455         specialParser.merge(this, json, builder);
1456         return;
1457       }
1458       mergeMessage(json, builder, false);
1459     }
1460 
1461     // Maps from camel-case field names to FieldDescriptor.
1462     private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps =
1463         new HashMap<Descriptor, Map<String, FieldDescriptor>>();
1464 
getFieldNameMap(Descriptor descriptor)1465     private Map<String, FieldDescriptor> getFieldNameMap(Descriptor descriptor) {
1466       if (!fieldNameMaps.containsKey(descriptor)) {
1467         Map<String, FieldDescriptor> fieldNameMap = new HashMap<String, FieldDescriptor>();
1468         for (FieldDescriptor field : descriptor.getFields()) {
1469           fieldNameMap.put(field.getName(), field);
1470           fieldNameMap.put(field.getJsonName(), field);
1471         }
1472         fieldNameMaps.put(descriptor, fieldNameMap);
1473         return fieldNameMap;
1474       }
1475       return fieldNameMaps.get(descriptor);
1476     }
1477 
mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)1478     private void mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)
1479         throws InvalidProtocolBufferException {
1480       if (!(json instanceof JsonObject)) {
1481         throw new InvalidProtocolBufferException("Expect message object but got: " + json);
1482       }
1483       JsonObject object = (JsonObject) json;
1484       Map<String, FieldDescriptor> fieldNameMap = getFieldNameMap(builder.getDescriptorForType());
1485       for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
1486         if (skipTypeUrl && entry.getKey().equals("@type")) {
1487           continue;
1488         }
1489         FieldDescriptor field = fieldNameMap.get(entry.getKey());
1490         if (field == null) {
1491           if (ignoringUnknownFields) {
1492             continue;
1493           }
1494           throw new InvalidProtocolBufferException(
1495               "Cannot find field: "
1496                   + entry.getKey()
1497                   + " in message "
1498                   + builder.getDescriptorForType().getFullName());
1499         }
1500         mergeField(field, entry.getValue(), builder);
1501       }
1502     }
1503 
mergeAny(JsonElement json, Message.Builder builder)1504     private void mergeAny(JsonElement json, Message.Builder builder)
1505         throws InvalidProtocolBufferException {
1506       Descriptor descriptor = builder.getDescriptorForType();
1507       FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
1508       FieldDescriptor valueField = descriptor.findFieldByName("value");
1509       // Validates type of the message. Note that we can't just cast the message
1510       // to com.google.protobuf.Any because it might be a DynamicMessage.
1511       if (typeUrlField == null
1512           || valueField == null
1513           || typeUrlField.getType() != FieldDescriptor.Type.STRING
1514           || valueField.getType() != FieldDescriptor.Type.BYTES) {
1515         throw new InvalidProtocolBufferException("Invalid Any type.");
1516       }
1517 
1518       if (!(json instanceof JsonObject)) {
1519         throw new InvalidProtocolBufferException("Expect message object but got: " + json);
1520       }
1521       JsonObject object = (JsonObject) json;
1522       if (object.entrySet().isEmpty()) {
1523         return; // builder never modified, so it will end up building the default instance of Any
1524       }
1525       JsonElement typeUrlElement = object.get("@type");
1526       if (typeUrlElement == null) {
1527         throw new InvalidProtocolBufferException("Missing type url when parsing: " + json);
1528       }
1529       String typeUrl = typeUrlElement.getAsString();
1530       Descriptor contentType = registry.getDescriptorForTypeUrl(typeUrl);
1531       if (contentType == null) {
1532         contentType = oldRegistry.getDescriptorForTypeUrl(typeUrl);
1533         if (contentType == null) {
1534           throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
1535         }
1536       }
1537       builder.setField(typeUrlField, typeUrl);
1538       Message.Builder contentBuilder =
1539           DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
1540       WellKnownTypeParser specialParser = wellKnownTypeParsers.get(contentType.getFullName());
1541       if (specialParser != null) {
1542         JsonElement value = object.get("value");
1543         if (value != null) {
1544           specialParser.merge(this, value, contentBuilder);
1545         }
1546       } else {
1547         mergeMessage(json, contentBuilder, true);
1548       }
1549       builder.setField(valueField, contentBuilder.build().toByteString());
1550     }
1551 
mergeFieldMask(JsonElement json, Message.Builder builder)1552     private void mergeFieldMask(JsonElement json, Message.Builder builder)
1553         throws InvalidProtocolBufferException {
1554       FieldMask value = FieldMaskUtil.fromJsonString(json.getAsString());
1555       builder.mergeFrom(value.toByteString());
1556     }
1557 
mergeTimestamp(JsonElement json, Message.Builder builder)1558     private void mergeTimestamp(JsonElement json, Message.Builder builder)
1559         throws InvalidProtocolBufferException {
1560       try {
1561         Timestamp value = Timestamps.parse(json.getAsString());
1562         builder.mergeFrom(value.toByteString());
1563       } catch (ParseException e) {
1564         throw new InvalidProtocolBufferException("Failed to parse timestamp: " + json);
1565       }
1566     }
1567 
mergeDuration(JsonElement json, Message.Builder builder)1568     private void mergeDuration(JsonElement json, Message.Builder builder)
1569         throws InvalidProtocolBufferException {
1570       try {
1571         Duration value = Durations.parse(json.getAsString());
1572         builder.mergeFrom(value.toByteString());
1573       } catch (ParseException e) {
1574         throw new InvalidProtocolBufferException("Failed to parse duration: " + json);
1575       }
1576     }
1577 
mergeStruct(JsonElement json, Message.Builder builder)1578     private void mergeStruct(JsonElement json, Message.Builder builder)
1579         throws InvalidProtocolBufferException {
1580       Descriptor descriptor = builder.getDescriptorForType();
1581       FieldDescriptor field = descriptor.findFieldByName("fields");
1582       if (field == null) {
1583         throw new InvalidProtocolBufferException("Invalid Struct type.");
1584       }
1585       mergeMapField(field, json, builder);
1586     }
1587 
mergeListValue(JsonElement json, Message.Builder builder)1588     private void mergeListValue(JsonElement json, Message.Builder builder)
1589         throws InvalidProtocolBufferException {
1590       Descriptor descriptor = builder.getDescriptorForType();
1591       FieldDescriptor field = descriptor.findFieldByName("values");
1592       if (field == null) {
1593         throw new InvalidProtocolBufferException("Invalid ListValue type.");
1594       }
1595       mergeRepeatedField(field, json, builder);
1596     }
1597 
mergeValue(JsonElement json, Message.Builder builder)1598     private void mergeValue(JsonElement json, Message.Builder builder)
1599         throws InvalidProtocolBufferException {
1600       Descriptor type = builder.getDescriptorForType();
1601       if (json instanceof JsonPrimitive) {
1602         JsonPrimitive primitive = (JsonPrimitive) json;
1603         if (primitive.isBoolean()) {
1604           builder.setField(type.findFieldByName("bool_value"), primitive.getAsBoolean());
1605         } else if (primitive.isNumber()) {
1606           builder.setField(type.findFieldByName("number_value"), primitive.getAsDouble());
1607         } else {
1608           builder.setField(type.findFieldByName("string_value"), primitive.getAsString());
1609         }
1610       } else if (json instanceof JsonObject) {
1611         FieldDescriptor field = type.findFieldByName("struct_value");
1612         Message.Builder structBuilder = builder.newBuilderForField(field);
1613         merge(json, structBuilder);
1614         builder.setField(field, structBuilder.build());
1615       } else if (json instanceof JsonArray) {
1616         FieldDescriptor field = type.findFieldByName("list_value");
1617         Message.Builder listBuilder = builder.newBuilderForField(field);
1618         merge(json, listBuilder);
1619         builder.setField(field, listBuilder.build());
1620       } else if (json instanceof JsonNull) {
1621         builder.setField(
1622             type.findFieldByName("null_value"), NullValue.NULL_VALUE.getValueDescriptor());
1623       } else {
1624         throw new IllegalStateException("Unexpected json data: " + json);
1625       }
1626     }
1627 
mergeWrapper(JsonElement json, Message.Builder builder)1628     private void mergeWrapper(JsonElement json, Message.Builder builder)
1629         throws InvalidProtocolBufferException {
1630       Descriptor type = builder.getDescriptorForType();
1631       FieldDescriptor field = type.findFieldByName("value");
1632       if (field == null) {
1633         throw new InvalidProtocolBufferException("Invalid wrapper type: " + type.getFullName());
1634       }
1635       builder.setField(field, parseFieldValue(field, json, builder));
1636     }
1637 
mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)1638     private void mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)
1639         throws InvalidProtocolBufferException {
1640       if (field.isRepeated()) {
1641         if (builder.getRepeatedFieldCount(field) > 0) {
1642           throw new InvalidProtocolBufferException(
1643               "Field " + field.getFullName() + " has already been set.");
1644         }
1645       } else {
1646         if (builder.hasField(field)) {
1647           throw new InvalidProtocolBufferException(
1648               "Field " + field.getFullName() + " has already been set.");
1649         }
1650       }
1651       if (field.isRepeated() && json instanceof JsonNull) {
1652         // We allow "null" as value for all field types and treat it as if the
1653         // field is not present.
1654         return;
1655       }
1656       if (field.isMapField()) {
1657         mergeMapField(field, json, builder);
1658       } else if (field.isRepeated()) {
1659         mergeRepeatedField(field, json, builder);
1660       } else if (field.getContainingOneof() != null) {
1661         mergeOneofField(field, json, builder);
1662       } else {
1663         Object value = parseFieldValue(field, json, builder);
1664         if (value != null) {
1665           // A field interpreted as "null" is means it's treated as absent.
1666           builder.setField(field, value);
1667         }
1668       }
1669     }
1670 
mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)1671     private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)
1672         throws InvalidProtocolBufferException {
1673       if (!(json instanceof JsonObject)) {
1674         throw new InvalidProtocolBufferException("Expect a map object but found: " + json);
1675       }
1676       Descriptor type = field.getMessageType();
1677       FieldDescriptor keyField = type.findFieldByName("key");
1678       FieldDescriptor valueField = type.findFieldByName("value");
1679       if (keyField == null || valueField == null) {
1680         throw new InvalidProtocolBufferException("Invalid map field: " + field.getFullName());
1681       }
1682       JsonObject object = (JsonObject) json;
1683       for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
1684         Message.Builder entryBuilder = builder.newBuilderForField(field);
1685         Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
1686         Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
1687         if (value == null) {
1688           if (ignoringUnknownFields && valueField.getType() == Type.ENUM) {
1689             continue;
1690           } else {
1691             throw new InvalidProtocolBufferException("Map value cannot be null.");
1692           }
1693         }
1694         entryBuilder.setField(keyField, key);
1695         entryBuilder.setField(valueField, value);
1696         builder.addRepeatedField(field, entryBuilder.build());
1697       }
1698     }
1699 
mergeOneofField(FieldDescriptor field, JsonElement json, Message.Builder builder)1700     private void mergeOneofField(FieldDescriptor field, JsonElement json, Message.Builder builder)
1701         throws InvalidProtocolBufferException {
1702       Object value = parseFieldValue(field, json, builder);
1703       if (value == null) {
1704         // A field interpreted as "null" is means it's treated as absent.
1705         return;
1706       }
1707       if (builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
1708         throw new InvalidProtocolBufferException(
1709             "Cannot set field "
1710                 + field.getFullName()
1711                 + " because another field "
1712                 + builder.getOneofFieldDescriptor(field.getContainingOneof()).getFullName()
1713                 + " belonging to the same oneof has already been set ");
1714       }
1715       builder.setField(field, value);
1716     }
1717 
mergeRepeatedField( FieldDescriptor field, JsonElement json, Message.Builder builder)1718     private void mergeRepeatedField(
1719         FieldDescriptor field, JsonElement json, Message.Builder builder)
1720         throws InvalidProtocolBufferException {
1721       if (!(json instanceof JsonArray)) {
1722         throw new InvalidProtocolBufferException("Expect an array but found: " + json);
1723       }
1724       JsonArray array = (JsonArray) json;
1725       for (int i = 0; i < array.size(); ++i) {
1726         Object value = parseFieldValue(field, array.get(i), builder);
1727         if (value == null) {
1728           if (ignoringUnknownFields && field.getType() == Type.ENUM) {
1729             continue;
1730           } else {
1731             throw new InvalidProtocolBufferException(
1732                 "Repeated field elements cannot be null in field: " + field.getFullName());
1733           }
1734         }
1735         builder.addRepeatedField(field, value);
1736       }
1737     }
1738 
parseInt32(JsonElement json)1739     private int parseInt32(JsonElement json) throws InvalidProtocolBufferException {
1740       try {
1741         return Integer.parseInt(json.getAsString());
1742       } catch (Exception e) {
1743         // Fall through.
1744       }
1745       // JSON doesn't distinguish between integer values and floating point values so "1" and
1746       // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
1747       // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
1748       try {
1749         BigDecimal value = new BigDecimal(json.getAsString());
1750         return value.intValueExact();
1751       } catch (Exception e) {
1752         throw new InvalidProtocolBufferException("Not an int32 value: " + json);
1753       }
1754     }
1755 
parseInt64(JsonElement json)1756     private long parseInt64(JsonElement json) throws InvalidProtocolBufferException {
1757       try {
1758         return Long.parseLong(json.getAsString());
1759       } catch (Exception e) {
1760         // Fall through.
1761       }
1762       // JSON doesn't distinguish between integer values and floating point values so "1" and
1763       // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
1764       // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
1765       try {
1766         BigDecimal value = new BigDecimal(json.getAsString());
1767         return value.longValueExact();
1768       } catch (Exception e) {
1769         throw new InvalidProtocolBufferException("Not an int64 value: " + json);
1770       }
1771     }
1772 
parseUint32(JsonElement json)1773     private int parseUint32(JsonElement json) throws InvalidProtocolBufferException {
1774       try {
1775         long result = Long.parseLong(json.getAsString());
1776         if (result < 0 || result > 0xFFFFFFFFL) {
1777           throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
1778         }
1779         return (int) result;
1780       } catch (InvalidProtocolBufferException e) {
1781         throw e;
1782       } catch (Exception e) {
1783         // Fall through.
1784       }
1785       // JSON doesn't distinguish between integer values and floating point values so "1" and
1786       // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
1787       // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
1788       try {
1789         BigDecimal decimalValue = new BigDecimal(json.getAsString());
1790         BigInteger value = decimalValue.toBigIntegerExact();
1791         if (value.signum() < 0 || value.compareTo(new BigInteger("FFFFFFFF", 16)) > 0) {
1792           throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
1793         }
1794         return value.intValue();
1795       } catch (InvalidProtocolBufferException e) {
1796         throw e;
1797       } catch (Exception e) {
1798         throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
1799       }
1800     }
1801 
1802     private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
1803 
parseUint64(JsonElement json)1804     private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
1805       try {
1806         BigDecimal decimalValue = new BigDecimal(json.getAsString());
1807         BigInteger value = decimalValue.toBigIntegerExact();
1808         if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
1809           throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
1810         }
1811         return value.longValue();
1812       } catch (InvalidProtocolBufferException e) {
1813         throw e;
1814       } catch (Exception e) {
1815         throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
1816       }
1817     }
1818 
parseBool(JsonElement json)1819     private boolean parseBool(JsonElement json) throws InvalidProtocolBufferException {
1820       if (json.getAsString().equals("true")) {
1821         return true;
1822       }
1823       if (json.getAsString().equals("false")) {
1824         return false;
1825       }
1826       throw new InvalidProtocolBufferException("Invalid bool value: " + json);
1827     }
1828 
1829     private static final double EPSILON = 1e-6;
1830 
parseFloat(JsonElement json)1831     private float parseFloat(JsonElement json) throws InvalidProtocolBufferException {
1832       if (json.getAsString().equals("NaN")) {
1833         return Float.NaN;
1834       } else if (json.getAsString().equals("Infinity")) {
1835         return Float.POSITIVE_INFINITY;
1836       } else if (json.getAsString().equals("-Infinity")) {
1837         return Float.NEGATIVE_INFINITY;
1838       }
1839       try {
1840         // We don't use Float.parseFloat() here because that function simply
1841         // accepts all double values. Here we parse the value into a Double
1842         // and do explicit range check on it.
1843         double value = Double.parseDouble(json.getAsString());
1844         // When a float value is printed, the printed value might be a little
1845         // larger or smaller due to precision loss. Here we need to add a bit
1846         // of tolerance when checking whether the float value is in range.
1847         if (value > Float.MAX_VALUE * (1.0 + EPSILON)
1848             || value < -Float.MAX_VALUE * (1.0 + EPSILON)) {
1849           throw new InvalidProtocolBufferException("Out of range float value: " + json);
1850         }
1851         return (float) value;
1852       } catch (InvalidProtocolBufferException e) {
1853         throw e;
1854       } catch (Exception e) {
1855         throw new InvalidProtocolBufferException("Not a float value: " + json);
1856       }
1857     }
1858 
1859     private static final BigDecimal MORE_THAN_ONE = new BigDecimal(String.valueOf(1.0 + EPSILON));
1860     // When a float value is printed, the printed value might be a little
1861     // larger or smaller due to precision loss. Here we need to add a bit
1862     // of tolerance when checking whether the float value is in range.
1863     private static final BigDecimal MAX_DOUBLE =
1864         new BigDecimal(String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
1865     private static final BigDecimal MIN_DOUBLE =
1866         new BigDecimal(String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
1867 
parseDouble(JsonElement json)1868     private double parseDouble(JsonElement json) throws InvalidProtocolBufferException {
1869       if (json.getAsString().equals("NaN")) {
1870         return Double.NaN;
1871       } else if (json.getAsString().equals("Infinity")) {
1872         return Double.POSITIVE_INFINITY;
1873       } else if (json.getAsString().equals("-Infinity")) {
1874         return Double.NEGATIVE_INFINITY;
1875       }
1876       try {
1877         // We don't use Double.parseDouble() here because that function simply
1878         // accepts all values. Here we parse the value into a BigDecimal and do
1879         // explicit range check on it.
1880         BigDecimal value = new BigDecimal(json.getAsString());
1881         if (value.compareTo(MAX_DOUBLE) > 0 || value.compareTo(MIN_DOUBLE) < 0) {
1882           throw new InvalidProtocolBufferException("Out of range double value: " + json);
1883         }
1884         return value.doubleValue();
1885       } catch (InvalidProtocolBufferException e) {
1886         throw e;
1887       } catch (Exception e) {
1888         throw new InvalidProtocolBufferException("Not an double value: " + json);
1889       }
1890     }
1891 
parseString(JsonElement json)1892     private String parseString(JsonElement json) {
1893       return json.getAsString();
1894     }
1895 
parseBytes(JsonElement json)1896     private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
1897       try {
1898         return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
1899       } catch (IllegalArgumentException e) {
1900         return ByteString.copyFrom(BaseEncoding.base64Url().decode(json.getAsString()));
1901       }
1902     }
1903 
parseEnum(EnumDescriptor enumDescriptor, JsonElement json)1904     private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
1905         throws InvalidProtocolBufferException {
1906       String value = json.getAsString();
1907       EnumValueDescriptor result = enumDescriptor.findValueByName(value);
1908       if (result == null) {
1909         // Try to interpret the value as a number.
1910         try {
1911           int numericValue = parseInt32(json);
1912           if (enumDescriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
1913             result = enumDescriptor.findValueByNumberCreatingIfUnknown(numericValue);
1914           } else {
1915             result = enumDescriptor.findValueByNumber(numericValue);
1916           }
1917         } catch (InvalidProtocolBufferException e) {
1918           // Fall through. This exception is about invalid int32 value we get from parseInt32() but
1919           // that's not the exception we want the user to see. Since result == null, we will throw
1920           // an exception later.
1921         }
1922 
1923         if (result == null && !ignoringUnknownFields) {
1924           throw new InvalidProtocolBufferException(
1925               "Invalid enum value: " + value + " for enum type: " + enumDescriptor.getFullName());
1926         }
1927       }
1928       return result;
1929     }
1930 
parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)1931     private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)
1932         throws InvalidProtocolBufferException {
1933       if (json instanceof JsonNull) {
1934         if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
1935             && field.getMessageType().getFullName().equals(Value.getDescriptor().getFullName())) {
1936           // For every other type, "null" means absence, but for the special
1937           // Value message, it means the "null_value" field has been set.
1938           Value value = Value.newBuilder().setNullValueValue(0).build();
1939           return builder.newBuilderForField(field).mergeFrom(value.toByteString()).build();
1940         } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM
1941             && field.getEnumType().getFullName().equals(NullValue.getDescriptor().getFullName())) {
1942           // If the type of the field is a NullValue, then the value should be explicitly set.
1943           return field.getEnumType().findValueByNumber(0);
1944         }
1945         return null;
1946       } else if (json instanceof JsonObject) {
1947         if (field.getType() != FieldDescriptor.Type.MESSAGE
1948             && field.getType() != FieldDescriptor.Type.GROUP) {
1949           // If the field type is primitive, but the json type is JsonObject rather than
1950           // JsonElement, throw a type mismatch error.
1951           throw new InvalidProtocolBufferException(
1952               String.format("Invalid value: %s for expected type: %s", json, field.getType()));
1953         }
1954       }
1955       switch (field.getType()) {
1956         case INT32:
1957         case SINT32:
1958         case SFIXED32:
1959           return parseInt32(json);
1960 
1961         case INT64:
1962         case SINT64:
1963         case SFIXED64:
1964           return parseInt64(json);
1965 
1966         case BOOL:
1967           return parseBool(json);
1968 
1969         case FLOAT:
1970           return parseFloat(json);
1971 
1972         case DOUBLE:
1973           return parseDouble(json);
1974 
1975         case UINT32:
1976         case FIXED32:
1977           return parseUint32(json);
1978 
1979         case UINT64:
1980         case FIXED64:
1981           return parseUint64(json);
1982 
1983         case STRING:
1984           return parseString(json);
1985 
1986         case BYTES:
1987           return parseBytes(json);
1988 
1989         case ENUM:
1990           return parseEnum(field.getEnumType(), json);
1991 
1992         case MESSAGE:
1993         case GROUP:
1994           if (currentDepth >= recursionLimit) {
1995             throw new InvalidProtocolBufferException("Hit recursion limit.");
1996           }
1997           ++currentDepth;
1998           Message.Builder subBuilder = builder.newBuilderForField(field);
1999           merge(json, subBuilder);
2000           --currentDepth;
2001           return subBuilder.build();
2002 
2003         default:
2004           throw new InvalidProtocolBufferException("Invalid field type: " + field.getType());
2005       }
2006     }
2007   }
2008 }
2009