Imported Upstream version 3.8.0
[platform/upstream/protobuf.git] / java / core / src / main / java / com / google / protobuf / Descriptors.java
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;
32
33 import static com.google.protobuf.Internal.checkNotNull;
34
35 import com.google.protobuf.DescriptorProtos.DescriptorProto;
36 import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
37 import com.google.protobuf.DescriptorProtos.EnumOptions;
38 import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto;
39 import com.google.protobuf.DescriptorProtos.EnumValueOptions;
40 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
41 import com.google.protobuf.DescriptorProtos.FieldOptions;
42 import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
43 import com.google.protobuf.DescriptorProtos.FileOptions;
44 import com.google.protobuf.DescriptorProtos.MessageOptions;
45 import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
46 import com.google.protobuf.DescriptorProtos.MethodOptions;
47 import com.google.protobuf.DescriptorProtos.OneofDescriptorProto;
48 import com.google.protobuf.DescriptorProtos.OneofOptions;
49 import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
50 import com.google.protobuf.DescriptorProtos.ServiceOptions;
51 import com.google.protobuf.Descriptors.FileDescriptor.Syntax;
52 import java.lang.ref.WeakReference;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.WeakHashMap;
62 import java.util.logging.Logger;
63
64 /**
65  * Contains a collection of classes which describe protocol message types.
66  *
67  * <p>Every message type has a {@link Descriptor}, which lists all its fields and other information
68  * about a type. You can get a message type's descriptor by calling {@code
69  * MessageType.getDescriptor()}, or (given a message object of the type) {@code
70  * message.getDescriptorForType()}. Furthermore, each message is associated with a {@link
71  * FileDescriptor} for a relevant {@code .proto} file. You can obtain it by calling {@code
72  * Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors for all the messages defined
73  * in that file, and file descriptors for all the imported {@code .proto} files.
74  *
75  * <p>Descriptors are built from DescriptorProtos, as defined in {@code
76  * google/protobuf/descriptor.proto}.
77  *
78  * @author kenton@google.com Kenton Varda
79  */
80 public final class Descriptors {
81   private static final Logger logger = Logger.getLogger(Descriptors.class.getName());
82   /**
83    * Describes a {@code .proto} file, including everything defined within. That includes, in
84    * particular, descriptors for all the messages and file descriptors for all other imported {@code
85    * .proto} files (dependencies).
86    */
87   public static final class FileDescriptor extends GenericDescriptor {
88     /** Convert the descriptor to its protocol message representation. */
89     @Override
90     public FileDescriptorProto toProto() {
91       return proto;
92     }
93
94     /** Get the file name. */
95     @Override
96     public String getName() {
97       return proto.getName();
98     }
99
100     /** Returns this object. */
101     @Override
102     public FileDescriptor getFile() {
103       return this;
104     }
105
106     /** Returns the same as getName(). */
107     @Override
108     public String getFullName() {
109       return proto.getName();
110     }
111
112     /**
113      * Get the proto package name. This is the package name given by the {@code package} statement
114      * in the {@code .proto} file, which differs from the Java package.
115      */
116     public String getPackage() {
117       return proto.getPackage();
118     }
119
120     /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
121     public FileOptions getOptions() {
122       return proto.getOptions();
123     }
124
125     /** Get a list of top-level message types declared in this file. */
126     public List<Descriptor> getMessageTypes() {
127       return Collections.unmodifiableList(Arrays.asList(messageTypes));
128     }
129
130     /** Get a list of top-level enum types declared in this file. */
131     public List<EnumDescriptor> getEnumTypes() {
132       return Collections.unmodifiableList(Arrays.asList(enumTypes));
133     }
134
135     /** Get a list of top-level services declared in this file. */
136     public List<ServiceDescriptor> getServices() {
137       return Collections.unmodifiableList(Arrays.asList(services));
138     }
139
140     /** Get a list of top-level extensions declared in this file. */
141     public List<FieldDescriptor> getExtensions() {
142       return Collections.unmodifiableList(Arrays.asList(extensions));
143     }
144
145     /** Get a list of this file's dependencies (imports). */
146     public List<FileDescriptor> getDependencies() {
147       return Collections.unmodifiableList(Arrays.asList(dependencies));
148     }
149
150     /** Get a list of this file's public dependencies (public imports). */
151     public List<FileDescriptor> getPublicDependencies() {
152       return Collections.unmodifiableList(Arrays.asList(publicDependencies));
153     }
154
155     /** The syntax of the .proto file. */
156     public enum Syntax {
157       UNKNOWN("unknown"),
158       PROTO2("proto2"),
159       PROTO3("proto3");
160
161       Syntax(String name) {
162         this.name = name;
163       }
164
165       private final String name;
166     }
167
168     /** Get the syntax of the .proto file. */
169     public Syntax getSyntax() {
170       if (Syntax.PROTO3.name.equals(proto.getSyntax())) {
171         return Syntax.PROTO3;
172       }
173       return Syntax.PROTO2;
174     }
175
176     /**
177      * Find a message type in the file by name. Does not find nested types.
178      *
179      * @param name The unqualified type name to look for.
180      * @return The message type's descriptor, or {@code null} if not found.
181      */
182     public Descriptor findMessageTypeByName(String name) {
183       // Don't allow looking up nested types.  This will make optimization
184       // easier later.
185       if (name.indexOf('.') != -1) {
186         return null;
187       }
188       final String packageName = getPackage();
189       if (!packageName.isEmpty()) {
190         name = packageName + '.' + name;
191       }
192       final GenericDescriptor result = pool.findSymbol(name);
193       if (result != null && result instanceof Descriptor && result.getFile() == this) {
194         return (Descriptor) result;
195       } else {
196         return null;
197       }
198     }
199
200     /**
201      * Find an enum type in the file by name. Does not find nested types.
202      *
203      * @param name The unqualified type name to look for.
204      * @return The enum type's descriptor, or {@code null} if not found.
205      */
206     public EnumDescriptor findEnumTypeByName(String name) {
207       // Don't allow looking up nested types.  This will make optimization
208       // easier later.
209       if (name.indexOf('.') != -1) {
210         return null;
211       }
212       final String packageName = getPackage();
213       if (!packageName.isEmpty()) {
214         name = packageName + '.' + name;
215       }
216       final GenericDescriptor result = pool.findSymbol(name);
217       if (result != null && result instanceof EnumDescriptor && result.getFile() == this) {
218         return (EnumDescriptor) result;
219       } else {
220         return null;
221       }
222     }
223
224     /**
225      * Find a service type in the file by name.
226      *
227      * @param name The unqualified type name to look for.
228      * @return The service type's descriptor, or {@code null} if not found.
229      */
230     public ServiceDescriptor findServiceByName(String name) {
231       // Don't allow looking up nested types.  This will make optimization
232       // easier later.
233       if (name.indexOf('.') != -1) {
234         return null;
235       }
236       final String packageName = getPackage();
237       if (!packageName.isEmpty()) {
238         name = packageName + '.' + name;
239       }
240       final GenericDescriptor result = pool.findSymbol(name);
241       if (result != null && result instanceof ServiceDescriptor && result.getFile() == this) {
242         return (ServiceDescriptor) result;
243       } else {
244         return null;
245       }
246     }
247
248     /**
249      * Find an extension in the file by name. Does not find extensions nested inside message types.
250      *
251      * @param name The unqualified extension name to look for.
252      * @return The extension's descriptor, or {@code null} if not found.
253      */
254     public FieldDescriptor findExtensionByName(String name) {
255       if (name.indexOf('.') != -1) {
256         return null;
257       }
258       final String packageName = getPackage();
259       if (!packageName.isEmpty()) {
260         name = packageName + '.' + name;
261       }
262       final GenericDescriptor result = pool.findSymbol(name);
263       if (result != null && result instanceof FieldDescriptor && result.getFile() == this) {
264         return (FieldDescriptor) result;
265       } else {
266         return null;
267       }
268     }
269
270     /**
271      * Construct a {@code FileDescriptor}.
272      *
273      * @param proto The protocol message form of the FileDescriptor.
274      * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies.
275      * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur
276      *     for a number of reasons, e.g. because a field has an undefined type or because two
277      *     messages were defined with the same name.
278      */
279     public static FileDescriptor buildFrom(
280         final FileDescriptorProto proto, final FileDescriptor[] dependencies)
281         throws DescriptorValidationException {
282       return buildFrom(proto, dependencies, false);
283     }
284
285     /**
286      * Construct a {@code FileDescriptor}.
287      *
288      * @param proto The protocol message form of the FileDescriptor.
289      * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies.
290      * @param allowUnknownDependencies If true, non-exist dependenncies will be ignored and
291      *     undefined message types will be replaced with a placeholder type.
292      * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur
293      *     for a number of reasons, e.g. because a field has an undefined type or because two
294      *     messages were defined with the same name.
295      */
296     public static FileDescriptor buildFrom(
297         final FileDescriptorProto proto,
298         final FileDescriptor[] dependencies,
299         final boolean allowUnknownDependencies)
300         throws DescriptorValidationException {
301       // Building descriptors involves two steps:  translating and linking.
302       // In the translation step (implemented by FileDescriptor's
303       // constructor), we build an object tree mirroring the
304       // FileDescriptorProto's tree and put all of the descriptors into the
305       // DescriptorPool's lookup tables.  In the linking step, we look up all
306       // type references in the DescriptorPool, so that, for example, a
307       // FieldDescriptor for an embedded message contains a pointer directly
308       // to the Descriptor for that message's type.  We also detect undefined
309       // types in the linking step.
310       final DescriptorPool pool = new DescriptorPool(dependencies, allowUnknownDependencies);
311       final FileDescriptor result =
312           new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
313       result.crossLink();
314       return result;
315     }
316
317     private static byte[] latin1Cat(final String[] strings) {
318       // Hack:  We can't embed a raw byte array inside generated Java code
319       //   (at least, not efficiently), but we can embed Strings.  So, the
320       //   protocol compiler embeds the FileDescriptorProto as a giant
321       //   string literal which is passed to this function to construct the
322       //   file's FileDescriptor.  The string literal contains only 8-bit
323       //   characters, each one representing a byte of the FileDescriptorProto's
324       //   serialized form.  So, if we convert it to bytes in ISO-8859-1, we
325       //   should get the original bytes that we want.
326       // Literal strings are limited to 64k, so it may be split into multiple strings.
327       if (strings.length == 1) {
328         return strings[0].getBytes(Internal.ISO_8859_1);
329       }
330       StringBuilder descriptorData = new StringBuilder();
331       for (String part : strings) {
332         descriptorData.append(part);
333       }
334       return descriptorData.toString().getBytes(Internal.ISO_8859_1);
335     }
336
337     private static FileDescriptor[] findDescriptors(
338         final Class<?> descriptorOuterClass,
339         final String[] dependencyClassNames,
340         final String[] dependencyFileNames) {
341       List<FileDescriptor> descriptors = new ArrayList<FileDescriptor>();
342       for (int i = 0; i < dependencyClassNames.length; i++) {
343         try {
344           Class<?> clazz = descriptorOuterClass.getClassLoader().loadClass(dependencyClassNames[i]);
345           descriptors.add((FileDescriptor) clazz.getField("descriptor").get(null));
346         } catch (Exception e) {
347           // We allow unknown dependencies by default. If a dependency cannot
348           // be found we only generate a warning.
349           logger.warning("Descriptors for \"" + dependencyFileNames[i] + "\" can not be found.");
350         }
351       }
352       return descriptors.toArray(new FileDescriptor[0]);
353     }
354
355     /**
356      * This method is for backward compatibility with generated code which passed an
357      * InternalDescriptorAssigner.
358      */
359     @Deprecated
360     public static void internalBuildGeneratedFileFrom(
361         final String[] descriptorDataParts,
362         final FileDescriptor[] dependencies,
363         final InternalDescriptorAssigner descriptorAssigner) {
364       final byte[] descriptorBytes = latin1Cat(descriptorDataParts);
365
366       FileDescriptorProto proto;
367       try {
368         proto = FileDescriptorProto.parseFrom(descriptorBytes);
369       } catch (InvalidProtocolBufferException e) {
370         throw new IllegalArgumentException(
371             "Failed to parse protocol buffer descriptor for generated code.", e);
372       }
373
374       final FileDescriptor result;
375       try {
376         // When building descriptors for generated code, we allow unknown
377         // dependencies by default.
378         result = buildFrom(proto, dependencies, true);
379       } catch (DescriptorValidationException e) {
380         throw new IllegalArgumentException(
381             "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
382       }
383
384       final ExtensionRegistry registry = descriptorAssigner.assignDescriptors(result);
385
386       if (registry != null) {
387         // We must re-parse the proto using the registry.
388         try {
389           proto = FileDescriptorProto.parseFrom(descriptorBytes, registry);
390         } catch (InvalidProtocolBufferException e) {
391           throw new IllegalArgumentException(
392               "Failed to parse protocol buffer descriptor for generated code.", e);
393         }
394
395         result.setProto(proto);
396       }
397     }
398
399     /**
400      * This method is to be called by generated code only. It is equivalent to {@code buildFrom}
401      * except that the {@code FileDescriptorProto} is encoded in protocol buffer wire format.
402      */
403     public static FileDescriptor internalBuildGeneratedFileFrom(
404         final String[] descriptorDataParts,
405         final FileDescriptor[] dependencies) {
406       final byte[] descriptorBytes = latin1Cat(descriptorDataParts);
407
408       FileDescriptorProto proto;
409       try {
410         proto = FileDescriptorProto.parseFrom(descriptorBytes);
411       } catch (InvalidProtocolBufferException e) {
412         throw new IllegalArgumentException(
413             "Failed to parse protocol buffer descriptor for generated code.", e);
414       }
415
416       try {
417         // When building descriptors for generated code, we allow unknown
418         // dependencies by default.
419         return buildFrom(proto, dependencies, true);
420       } catch (DescriptorValidationException e) {
421         throw new IllegalArgumentException(
422             "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
423       }
424     }
425
426     /**
427      * This method is for backward compatibility with generated code which passed an
428      * InternalDescriptorAssigner.
429      */
430     @Deprecated
431     public static void internalBuildGeneratedFileFrom(
432         final String[] descriptorDataParts,
433         final Class<?> descriptorOuterClass,
434         final String[] dependencyClassNames,
435         final String[] dependencyFileNames,
436         final InternalDescriptorAssigner descriptorAssigner) {
437       FileDescriptor[] dependencies = findDescriptors(
438           descriptorOuterClass, dependencyClassNames, dependencyFileNames);
439       internalBuildGeneratedFileFrom(
440           descriptorDataParts, dependencies, descriptorAssigner);
441     }
442
443     /**
444      * This method is to be called by generated code only. It uses Java reflection to load the
445      * dependencies' descriptors.
446      */
447     public static FileDescriptor internalBuildGeneratedFileFrom(
448         final String[] descriptorDataParts,
449         final Class<?> descriptorOuterClass,
450         final String[] dependencyClassNames,
451         final String[] dependencyFileNames) {
452       FileDescriptor[] dependencies = findDescriptors(
453           descriptorOuterClass, dependencyClassNames, dependencyFileNames);
454       return internalBuildGeneratedFileFrom(descriptorDataParts, dependencies);
455     }
456
457     /**
458      * This method is to be called by generated code only. It is used to update the
459      * FileDescriptorProto associated with the descriptor by parsing it again with the given
460      * ExtensionRegistry. This is needed to recognize custom options.
461      */
462     public static void internalUpdateFileDescriptor(
463         final FileDescriptor descriptor, final ExtensionRegistry registry) {
464       ByteString bytes = descriptor.proto.toByteString();
465       FileDescriptorProto proto;
466       try {
467         proto = FileDescriptorProto.parseFrom(bytes, registry);
468       } catch (InvalidProtocolBufferException e) {
469         throw new IllegalArgumentException(
470             "Failed to parse protocol buffer descriptor for generated code.", e);
471       }
472       descriptor.setProto(proto);
473     }
474
475     /**
476      * This class should be used by generated code only. When calling {@link
477      * FileDescriptor#internalBuildGeneratedFileFrom}, the caller provides a callback implementing
478      * this interface. The callback is called after the FileDescriptor has been constructed, in
479      * order to assign all the global variables defined in the generated code which point at parts
480      * of the FileDescriptor. The callback returns an ExtensionRegistry which contains any
481      * extensions which might be used in the descriptor -- that is, extensions of the various
482      * "Options" messages defined in descriptor.proto. The callback may also return null to indicate
483      * that no extensions are used in the descriptor.
484      *
485      * This interface is deprecated.  Use the return value of internalBuildGeneratedFrom() instead.
486      */
487     @Deprecated
488     public interface InternalDescriptorAssigner {
489       ExtensionRegistry assignDescriptors(FileDescriptor root);
490     }
491
492     private FileDescriptorProto proto;
493     private final Descriptor[] messageTypes;
494     private final EnumDescriptor[] enumTypes;
495     private final ServiceDescriptor[] services;
496     private final FieldDescriptor[] extensions;
497     private final FileDescriptor[] dependencies;
498     private final FileDescriptor[] publicDependencies;
499     private final DescriptorPool pool;
500
501     private FileDescriptor(
502         final FileDescriptorProto proto,
503         final FileDescriptor[] dependencies,
504         final DescriptorPool pool,
505         boolean allowUnknownDependencies)
506         throws DescriptorValidationException {
507       this.pool = pool;
508       this.proto = proto;
509       this.dependencies = dependencies.clone();
510       HashMap<String, FileDescriptor> nameToFileMap = new HashMap<String, FileDescriptor>();
511       for (FileDescriptor file : dependencies) {
512         nameToFileMap.put(file.getName(), file);
513       }
514       List<FileDescriptor> publicDependencies = new ArrayList<FileDescriptor>();
515       for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
516         int index = proto.getPublicDependency(i);
517         if (index < 0 || index >= proto.getDependencyCount()) {
518           throw new DescriptorValidationException(this, "Invalid public dependency index.");
519         }
520         String name = proto.getDependency(index);
521         FileDescriptor file = nameToFileMap.get(name);
522         if (file == null) {
523           if (!allowUnknownDependencies) {
524             throw new DescriptorValidationException(this, "Invalid public dependency: " + name);
525           }
526           // Ignore unknown dependencies.
527         } else {
528           publicDependencies.add(file);
529         }
530       }
531       this.publicDependencies = new FileDescriptor[publicDependencies.size()];
532       publicDependencies.toArray(this.publicDependencies);
533
534       pool.addPackage(getPackage(), this);
535
536       messageTypes = new Descriptor[proto.getMessageTypeCount()];
537       for (int i = 0; i < proto.getMessageTypeCount(); i++) {
538         messageTypes[i] = new Descriptor(proto.getMessageType(i), this, null, i);
539       }
540
541       enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
542       for (int i = 0; i < proto.getEnumTypeCount(); i++) {
543         enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i);
544       }
545
546       services = new ServiceDescriptor[proto.getServiceCount()];
547       for (int i = 0; i < proto.getServiceCount(); i++) {
548         services[i] = new ServiceDescriptor(proto.getService(i), this, i);
549       }
550
551       extensions = new FieldDescriptor[proto.getExtensionCount()];
552       for (int i = 0; i < proto.getExtensionCount(); i++) {
553         extensions[i] = new FieldDescriptor(proto.getExtension(i), this, null, i, true);
554       }
555     }
556
557     /** Create a placeholder FileDescriptor for a message Descriptor. */
558     FileDescriptor(String packageName, Descriptor message) throws DescriptorValidationException {
559       this.pool = new DescriptorPool(new FileDescriptor[0], true);
560       this.proto =
561           FileDescriptorProto.newBuilder()
562               .setName(message.getFullName() + ".placeholder.proto")
563               .setPackage(packageName)
564               .addMessageType(message.toProto())
565               .build();
566       this.dependencies = new FileDescriptor[0];
567       this.publicDependencies = new FileDescriptor[0];
568
569       messageTypes = new Descriptor[] {message};
570       enumTypes = new EnumDescriptor[0];
571       services = new ServiceDescriptor[0];
572       extensions = new FieldDescriptor[0];
573
574       pool.addPackage(packageName, this);
575       pool.addSymbol(message);
576     }
577
578     /** Look up and cross-link all field types, etc. */
579     private void crossLink() throws DescriptorValidationException {
580       for (final Descriptor messageType : messageTypes) {
581         messageType.crossLink();
582       }
583
584       for (final ServiceDescriptor service : services) {
585         service.crossLink();
586       }
587
588       for (final FieldDescriptor extension : extensions) {
589         extension.crossLink();
590       }
591     }
592
593     /**
594      * Replace our {@link FileDescriptorProto} with the given one, which is identical except that it
595      * might contain extensions that weren't present in the original. This method is needed for
596      * bootstrapping when a file defines custom options. The options may be defined in the file
597      * itself, so we can't actually parse them until we've constructed the descriptors, but to
598      * construct the descriptors we have to have parsed the descriptor protos. So, we have to parse
599      * the descriptor protos a second time after constructing the descriptors.
600      */
601     private void setProto(final FileDescriptorProto proto) {
602       this.proto = proto;
603
604       for (int i = 0; i < messageTypes.length; i++) {
605         messageTypes[i].setProto(proto.getMessageType(i));
606       }
607
608       for (int i = 0; i < enumTypes.length; i++) {
609         enumTypes[i].setProto(proto.getEnumType(i));
610       }
611
612       for (int i = 0; i < services.length; i++) {
613         services[i].setProto(proto.getService(i));
614       }
615
616       for (int i = 0; i < extensions.length; i++) {
617         extensions[i].setProto(proto.getExtension(i));
618       }
619     }
620
621     boolean supportsUnknownEnumValue() {
622       return getSyntax() == Syntax.PROTO3;
623     }
624   }
625
626   // =================================================================
627
628   /** Describes a message type. */
629   public static final class Descriptor extends GenericDescriptor {
630     /**
631      * Get the index of this descriptor within its parent. In other words, given a {@link
632      * FileDescriptor} {@code file}, the following is true:
633      *
634      * <pre>
635      *   for all i in [0, file.getMessageTypeCount()):
636      *     file.getMessageType(i).getIndex() == i
637      * </pre>
638      *
639      * Similarly, for a {@link Descriptor} {@code messageType}:
640      *
641      * <pre>
642      *   for all i in [0, messageType.getNestedTypeCount()):
643      *     messageType.getNestedType(i).getIndex() == i
644      * </pre>
645      */
646     public int getIndex() {
647       return index;
648     }
649
650     /** Convert the descriptor to its protocol message representation. */
651     @Override
652     public DescriptorProto toProto() {
653       return proto;
654     }
655
656     /** Get the type's unqualified name. */
657     @Override
658     public String getName() {
659       return proto.getName();
660     }
661
662     /**
663      * Get the type's fully-qualified name, within the proto language's namespace. This differs from
664      * the Java name. For example, given this {@code .proto}:
665      *
666      * <pre>
667      *   package foo.bar;
668      *   option java_package = "com.example.protos"
669      *   message Baz {}
670      * </pre>
671      *
672      * {@code Baz}'s full name is "foo.bar.Baz".
673      */
674     @Override
675     public String getFullName() {
676       return fullName;
677     }
678
679     /** Get the {@link FileDescriptor} containing this descriptor. */
680     @Override
681     public FileDescriptor getFile() {
682       return file;
683     }
684
685     /** If this is a nested type, get the outer descriptor, otherwise null. */
686     public Descriptor getContainingType() {
687       return containingType;
688     }
689
690     /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
691     public MessageOptions getOptions() {
692       return proto.getOptions();
693     }
694
695     /** Get a list of this message type's fields. */
696     public List<FieldDescriptor> getFields() {
697       return Collections.unmodifiableList(Arrays.asList(fields));
698     }
699
700     /** Get a list of this message type's oneofs. */
701     public List<OneofDescriptor> getOneofs() {
702       return Collections.unmodifiableList(Arrays.asList(oneofs));
703     }
704
705     /** Get a list of this message type's extensions. */
706     public List<FieldDescriptor> getExtensions() {
707       return Collections.unmodifiableList(Arrays.asList(extensions));
708     }
709
710     /** Get a list of message types nested within this one. */
711     public List<Descriptor> getNestedTypes() {
712       return Collections.unmodifiableList(Arrays.asList(nestedTypes));
713     }
714
715     /** Get a list of enum types nested within this one. */
716     public List<EnumDescriptor> getEnumTypes() {
717       return Collections.unmodifiableList(Arrays.asList(enumTypes));
718     }
719
720     /** Determines if the given field number is an extension. */
721     public boolean isExtensionNumber(final int number) {
722       for (final DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) {
723         if (range.getStart() <= number && number < range.getEnd()) {
724           return true;
725         }
726       }
727       return false;
728     }
729
730     /** Determines if the given field number is reserved. */
731     public boolean isReservedNumber(final int number) {
732       for (final DescriptorProto.ReservedRange range : proto.getReservedRangeList()) {
733         if (range.getStart() <= number && number < range.getEnd()) {
734           return true;
735         }
736       }
737       return false;
738     }
739
740     /** Determines if the given field name is reserved. */
741     public boolean isReservedName(final String name) {
742       checkNotNull(name);
743       for (final String reservedName : proto.getReservedNameList()) {
744         if (reservedName.equals(name)) {
745           return true;
746         }
747       }
748       return false;
749     }
750
751     /**
752      * Indicates whether the message can be extended. That is, whether it has any "extensions x to
753      * y" ranges declared on it.
754      */
755     public boolean isExtendable() {
756       return proto.getExtensionRangeList().size() != 0;
757     }
758
759     /**
760      * Finds a field by name.
761      *
762      * @param name The unqualified name of the field (e.g. "foo").
763      * @return The field's descriptor, or {@code null} if not found.
764      */
765     public FieldDescriptor findFieldByName(final String name) {
766       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
767       if (result != null && result instanceof FieldDescriptor) {
768         return (FieldDescriptor) result;
769       } else {
770         return null;
771       }
772     }
773
774     /**
775      * Finds a field by field number.
776      *
777      * @param number The field number within this message type.
778      * @return The field's descriptor, or {@code null} if not found.
779      */
780     public FieldDescriptor findFieldByNumber(final int number) {
781       return file.pool.fieldsByNumber.get(new DescriptorPool.DescriptorIntPair(this, number));
782     }
783
784     /**
785      * Finds a nested message type by name.
786      *
787      * @param name The unqualified name of the nested type (e.g. "Foo").
788      * @return The types's descriptor, or {@code null} if not found.
789      */
790     public Descriptor findNestedTypeByName(final String name) {
791       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
792       if (result != null && result instanceof Descriptor) {
793         return (Descriptor) result;
794       } else {
795         return null;
796       }
797     }
798
799     /**
800      * Finds a nested enum type by name.
801      *
802      * @param name The unqualified name of the nested type (e.g. "Foo").
803      * @return The types's descriptor, or {@code null} if not found.
804      */
805     public EnumDescriptor findEnumTypeByName(final String name) {
806       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
807       if (result != null && result instanceof EnumDescriptor) {
808         return (EnumDescriptor) result;
809       } else {
810         return null;
811       }
812     }
813
814     private final int index;
815     private DescriptorProto proto;
816     private final String fullName;
817     private final FileDescriptor file;
818     private final Descriptor containingType;
819     private final Descriptor[] nestedTypes;
820     private final EnumDescriptor[] enumTypes;
821     private final FieldDescriptor[] fields;
822     private final FieldDescriptor[] extensions;
823     private final OneofDescriptor[] oneofs;
824
825     // Used to create a placeholder when the type cannot be found.
826     Descriptor(final String fullname) throws DescriptorValidationException {
827       String name = fullname;
828       String packageName = "";
829       int pos = fullname.lastIndexOf('.');
830       if (pos != -1) {
831         name = fullname.substring(pos + 1);
832         packageName = fullname.substring(0, pos);
833       }
834       this.index = 0;
835       this.proto =
836           DescriptorProto.newBuilder()
837               .setName(name)
838               .addExtensionRange(
839                   DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build())
840               .build();
841       this.fullName = fullname;
842       this.containingType = null;
843
844       this.nestedTypes = new Descriptor[0];
845       this.enumTypes = new EnumDescriptor[0];
846       this.fields = new FieldDescriptor[0];
847       this.extensions = new FieldDescriptor[0];
848       this.oneofs = new OneofDescriptor[0];
849
850       // Create a placeholder FileDescriptor to hold this message.
851       this.file = new FileDescriptor(packageName, this);
852     }
853
854     private Descriptor(
855         final DescriptorProto proto,
856         final FileDescriptor file,
857         final Descriptor parent,
858         final int index)
859         throws DescriptorValidationException {
860       this.index = index;
861       this.proto = proto;
862       fullName = computeFullName(file, parent, proto.getName());
863       this.file = file;
864       containingType = parent;
865
866       oneofs = new OneofDescriptor[proto.getOneofDeclCount()];
867       for (int i = 0; i < proto.getOneofDeclCount(); i++) {
868         oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i);
869       }
870
871       nestedTypes = new Descriptor[proto.getNestedTypeCount()];
872       for (int i = 0; i < proto.getNestedTypeCount(); i++) {
873         nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i);
874       }
875
876       enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
877       for (int i = 0; i < proto.getEnumTypeCount(); i++) {
878         enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i);
879       }
880
881       fields = new FieldDescriptor[proto.getFieldCount()];
882       for (int i = 0; i < proto.getFieldCount(); i++) {
883         fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false);
884       }
885
886       extensions = new FieldDescriptor[proto.getExtensionCount()];
887       for (int i = 0; i < proto.getExtensionCount(); i++) {
888         extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true);
889       }
890
891       for (int i = 0; i < proto.getOneofDeclCount(); i++) {
892         oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()];
893         oneofs[i].fieldCount = 0;
894       }
895       for (int i = 0; i < proto.getFieldCount(); i++) {
896         OneofDescriptor oneofDescriptor = fields[i].getContainingOneof();
897         if (oneofDescriptor != null) {
898           oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
899         }
900       }
901
902       file.pool.addSymbol(this);
903     }
904
905     /** Look up and cross-link all field types, etc. */
906     private void crossLink() throws DescriptorValidationException {
907       for (final Descriptor nestedType : nestedTypes) {
908         nestedType.crossLink();
909       }
910
911       for (final FieldDescriptor field : fields) {
912         field.crossLink();
913       }
914
915       for (final FieldDescriptor extension : extensions) {
916         extension.crossLink();
917       }
918     }
919
920     /** See {@link FileDescriptor#setProto}. */
921     private void setProto(final DescriptorProto proto) {
922       this.proto = proto;
923
924       for (int i = 0; i < nestedTypes.length; i++) {
925         nestedTypes[i].setProto(proto.getNestedType(i));
926       }
927
928       for (int i = 0; i < oneofs.length; i++) {
929         oneofs[i].setProto(proto.getOneofDecl(i));
930       }
931
932       for (int i = 0; i < enumTypes.length; i++) {
933         enumTypes[i].setProto(proto.getEnumType(i));
934       }
935
936       for (int i = 0; i < fields.length; i++) {
937         fields[i].setProto(proto.getField(i));
938       }
939
940       for (int i = 0; i < extensions.length; i++) {
941         extensions[i].setProto(proto.getExtension(i));
942       }
943     }
944   }
945
946   // =================================================================
947
948   /** Describes a field of a message type. */
949   public static final class FieldDescriptor extends GenericDescriptor
950       implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> {
951     /**
952      * Get the index of this descriptor within its parent.
953      *
954      * @see Descriptors.Descriptor#getIndex()
955      */
956     public int getIndex() {
957       return index;
958     }
959
960     /** Convert the descriptor to its protocol message representation. */
961     @Override
962     public FieldDescriptorProto toProto() {
963       return proto;
964     }
965
966     /** Get the field's unqualified name. */
967     @Override
968     public String getName() {
969       return proto.getName();
970     }
971
972     /** Get the field's number. */
973     @Override
974     public int getNumber() {
975       return proto.getNumber();
976     }
977
978     /**
979      * Get the field's fully-qualified name.
980      *
981      * @see Descriptors.Descriptor#getFullName()
982      */
983     @Override
984     public String getFullName() {
985       return fullName;
986     }
987
988     /** Get the JSON name of this field. */
989     public String getJsonName() {
990       return jsonName;
991     }
992
993     /**
994      * Get the field's java type. This is just for convenience. Every {@code
995      * FieldDescriptorProto.Type} maps to exactly one Java type.
996      */
997     public JavaType getJavaType() {
998       return type.getJavaType();
999     }
1000
1001     /** For internal use only. */
1002     @Override
1003     public WireFormat.JavaType getLiteJavaType() {
1004       return getLiteType().getJavaType();
1005     }
1006
1007     /** Get the {@code FileDescriptor} containing this descriptor. */
1008     @Override
1009     public FileDescriptor getFile() {
1010       return file;
1011     }
1012
1013     /** Get the field's declared type. */
1014     public Type getType() {
1015       return type;
1016     }
1017
1018     /** For internal use only. */
1019     @Override
1020     public WireFormat.FieldType getLiteType() {
1021       return table[type.ordinal()];
1022     }
1023
1024     /** For internal use only. */
1025     public boolean needsUtf8Check() {
1026       if (type != Type.STRING) {
1027         return false;
1028       }
1029       if (getContainingType().getOptions().getMapEntry()) {
1030         // Always enforce strict UTF-8 checking for map fields.
1031         return true;
1032       }
1033       if (getFile().getSyntax() == Syntax.PROTO3) {
1034         return true;
1035       }
1036       return getFile().getOptions().getJavaStringCheckUtf8();
1037     }
1038
1039     public boolean isMapField() {
1040       return getType() == Type.MESSAGE
1041           && isRepeated()
1042           && getMessageType().getOptions().getMapEntry();
1043     }
1044
1045     // I'm pretty sure values() constructs a new array every time, since there
1046     // is nothing stopping the caller from mutating the array.  Therefore we
1047     // make a static copy here.
1048     private static final WireFormat.FieldType[] table = WireFormat.FieldType.values();
1049
1050     /** Is this field declared required? */
1051     public boolean isRequired() {
1052       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED;
1053     }
1054
1055     /** Is this field declared optional? */
1056     public boolean isOptional() {
1057       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL;
1058     }
1059
1060     /** Is this field declared repeated? */
1061     @Override
1062     public boolean isRepeated() {
1063       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
1064     }
1065
1066     /**
1067      * Does this field have the {@code [packed = true]} option or is this field packable in proto3
1068      * and not explicitly setted to unpacked?
1069      */
1070     @Override
1071     public boolean isPacked() {
1072       if (!isPackable()) {
1073         return false;
1074       }
1075       if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) {
1076         return getOptions().getPacked();
1077       } else {
1078         return !getOptions().hasPacked() || getOptions().getPacked();
1079       }
1080     }
1081
1082     /** Can this field be packed? i.e. is it a repeated primitive field? */
1083     public boolean isPackable() {
1084       return isRepeated() && getLiteType().isPackable();
1085     }
1086
1087     /** Returns true if the field had an explicitly-defined default value. */
1088     public boolean hasDefaultValue() {
1089       return proto.hasDefaultValue();
1090     }
1091
1092     /**
1093      * Returns the field's default value. Valid for all types except for messages and groups. For
1094      * all other types, the object returned is of the same class that would returned by
1095      * Message.getField(this).
1096      */
1097     public Object getDefaultValue() {
1098       if (getJavaType() == JavaType.MESSAGE) {
1099         throw new UnsupportedOperationException(
1100             "FieldDescriptor.getDefaultValue() called on an embedded message field.");
1101       }
1102       return defaultValue;
1103     }
1104
1105     /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
1106     public FieldOptions getOptions() {
1107       return proto.getOptions();
1108     }
1109
1110     /** Is this field an extension? */
1111     public boolean isExtension() {
1112       return proto.hasExtendee();
1113     }
1114
1115     /**
1116      * Get the field's containing type. For extensions, this is the type being extended, not the
1117      * location where the extension was defined. See {@link #getExtensionScope()}.
1118      */
1119     public Descriptor getContainingType() {
1120       return containingType;
1121     }
1122
1123     /** Get the field's containing oneof. */
1124     public OneofDescriptor getContainingOneof() {
1125       return containingOneof;
1126     }
1127
1128     /**
1129      * For extensions defined nested within message types, gets the outer type. Not valid for
1130      * non-extension fields. For example, consider this {@code .proto} file:
1131      *
1132      * <pre>
1133      *   message Foo {
1134      *     extensions 1000 to max;
1135      *   }
1136      *   extend Foo {
1137      *     optional int32 baz = 1234;
1138      *   }
1139      *   message Bar {
1140      *     extend Foo {
1141      *       optional int32 qux = 4321;
1142      *     }
1143      *   }
1144      * </pre>
1145      *
1146      * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. However, {@code baz}'s
1147      * extension scope is {@code null} while {@code qux}'s extension scope is {@code Bar}.
1148      */
1149     public Descriptor getExtensionScope() {
1150       if (!isExtension()) {
1151         throw new UnsupportedOperationException(
1152             String.format("This field is not an extension. (%s)", fullName));
1153       }
1154       return extensionScope;
1155     }
1156
1157     /** For embedded message and group fields, gets the field's type. */
1158     public Descriptor getMessageType() {
1159       if (getJavaType() != JavaType.MESSAGE) {
1160         throw new UnsupportedOperationException(
1161             String.format("This field is not of message type. (%s)", fullName));
1162       }
1163       return messageType;
1164     }
1165
1166     /** For enum fields, gets the field's type. */
1167     @Override
1168     public EnumDescriptor getEnumType() {
1169       if (getJavaType() != JavaType.ENUM) {
1170         throw new UnsupportedOperationException(
1171             String.format("This field is not of enum type. (%s)", fullName));
1172       }
1173       return enumType;
1174     }
1175
1176     /**
1177      * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which
1178      * simply means ascending order by field number. {@code other} must be a field of the same type
1179      * -- i.e. {@code getContainingType()} must return the same {@code Descriptor} for both fields.
1180      *
1181      * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than
1182      *     {@code other}, respectively.
1183      */
1184     @Override
1185     public int compareTo(final FieldDescriptor other) {
1186       if (other.containingType != containingType) {
1187         throw new IllegalArgumentException(
1188             "FieldDescriptors can only be compared to other FieldDescriptors "
1189                 + "for fields of the same message type.");
1190       }
1191       return getNumber() - other.getNumber();
1192     }
1193
1194     @Override
1195     public String toString() {
1196       return getFullName();
1197     }
1198
1199     private final int index;
1200
1201     private FieldDescriptorProto proto;
1202     private final String fullName;
1203     private final String jsonName;
1204     private final FileDescriptor file;
1205     private final Descriptor extensionScope;
1206
1207     // Possibly initialized during cross-linking.
1208     private Type type;
1209     private Descriptor containingType;
1210     private Descriptor messageType;
1211     private OneofDescriptor containingOneof;
1212     private EnumDescriptor enumType;
1213     private Object defaultValue;
1214
1215     public enum Type {
1216       DOUBLE(JavaType.DOUBLE),
1217       FLOAT(JavaType.FLOAT),
1218       INT64(JavaType.LONG),
1219       UINT64(JavaType.LONG),
1220       INT32(JavaType.INT),
1221       FIXED64(JavaType.LONG),
1222       FIXED32(JavaType.INT),
1223       BOOL(JavaType.BOOLEAN),
1224       STRING(JavaType.STRING),
1225       GROUP(JavaType.MESSAGE),
1226       MESSAGE(JavaType.MESSAGE),
1227       BYTES(JavaType.BYTE_STRING),
1228       UINT32(JavaType.INT),
1229       ENUM(JavaType.ENUM),
1230       SFIXED32(JavaType.INT),
1231       SFIXED64(JavaType.LONG),
1232       SINT32(JavaType.INT),
1233       SINT64(JavaType.LONG);
1234
1235       Type(final JavaType javaType) {
1236         this.javaType = javaType;
1237       }
1238
1239       private JavaType javaType;
1240
1241       public FieldDescriptorProto.Type toProto() {
1242         return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
1243       }
1244
1245       public JavaType getJavaType() {
1246         return javaType;
1247       }
1248
1249       public static Type valueOf(final FieldDescriptorProto.Type type) {
1250         return values()[type.getNumber() - 1];
1251       }
1252     }
1253
1254     static {
1255       // Refuse to init if someone added a new declared type.
1256       if (Type.values().length != FieldDescriptorProto.Type.values().length) {
1257         throw new RuntimeException(
1258             "descriptor.proto has a new declared type but Descriptors.java wasn't updated.");
1259       }
1260     }
1261
1262     public enum JavaType {
1263       INT(0),
1264       LONG(0L),
1265       FLOAT(0F),
1266       DOUBLE(0D),
1267       BOOLEAN(false),
1268       STRING(""),
1269       BYTE_STRING(ByteString.EMPTY),
1270       ENUM(null),
1271       MESSAGE(null);
1272
1273       JavaType(final Object defaultDefault) {
1274         this.defaultDefault = defaultDefault;
1275       }
1276
1277       /**
1278        * The default default value for fields of this type, if it's a primitive type. This is meant
1279        * for use inside this file only, hence is private.
1280        */
1281       private final Object defaultDefault;
1282     }
1283
1284     // This method should match exactly with the ToJsonName() function in C++
1285     // descriptor.cc.
1286     private static String fieldNameToJsonName(String name) {
1287       final int length = name.length();
1288       StringBuilder result = new StringBuilder(length);
1289       boolean isNextUpperCase = false;
1290       for (int i = 0; i < length; i++) {
1291         char ch = name.charAt(i);
1292         if (ch == '_') {
1293           isNextUpperCase = true;
1294         } else if (isNextUpperCase) {
1295           // This closely matches the logic for ASCII characters in:
1296           // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689
1297           if ('a' <= ch && ch <= 'z') {
1298             ch = (char) (ch - 'a' + 'A');
1299           }
1300           result.append(ch);
1301           isNextUpperCase = false;
1302         } else {
1303           result.append(ch);
1304         }
1305       }
1306       return result.toString();
1307     }
1308
1309     private FieldDescriptor(
1310         final FieldDescriptorProto proto,
1311         final FileDescriptor file,
1312         final Descriptor parent,
1313         final int index,
1314         final boolean isExtension)
1315         throws DescriptorValidationException {
1316       this.index = index;
1317       this.proto = proto;
1318       fullName = computeFullName(file, parent, proto.getName());
1319       this.file = file;
1320       if (proto.hasJsonName()) {
1321         jsonName = proto.getJsonName();
1322       } else {
1323         jsonName = fieldNameToJsonName(proto.getName());
1324       }
1325
1326       if (proto.hasType()) {
1327         type = Type.valueOf(proto.getType());
1328       }
1329
1330       if (getNumber() <= 0) {
1331         throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
1332       }
1333
1334       if (isExtension) {
1335         if (!proto.hasExtendee()) {
1336           throw new DescriptorValidationException(
1337               this, "FieldDescriptorProto.extendee not set for extension field.");
1338         }
1339         containingType = null; // Will be filled in when cross-linking
1340         if (parent != null) {
1341           extensionScope = parent;
1342         } else {
1343           extensionScope = null;
1344         }
1345
1346         if (proto.hasOneofIndex()) {
1347           throw new DescriptorValidationException(
1348               this, "FieldDescriptorProto.oneof_index set for extension field.");
1349         }
1350         containingOneof = null;
1351       } else {
1352         if (proto.hasExtendee()) {
1353           throw new DescriptorValidationException(
1354               this, "FieldDescriptorProto.extendee set for non-extension field.");
1355         }
1356         containingType = parent;
1357
1358         if (proto.hasOneofIndex()) {
1359           if (proto.getOneofIndex() < 0
1360               || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) {
1361             throw new DescriptorValidationException(
1362                 this,
1363                 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName());
1364           }
1365           containingOneof = parent.getOneofs().get(proto.getOneofIndex());
1366           containingOneof.fieldCount++;
1367         } else {
1368           containingOneof = null;
1369         }
1370         extensionScope = null;
1371       }
1372
1373       file.pool.addSymbol(this);
1374     }
1375
1376     /** Look up and cross-link all field types, etc. */
1377     private void crossLink() throws DescriptorValidationException {
1378       if (proto.hasExtendee()) {
1379         final GenericDescriptor extendee =
1380             file.pool.lookupSymbol(
1381                 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1382         if (!(extendee instanceof Descriptor)) {
1383           throw new DescriptorValidationException(
1384               this, '\"' + proto.getExtendee() + "\" is not a message type.");
1385         }
1386         containingType = (Descriptor) extendee;
1387
1388         if (!getContainingType().isExtensionNumber(getNumber())) {
1389           throw new DescriptorValidationException(
1390               this,
1391               '\"'
1392                   + getContainingType().getFullName()
1393                   + "\" does not declare "
1394                   + getNumber()
1395                   + " as an extension number.");
1396         }
1397       }
1398
1399       if (proto.hasTypeName()) {
1400         final GenericDescriptor typeDescriptor =
1401             file.pool.lookupSymbol(
1402                 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1403
1404         if (!proto.hasType()) {
1405           // Choose field type based on symbol.
1406           if (typeDescriptor instanceof Descriptor) {
1407             type = Type.MESSAGE;
1408           } else if (typeDescriptor instanceof EnumDescriptor) {
1409             type = Type.ENUM;
1410           } else {
1411             throw new DescriptorValidationException(
1412                 this, '\"' + proto.getTypeName() + "\" is not a type.");
1413           }
1414         }
1415
1416         if (getJavaType() == JavaType.MESSAGE) {
1417           if (!(typeDescriptor instanceof Descriptor)) {
1418             throw new DescriptorValidationException(
1419                 this, '\"' + proto.getTypeName() + "\" is not a message type.");
1420           }
1421           messageType = (Descriptor) typeDescriptor;
1422
1423           if (proto.hasDefaultValue()) {
1424             throw new DescriptorValidationException(this, "Messages can't have default values.");
1425           }
1426         } else if (getJavaType() == JavaType.ENUM) {
1427           if (!(typeDescriptor instanceof EnumDescriptor)) {
1428             throw new DescriptorValidationException(
1429                 this, '\"' + proto.getTypeName() + "\" is not an enum type.");
1430           }
1431           enumType = (EnumDescriptor) typeDescriptor;
1432         } else {
1433           throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
1434         }
1435       } else {
1436         if (getJavaType() == JavaType.MESSAGE || getJavaType() == JavaType.ENUM) {
1437           throw new DescriptorValidationException(
1438               this, "Field with message or enum type missing type_name.");
1439         }
1440       }
1441
1442       // Only repeated primitive fields may be packed.
1443       if (proto.getOptions().getPacked() && !isPackable()) {
1444         throw new DescriptorValidationException(
1445             this, "[packed = true] can only be specified for repeated primitive fields.");
1446       }
1447
1448       // We don't attempt to parse the default value until here because for
1449       // enums we need the enum type's descriptor.
1450       if (proto.hasDefaultValue()) {
1451         if (isRepeated()) {
1452           throw new DescriptorValidationException(
1453               this, "Repeated fields cannot have default values.");
1454         }
1455
1456         try {
1457           switch (getType()) {
1458             case INT32:
1459             case SINT32:
1460             case SFIXED32:
1461               defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
1462               break;
1463             case UINT32:
1464             case FIXED32:
1465               defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
1466               break;
1467             case INT64:
1468             case SINT64:
1469             case SFIXED64:
1470               defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
1471               break;
1472             case UINT64:
1473             case FIXED64:
1474               defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
1475               break;
1476             case FLOAT:
1477               if (proto.getDefaultValue().equals("inf")) {
1478                 defaultValue = Float.POSITIVE_INFINITY;
1479               } else if (proto.getDefaultValue().equals("-inf")) {
1480                 defaultValue = Float.NEGATIVE_INFINITY;
1481               } else if (proto.getDefaultValue().equals("nan")) {
1482                 defaultValue = Float.NaN;
1483               } else {
1484                 defaultValue = Float.valueOf(proto.getDefaultValue());
1485               }
1486               break;
1487             case DOUBLE:
1488               if (proto.getDefaultValue().equals("inf")) {
1489                 defaultValue = Double.POSITIVE_INFINITY;
1490               } else if (proto.getDefaultValue().equals("-inf")) {
1491                 defaultValue = Double.NEGATIVE_INFINITY;
1492               } else if (proto.getDefaultValue().equals("nan")) {
1493                 defaultValue = Double.NaN;
1494               } else {
1495                 defaultValue = Double.valueOf(proto.getDefaultValue());
1496               }
1497               break;
1498             case BOOL:
1499               defaultValue = Boolean.valueOf(proto.getDefaultValue());
1500               break;
1501             case STRING:
1502               defaultValue = proto.getDefaultValue();
1503               break;
1504             case BYTES:
1505               try {
1506                 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue());
1507               } catch (TextFormat.InvalidEscapeSequenceException e) {
1508                 throw new DescriptorValidationException(
1509                     this, "Couldn't parse default value: " + e.getMessage(), e);
1510               }
1511               break;
1512             case ENUM:
1513               defaultValue = enumType.findValueByName(proto.getDefaultValue());
1514               if (defaultValue == null) {
1515                 throw new DescriptorValidationException(
1516                     this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"');
1517               }
1518               break;
1519             case MESSAGE:
1520             case GROUP:
1521               throw new DescriptorValidationException(this, "Message type had default value.");
1522           }
1523         } catch (NumberFormatException e) {
1524           throw new DescriptorValidationException(
1525               this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e);
1526         }
1527       } else {
1528         // Determine the default default for this field.
1529         if (isRepeated()) {
1530           defaultValue = Collections.emptyList();
1531         } else {
1532           switch (getJavaType()) {
1533             case ENUM:
1534               // We guarantee elsewhere that an enum type always has at least
1535               // one possible value.
1536               defaultValue = enumType.getValues().get(0);
1537               break;
1538             case MESSAGE:
1539               defaultValue = null;
1540               break;
1541             default:
1542               defaultValue = getJavaType().defaultDefault;
1543               break;
1544           }
1545         }
1546       }
1547
1548       if (!isExtension()) {
1549         file.pool.addFieldByNumber(this);
1550       }
1551
1552       if (containingType != null && containingType.getOptions().getMessageSetWireFormat()) {
1553         if (isExtension()) {
1554           if (!isOptional() || getType() != Type.MESSAGE) {
1555             throw new DescriptorValidationException(
1556                 this, "Extensions of MessageSets must be optional messages.");
1557           }
1558         } else {
1559           throw new DescriptorValidationException(
1560               this, "MessageSets cannot have fields, only extensions.");
1561         }
1562       }
1563     }
1564
1565     /** See {@link FileDescriptor#setProto}. */
1566     private void setProto(final FieldDescriptorProto proto) {
1567       this.proto = proto;
1568     }
1569
1570     /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */
1571     @Override
1572     public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
1573       // FieldDescriptors are only used with non-lite messages so we can just
1574       // down-cast and call mergeFrom directly.
1575       return ((Message.Builder) to).mergeFrom((Message) from);
1576     }
1577
1578   }
1579
1580   // =================================================================
1581
1582   /** Describes an enum type. */
1583   public static final class EnumDescriptor extends GenericDescriptor
1584       implements Internal.EnumLiteMap<EnumValueDescriptor> {
1585     /**
1586      * Get the index of this descriptor within its parent.
1587      *
1588      * @see Descriptors.Descriptor#getIndex()
1589      */
1590     public int getIndex() {
1591       return index;
1592     }
1593
1594     /** Convert the descriptor to its protocol message representation. */
1595     @Override
1596     public EnumDescriptorProto toProto() {
1597       return proto;
1598     }
1599
1600     /** Get the type's unqualified name. */
1601     @Override
1602     public String getName() {
1603       return proto.getName();
1604     }
1605
1606     /**
1607      * Get the type's fully-qualified name.
1608      *
1609      * @see Descriptors.Descriptor#getFullName()
1610      */
1611     @Override
1612     public String getFullName() {
1613       return fullName;
1614     }
1615
1616     /** Get the {@link FileDescriptor} containing this descriptor. */
1617     @Override
1618     public FileDescriptor getFile() {
1619       return file;
1620     }
1621
1622     /** If this is a nested type, get the outer descriptor, otherwise null. */
1623     public Descriptor getContainingType() {
1624       return containingType;
1625     }
1626
1627     /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
1628     public EnumOptions getOptions() {
1629       return proto.getOptions();
1630     }
1631
1632     /** Get a list of defined values for this enum. */
1633     public List<EnumValueDescriptor> getValues() {
1634       return Collections.unmodifiableList(Arrays.asList(values));
1635     }
1636
1637     /**
1638      * Find an enum value by name.
1639      *
1640      * @param name The unqualified name of the value (e.g. "FOO").
1641      * @return the value's descriptor, or {@code null} if not found.
1642      */
1643     public EnumValueDescriptor findValueByName(final String name) {
1644       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
1645       if (result != null && result instanceof EnumValueDescriptor) {
1646         return (EnumValueDescriptor) result;
1647       } else {
1648         return null;
1649       }
1650     }
1651
1652     /**
1653      * Find an enum value by number. If multiple enum values have the same number, this returns the
1654      * first defined value with that number.
1655      *
1656      * @param number The value's number.
1657      * @return the value's descriptor, or {@code null} if not found.
1658      */
1659     @Override
1660     public EnumValueDescriptor findValueByNumber(final int number) {
1661       return file.pool.enumValuesByNumber.get(new DescriptorPool.DescriptorIntPair(this, number));
1662     }
1663
1664     /**
1665      * Get the enum value for a number. If no enum value has this number, construct an
1666      * EnumValueDescriptor for it.
1667      */
1668     public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) {
1669       EnumValueDescriptor result = findValueByNumber(number);
1670       if (result != null) {
1671         return result;
1672       }
1673       // The number represents an unknown enum value.
1674       synchronized (this) {
1675         // Descriptors are compared by object identity so for the same number
1676         // we need to return the same EnumValueDescriptor object. This means
1677         // we have to store created EnumValueDescriptors. However, as there
1678         // are potentially 2G unknown enum values, storing all of these
1679         // objects persistently will consume lots of memory for long-running
1680         // services and it's also unnecessary as not many EnumValueDescriptors
1681         // will be used at the same time.
1682         //
1683         // To solve the problem we take advantage of Java's weak references and
1684         // rely on gc to release unused descriptors.
1685         //
1686         // Here is how it works:
1687         //   * We store unknown EnumValueDescriptors in a WeakHashMap with the
1688         //     value being a weak reference to the descriptor.
1689         //   * The descriptor holds a strong reference to the key so as long
1690         //     as the EnumValueDescriptor is in use, the key will be there
1691         //     and the corresponding map entry will be there. Following-up
1692         //     queries with the same number will return the same descriptor.
1693         //   * If the user no longer uses an unknown EnumValueDescriptor,
1694         //     it will be gc-ed since we only hold a weak reference to it in
1695         //     the map. The key in the corresponding map entry will also be
1696         //     gc-ed as the only strong reference to it is in the descriptor
1697         //     which is just gc-ed. With the key being gone WeakHashMap will
1698         //     then remove the whole entry. This way unknown descriptors will
1699         //     be freed automatically and we don't need to do anything to
1700         //     clean-up unused map entries.
1701
1702         // Note: We must use "new Integer(number)" here because we don't want
1703         // these Integer objects to be cached.
1704         Integer key = new Integer(number);
1705         WeakReference<EnumValueDescriptor> reference = unknownValues.get(key);
1706         if (reference != null) {
1707           result = reference.get();
1708         }
1709         if (result == null) {
1710           result = new EnumValueDescriptor(file, this, key);
1711           unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result));
1712         }
1713       }
1714       return result;
1715     }
1716
1717     // Used in tests only.
1718     int getUnknownEnumValueDescriptorCount() {
1719       return unknownValues.size();
1720     }
1721
1722     private final int index;
1723     private EnumDescriptorProto proto;
1724     private final String fullName;
1725     private final FileDescriptor file;
1726     private final Descriptor containingType;
1727     private EnumValueDescriptor[] values;
1728     private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues =
1729         new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>();
1730
1731     private EnumDescriptor(
1732         final EnumDescriptorProto proto,
1733         final FileDescriptor file,
1734         final Descriptor parent,
1735         final int index)
1736         throws DescriptorValidationException {
1737       this.index = index;
1738       this.proto = proto;
1739       fullName = computeFullName(file, parent, proto.getName());
1740       this.file = file;
1741       containingType = parent;
1742
1743       if (proto.getValueCount() == 0) {
1744         // We cannot allow enums with no values because this would mean there
1745         // would be no valid default value for fields of this type.
1746         throw new DescriptorValidationException(this, "Enums must contain at least one value.");
1747       }
1748
1749       values = new EnumValueDescriptor[proto.getValueCount()];
1750       for (int i = 0; i < proto.getValueCount(); i++) {
1751         values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i);
1752       }
1753
1754       file.pool.addSymbol(this);
1755     }
1756
1757     /** See {@link FileDescriptor#setProto}. */
1758     private void setProto(final EnumDescriptorProto proto) {
1759       this.proto = proto;
1760
1761       for (int i = 0; i < values.length; i++) {
1762         values[i].setProto(proto.getValue(i));
1763       }
1764     }
1765   }
1766
1767   // =================================================================
1768
1769   /**
1770    * Describes one value within an enum type. Note that multiple defined values may have the same
1771    * number. In generated Java code, all values with the same number after the first become aliases
1772    * of the first. However, they still have independent EnumValueDescriptors.
1773    */
1774   public static final class EnumValueDescriptor extends GenericDescriptor
1775       implements Internal.EnumLite {
1776     /**
1777      * Get the index of this descriptor within its parent.
1778      *
1779      * @see Descriptors.Descriptor#getIndex()
1780      */
1781     public int getIndex() {
1782       return index;
1783     }
1784
1785     /** Convert the descriptor to its protocol message representation. */
1786     @Override
1787     public EnumValueDescriptorProto toProto() {
1788       return proto;
1789     }
1790
1791     /** Get the value's unqualified name. */
1792     @Override
1793     public String getName() {
1794       return proto.getName();
1795     }
1796
1797     /** Get the value's number. */
1798     @Override
1799     public int getNumber() {
1800       return proto.getNumber();
1801     }
1802
1803     @Override
1804     public String toString() {
1805       return proto.getName();
1806     }
1807
1808     /**
1809      * Get the value's fully-qualified name.
1810      *
1811      * @see Descriptors.Descriptor#getFullName()
1812      */
1813     @Override
1814     public String getFullName() {
1815       return fullName;
1816     }
1817
1818     /** Get the {@link FileDescriptor} containing this descriptor. */
1819     @Override
1820     public FileDescriptor getFile() {
1821       return file;
1822     }
1823
1824     /** Get the value's enum type. */
1825     public EnumDescriptor getType() {
1826       return type;
1827     }
1828
1829     /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */
1830     public EnumValueOptions getOptions() {
1831       return proto.getOptions();
1832     }
1833
1834     private final int index;
1835     private EnumValueDescriptorProto proto;
1836     private final String fullName;
1837     private final FileDescriptor file;
1838     private final EnumDescriptor type;
1839
1840     private EnumValueDescriptor(
1841         final EnumValueDescriptorProto proto,
1842         final FileDescriptor file,
1843         final EnumDescriptor parent,
1844         final int index)
1845         throws DescriptorValidationException {
1846       this.index = index;
1847       this.proto = proto;
1848       this.file = file;
1849       type = parent;
1850
1851       fullName = parent.getFullName() + '.' + proto.getName();
1852
1853       file.pool.addSymbol(this);
1854       file.pool.addEnumValueByNumber(this);
1855     }
1856
1857     // Create an unknown enum value.
1858     private EnumValueDescriptor(
1859         final FileDescriptor file, final EnumDescriptor parent, final Integer number) {
1860       String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number;
1861       EnumValueDescriptorProto proto =
1862           EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build();
1863       this.index = -1;
1864       this.proto = proto;
1865       this.file = file;
1866       this.type = parent;
1867       this.fullName = parent.getFullName() + '.' + proto.getName();
1868
1869       // Don't add this descriptor into pool.
1870     }
1871
1872     /** See {@link FileDescriptor#setProto}. */
1873     private void setProto(final EnumValueDescriptorProto proto) {
1874       this.proto = proto;
1875     }
1876   }
1877
1878   // =================================================================
1879
1880   /** Describes a service type. */
1881   public static final class ServiceDescriptor extends GenericDescriptor {
1882     /**
1883      * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
1884      */
1885     public int getIndex() {
1886       return index;
1887     }
1888
1889     /** Convert the descriptor to its protocol message representation. */
1890     @Override
1891     public ServiceDescriptorProto toProto() {
1892       return proto;
1893     }
1894
1895     /** Get the type's unqualified name. */
1896     @Override
1897     public String getName() {
1898       return proto.getName();
1899     }
1900
1901     /**
1902      * Get the type's fully-qualified name.
1903      *
1904      * @see Descriptors.Descriptor#getFullName()
1905      */
1906     @Override
1907     public String getFullName() {
1908       return fullName;
1909     }
1910
1911     /** Get the {@link FileDescriptor} containing this descriptor. */
1912     @Override
1913     public FileDescriptor getFile() {
1914       return file;
1915     }
1916
1917     /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
1918     public ServiceOptions getOptions() {
1919       return proto.getOptions();
1920     }
1921
1922     /** Get a list of methods for this service. */
1923     public List<MethodDescriptor> getMethods() {
1924       return Collections.unmodifiableList(Arrays.asList(methods));
1925     }
1926
1927     /**
1928      * Find a method by name.
1929      *
1930      * @param name The unqualified name of the method (e.g. "Foo").
1931      * @return the method's descriptor, or {@code null} if not found.
1932      */
1933     public MethodDescriptor findMethodByName(final String name) {
1934       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
1935       if (result != null && result instanceof MethodDescriptor) {
1936         return (MethodDescriptor) result;
1937       } else {
1938         return null;
1939       }
1940     }
1941
1942     private final int index;
1943     private ServiceDescriptorProto proto;
1944     private final String fullName;
1945     private final FileDescriptor file;
1946     private MethodDescriptor[] methods;
1947
1948     private ServiceDescriptor(
1949         final ServiceDescriptorProto proto, final FileDescriptor file, final int index)
1950         throws DescriptorValidationException {
1951       this.index = index;
1952       this.proto = proto;
1953       fullName = computeFullName(file, null, proto.getName());
1954       this.file = file;
1955
1956       methods = new MethodDescriptor[proto.getMethodCount()];
1957       for (int i = 0; i < proto.getMethodCount(); i++) {
1958         methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i);
1959       }
1960
1961       file.pool.addSymbol(this);
1962     }
1963
1964     private void crossLink() throws DescriptorValidationException {
1965       for (final MethodDescriptor method : methods) {
1966         method.crossLink();
1967       }
1968     }
1969
1970     /** See {@link FileDescriptor#setProto}. */
1971     private void setProto(final ServiceDescriptorProto proto) {
1972       this.proto = proto;
1973
1974       for (int i = 0; i < methods.length; i++) {
1975         methods[i].setProto(proto.getMethod(i));
1976       }
1977     }
1978   }
1979
1980   // =================================================================
1981
1982   /** Describes one method within a service type. */
1983   public static final class MethodDescriptor extends GenericDescriptor {
1984     /**
1985      * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
1986      */
1987     public int getIndex() {
1988       return index;
1989     }
1990
1991     /** Convert the descriptor to its protocol message representation. */
1992     @Override
1993     public MethodDescriptorProto toProto() {
1994       return proto;
1995     }
1996
1997     /** Get the method's unqualified name. */
1998     @Override
1999     public String getName() {
2000       return proto.getName();
2001     }
2002
2003     /**
2004      * Get the method's fully-qualified name.
2005      *
2006      * @see Descriptors.Descriptor#getFullName()
2007      */
2008     @Override
2009     public String getFullName() {
2010       return fullName;
2011     }
2012
2013     /** Get the {@link FileDescriptor} containing this descriptor. */
2014     @Override
2015     public FileDescriptor getFile() {
2016       return file;
2017     }
2018
2019     /** Get the method's service type. */
2020     public ServiceDescriptor getService() {
2021       return service;
2022     }
2023
2024     /** Get the method's input type. */
2025     public Descriptor getInputType() {
2026       return inputType;
2027     }
2028
2029     /** Get the method's output type. */
2030     public Descriptor getOutputType() {
2031       return outputType;
2032     }
2033
2034     /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */
2035     public MethodOptions getOptions() {
2036       return proto.getOptions();
2037     }
2038
2039     private final int index;
2040     private MethodDescriptorProto proto;
2041     private final String fullName;
2042     private final FileDescriptor file;
2043     private final ServiceDescriptor service;
2044
2045     // Initialized during cross-linking.
2046     private Descriptor inputType;
2047     private Descriptor outputType;
2048
2049     private MethodDescriptor(
2050         final MethodDescriptorProto proto,
2051         final FileDescriptor file,
2052         final ServiceDescriptor parent,
2053         final int index)
2054         throws DescriptorValidationException {
2055       this.index = index;
2056       this.proto = proto;
2057       this.file = file;
2058       service = parent;
2059
2060       fullName = parent.getFullName() + '.' + proto.getName();
2061
2062       file.pool.addSymbol(this);
2063     }
2064
2065     private void crossLink() throws DescriptorValidationException {
2066       final GenericDescriptor input =
2067           file.pool.lookupSymbol(
2068               proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2069       if (!(input instanceof Descriptor)) {
2070         throw new DescriptorValidationException(
2071             this, '\"' + proto.getInputType() + "\" is not a message type.");
2072       }
2073       inputType = (Descriptor) input;
2074
2075       final GenericDescriptor output =
2076           file.pool.lookupSymbol(
2077               proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2078       if (!(output instanceof Descriptor)) {
2079         throw new DescriptorValidationException(
2080             this, '\"' + proto.getOutputType() + "\" is not a message type.");
2081       }
2082       outputType = (Descriptor) output;
2083     }
2084
2085     /** See {@link FileDescriptor#setProto}. */
2086     private void setProto(final MethodDescriptorProto proto) {
2087       this.proto = proto;
2088     }
2089   }
2090
2091   // =================================================================
2092
2093   private static String computeFullName(
2094       final FileDescriptor file, final Descriptor parent, final String name) {
2095     if (parent != null) {
2096       return parent.getFullName() + '.' + name;
2097     }
2098
2099     final String packageName = file.getPackage();
2100     if (!packageName.isEmpty()) {
2101       return packageName + '.' + name;
2102     }
2103
2104     return name;
2105   }
2106
2107   // =================================================================
2108
2109   /**
2110    * All descriptors implement this to make it easier to implement tools like {@code
2111    * DescriptorPool}.
2112    *
2113    * <p>This class is public so that the methods it exposes can be called from outside of this
2114    * package. However, it should only be subclassed from nested classes of Descriptors.
2115    */
2116   public abstract static class GenericDescriptor {
2117     public abstract Message toProto();
2118
2119     public abstract String getName();
2120
2121     public abstract String getFullName();
2122
2123     public abstract FileDescriptor getFile();
2124   }
2125
2126   /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */
2127   public static class DescriptorValidationException extends Exception {
2128     private static final long serialVersionUID = 5750205775490483148L;
2129
2130     /** Gets the full name of the descriptor where the error occurred. */
2131     public String getProblemSymbolName() {
2132       return name;
2133     }
2134
2135     /** Gets the protocol message representation of the invalid descriptor. */
2136     public Message getProblemProto() {
2137       return proto;
2138     }
2139
2140     /** Gets a human-readable description of the error. */
2141     public String getDescription() {
2142       return description;
2143     }
2144
2145     private final String name;
2146     private final Message proto;
2147     private final String description;
2148
2149     private DescriptorValidationException(
2150         final GenericDescriptor problemDescriptor, final String description) {
2151       super(problemDescriptor.getFullName() + ": " + description);
2152
2153       // Note that problemDescriptor may be partially uninitialized, so we
2154       // don't want to expose it directly to the user.  So, we only provide
2155       // the name and the original proto.
2156       name = problemDescriptor.getFullName();
2157       proto = problemDescriptor.toProto();
2158       this.description = description;
2159     }
2160
2161     private DescriptorValidationException(
2162         final GenericDescriptor problemDescriptor,
2163         final String description,
2164         final Throwable cause) {
2165       this(problemDescriptor, description);
2166       initCause(cause);
2167     }
2168
2169     private DescriptorValidationException(
2170         final FileDescriptor problemDescriptor, final String description) {
2171       super(problemDescriptor.getName() + ": " + description);
2172
2173       // Note that problemDescriptor may be partially uninitialized, so we
2174       // don't want to expose it directly to the user.  So, we only provide
2175       // the name and the original proto.
2176       name = problemDescriptor.getName();
2177       proto = problemDescriptor.toProto();
2178       this.description = description;
2179     }
2180   }
2181
2182   // =================================================================
2183
2184   /**
2185    * A private helper class which contains lookup tables containing all the descriptors defined in a
2186    * particular file.
2187    */
2188   private static final class DescriptorPool {
2189
2190     /** Defines what subclass of descriptors to search in the descriptor pool. */
2191     enum SearchFilter {
2192       TYPES_ONLY,
2193       AGGREGATES_ONLY,
2194       ALL_SYMBOLS
2195     }
2196
2197     DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) {
2198       this.dependencies = new HashSet<FileDescriptor>();
2199       this.allowUnknownDependencies = allowUnknownDependencies;
2200
2201       for (int i = 0; i < dependencies.length; i++) {
2202         this.dependencies.add(dependencies[i]);
2203         importPublicDependencies(dependencies[i]);
2204       }
2205
2206       for (final FileDescriptor dependency : this.dependencies) {
2207         try {
2208           addPackage(dependency.getPackage(), dependency);
2209         } catch (DescriptorValidationException e) {
2210           // Can't happen, because addPackage() only fails when the name
2211           // conflicts with a non-package, but we have not yet added any
2212           // non-packages at this point.
2213           throw new AssertionError(e);
2214         }
2215       }
2216     }
2217
2218     /** Find and put public dependencies of the file into dependencies set. */
2219     private void importPublicDependencies(final FileDescriptor file) {
2220       for (FileDescriptor dependency : file.getPublicDependencies()) {
2221         if (dependencies.add(dependency)) {
2222           importPublicDependencies(dependency);
2223         }
2224       }
2225     }
2226
2227     private final Set<FileDescriptor> dependencies;
2228     private boolean allowUnknownDependencies;
2229
2230     private final Map<String, GenericDescriptor> descriptorsByName =
2231         new HashMap<String, GenericDescriptor>();
2232     private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
2233         new HashMap<DescriptorIntPair, FieldDescriptor>();
2234     private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
2235         new HashMap<DescriptorIntPair, EnumValueDescriptor>();
2236
2237     /** Find a generic descriptor by fully-qualified name. */
2238     GenericDescriptor findSymbol(final String fullName) {
2239       return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
2240     }
2241
2242     /**
2243      * Find a descriptor by fully-qualified name and given option to only search valid field type
2244      * descriptors.
2245      */
2246     GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) {
2247       GenericDescriptor result = descriptorsByName.get(fullName);
2248       if (result != null) {
2249         if ((filter == SearchFilter.ALL_SYMBOLS)
2250             || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2251             || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2252           return result;
2253         }
2254       }
2255
2256       for (final FileDescriptor dependency : dependencies) {
2257         result = dependency.pool.descriptorsByName.get(fullName);
2258         if (result != null) {
2259           if ((filter == SearchFilter.ALL_SYMBOLS)
2260               || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2261               || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2262             return result;
2263           }
2264         }
2265       }
2266
2267       return null;
2268     }
2269
2270     /** Checks if the descriptor is a valid type for a message field. */
2271     boolean isType(GenericDescriptor descriptor) {
2272       return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor);
2273     }
2274
2275     /** Checks if the descriptor is a valid namespace type. */
2276     boolean isAggregate(GenericDescriptor descriptor) {
2277       return (descriptor instanceof Descriptor)
2278           || (descriptor instanceof EnumDescriptor)
2279           || (descriptor instanceof PackageDescriptor)
2280           || (descriptor instanceof ServiceDescriptor);
2281     }
2282
2283     /**
2284      * Look up a type descriptor by name, relative to some other descriptor. The name may be
2285      * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name
2286      * lookup semantics are used to search for the matching descriptor.
2287      */
2288     GenericDescriptor lookupSymbol(
2289         final String name,
2290         final GenericDescriptor relativeTo,
2291         final DescriptorPool.SearchFilter filter)
2292         throws DescriptorValidationException {
2293       // TODO(kenton):  This could be optimized in a number of ways.
2294
2295       GenericDescriptor result;
2296       String fullname;
2297       if (name.startsWith(".")) {
2298         // Fully-qualified name.
2299         fullname = name.substring(1);
2300         result = findSymbol(fullname, filter);
2301       } else {
2302         // If "name" is a compound identifier, we want to search for the
2303         // first component of it, then search within it for the rest.
2304         // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
2305         // defined in multiple parent scopes, we only want to find "Bar.baz" in
2306         // the innermost one.  E.g., the following should produce an error:
2307         //   message Bar { message Baz {} }
2308         //   message Foo {
2309         //     message Bar {
2310         //     }
2311         //     optional Bar.Baz baz = 1;
2312         //   }
2313         // So, we look for just "Foo" first, then look for "Bar.baz" within it
2314         // if found.
2315         final int firstPartLength = name.indexOf('.');
2316         final String firstPart;
2317         if (firstPartLength == -1) {
2318           firstPart = name;
2319         } else {
2320           firstPart = name.substring(0, firstPartLength);
2321         }
2322
2323         // We will search each parent scope of "relativeTo" looking for the
2324         // symbol.
2325         final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName());
2326
2327         while (true) {
2328           // Chop off the last component of the scope.
2329           final int dotpos = scopeToTry.lastIndexOf(".");
2330           if (dotpos == -1) {
2331             fullname = name;
2332             result = findSymbol(name, filter);
2333             break;
2334           } else {
2335             scopeToTry.setLength(dotpos + 1);
2336
2337             // Append firstPart and try to find
2338             scopeToTry.append(firstPart);
2339             result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY);
2340
2341             if (result != null) {
2342               if (firstPartLength != -1) {
2343                 // We only found the first part of the symbol.  Now look for
2344                 // the whole thing.  If this fails, we *don't* want to keep
2345                 // searching parent scopes.
2346                 scopeToTry.setLength(dotpos + 1);
2347                 scopeToTry.append(name);
2348                 result = findSymbol(scopeToTry.toString(), filter);
2349               }
2350               fullname = scopeToTry.toString();
2351               break;
2352             }
2353
2354             // Not found.  Remove the name so we can try again.
2355             scopeToTry.setLength(dotpos);
2356           }
2357         }
2358       }
2359
2360       if (result == null) {
2361         if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) {
2362           logger.warning(
2363               "The descriptor for message type \""
2364                   + name
2365                   + "\" can not be found and a placeholder is created for it");
2366           // We create a dummy message descriptor here regardless of the
2367           // expected type. If the type should be message, this dummy
2368           // descriptor will work well and if the type should be enum, a
2369           // DescriptorValidationException will be thrown latter. In either
2370           // case, the code works as expected: we allow unknown message types
2371           // but not unknown enum types.
2372           result = new Descriptor(fullname);
2373           // Add the placeholder file as a dependency so we can find the
2374           // placeholder symbol when resolving other references.
2375           this.dependencies.add(result.getFile());
2376           return result;
2377         } else {
2378           throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined.");
2379         }
2380       } else {
2381         return result;
2382       }
2383     }
2384
2385     /**
2386      * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an
2387      * error.
2388      */
2389     void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException {
2390       validateSymbolName(descriptor);
2391
2392       final String fullName = descriptor.getFullName();
2393
2394       final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
2395       if (old != null) {
2396         descriptorsByName.put(fullName, old);
2397
2398         if (descriptor.getFile() == old.getFile()) {
2399           final int dotpos = fullName.lastIndexOf('.');
2400           if (dotpos == -1) {
2401             throw new DescriptorValidationException(
2402                 descriptor, '\"' + fullName + "\" is already defined.");
2403           } else {
2404             throw new DescriptorValidationException(
2405                 descriptor,
2406                 '\"'
2407                     + fullName.substring(dotpos + 1)
2408                     + "\" is already defined in \""
2409                     + fullName.substring(0, dotpos)
2410                     + "\".");
2411           }
2412         } else {
2413           throw new DescriptorValidationException(
2414               descriptor,
2415               '\"'
2416                   + fullName
2417                   + "\" is already defined in file \""
2418                   + old.getFile().getName()
2419                   + "\".");
2420         }
2421       }
2422     }
2423
2424     /**
2425      * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so
2426      * that someone cannot define, say, a message type that has the same name as an existing
2427      * package.
2428      */
2429     private static final class PackageDescriptor extends GenericDescriptor {
2430       @Override
2431       public Message toProto() {
2432         return file.toProto();
2433       }
2434
2435       @Override
2436       public String getName() {
2437         return name;
2438       }
2439
2440       @Override
2441       public String getFullName() {
2442         return fullName;
2443       }
2444
2445       @Override
2446       public FileDescriptor getFile() {
2447         return file;
2448       }
2449
2450       PackageDescriptor(final String name, final String fullName, final FileDescriptor file) {
2451         this.file = file;
2452         this.fullName = fullName;
2453         this.name = name;
2454       }
2455
2456       private final String name;
2457       private final String fullName;
2458       private final FileDescriptor file;
2459     }
2460
2461     /**
2462      * Adds a package to the symbol tables. If a package by the same name already exists, that is
2463      * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If
2464      * the package has multiple components, this also adds the parent package(s).
2465      */
2466     void addPackage(final String fullName, final FileDescriptor file)
2467         throws DescriptorValidationException {
2468       final int dotpos = fullName.lastIndexOf('.');
2469       final String name;
2470       if (dotpos == -1) {
2471         name = fullName;
2472       } else {
2473         addPackage(fullName.substring(0, dotpos), file);
2474         name = fullName.substring(dotpos + 1);
2475       }
2476
2477       final GenericDescriptor old =
2478           descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file));
2479       if (old != null) {
2480         descriptorsByName.put(fullName, old);
2481         if (!(old instanceof PackageDescriptor)) {
2482           throw new DescriptorValidationException(
2483               file,
2484               '\"'
2485                   + name
2486                   + "\" is already defined (as something other than a "
2487                   + "package) in file \""
2488                   + old.getFile().getName()
2489                   + "\".");
2490         }
2491       }
2492     }
2493
2494     /** A (GenericDescriptor, int) pair, used as a map key. */
2495     private static final class DescriptorIntPair {
2496       private final GenericDescriptor descriptor;
2497       private final int number;
2498
2499       DescriptorIntPair(final GenericDescriptor descriptor, final int number) {
2500         this.descriptor = descriptor;
2501         this.number = number;
2502       }
2503
2504       @Override
2505       public int hashCode() {
2506         return descriptor.hashCode() * ((1 << 16) - 1) + number;
2507       }
2508
2509       @Override
2510       public boolean equals(final Object obj) {
2511         if (!(obj instanceof DescriptorIntPair)) {
2512           return false;
2513         }
2514         final DescriptorIntPair other = (DescriptorIntPair) obj;
2515         return descriptor == other.descriptor && number == other.number;
2516       }
2517     }
2518
2519     /**
2520      * Adds a field to the fieldsByNumber table. Throws an exception if a field with the same
2521      * containing type and number already exists.
2522      */
2523     void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException {
2524       final DescriptorIntPair key =
2525           new DescriptorIntPair(field.getContainingType(), field.getNumber());
2526       final FieldDescriptor old = fieldsByNumber.put(key, field);
2527       if (old != null) {
2528         fieldsByNumber.put(key, old);
2529         throw new DescriptorValidationException(
2530             field,
2531             "Field number "
2532                 + field.getNumber()
2533                 + " has already been used in \""
2534                 + field.getContainingType().getFullName()
2535                 + "\" by field \""
2536                 + old.getName()
2537                 + "\".");
2538       }
2539     }
2540
2541     /**
2542      * Adds an enum value to the enumValuesByNumber table. If an enum value with the same type and
2543      * number already exists, does nothing. (This is allowed; the first value define with the number
2544      * takes precedence.)
2545      */
2546     void addEnumValueByNumber(final EnumValueDescriptor value) {
2547       final DescriptorIntPair key = new DescriptorIntPair(value.getType(), value.getNumber());
2548       final EnumValueDescriptor old = enumValuesByNumber.put(key, value);
2549       if (old != null) {
2550         enumValuesByNumber.put(key, old);
2551         // Not an error:  Multiple enum values may have the same number, but
2552         // we only want the first one in the map.
2553       }
2554     }
2555
2556     /**
2557      * Verifies that the descriptor's name is valid (i.e. it contains only letters, digits, and
2558      * underscores, and does not start with a digit).
2559      */
2560     static void validateSymbolName(final GenericDescriptor descriptor)
2561         throws DescriptorValidationException {
2562       final String name = descriptor.getName();
2563       if (name.length() == 0) {
2564         throw new DescriptorValidationException(descriptor, "Missing name.");
2565       }
2566
2567       // Non-ASCII characters are not valid in protobuf identifiers, even
2568       // if they are letters or digits.
2569       // The first character must be a letter or '_'.
2570       // Subsequent characters may be letters, numbers, or digits.
2571       for (int i = 0; i < name.length(); i++) {
2572         final char c = name.charAt(i);
2573         if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
2574           || (c == '_')
2575           || ('0' <= c && c <= '9' && i > 0)) {
2576           // Valid
2577           continue;
2578         }
2579         throw new DescriptorValidationException(
2580             descriptor, '\"' + name + "\" is not a valid identifier.");
2581       }
2582     }
2583   }
2584
2585   /** Describes an oneof of a message type. */
2586   public static final class OneofDescriptor {
2587     /** Get the index of this descriptor within its parent. */
2588     public int getIndex() {
2589       return index;
2590     }
2591
2592     public String getName() {
2593       return proto.getName();
2594     }
2595
2596     public FileDescriptor getFile() {
2597       return file;
2598     }
2599
2600     public String getFullName() {
2601       return fullName;
2602     }
2603
2604     public Descriptor getContainingType() {
2605       return containingType;
2606     }
2607
2608     public int getFieldCount() {
2609       return fieldCount;
2610     }
2611
2612     public OneofOptions getOptions() {
2613       return proto.getOptions();
2614     }
2615
2616     /** Get a list of this message type's fields. */
2617     public List<FieldDescriptor> getFields() {
2618       return Collections.unmodifiableList(Arrays.asList(fields));
2619     }
2620
2621     public FieldDescriptor getField(int index) {
2622       return fields[index];
2623     }
2624
2625     private void setProto(final OneofDescriptorProto proto) {
2626       this.proto = proto;
2627     }
2628
2629     private OneofDescriptor(
2630         final OneofDescriptorProto proto,
2631         final FileDescriptor file,
2632         final Descriptor parent,
2633         final int index)
2634         throws DescriptorValidationException {
2635       this.proto = proto;
2636       fullName = computeFullName(file, parent, proto.getName());
2637       this.file = file;
2638       this.index = index;
2639
2640       containingType = parent;
2641       fieldCount = 0;
2642     }
2643
2644     private final int index;
2645     private OneofDescriptorProto proto;
2646     private final String fullName;
2647     private final FileDescriptor file;
2648
2649     private Descriptor containingType;
2650     private int fieldCount;
2651     private FieldDescriptor[] fields;
2652   }
2653 }