• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
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
toProto()90     public FileDescriptorProto toProto() {
91       return proto;
92     }
93 
94     /** Get the file name. */
95     @Override
getName()96     public String getName() {
97       return proto.getName();
98     }
99 
100     /** Returns this object. */
101     @Override
getFile()102     public FileDescriptor getFile() {
103       return this;
104     }
105 
106     /** Returns the same as getName(). */
107     @Override
getFullName()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      */
getPackage()116     public String getPackage() {
117       return proto.getPackage();
118     }
119 
120     /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
getOptions()121     public FileOptions getOptions() {
122       return proto.getOptions();
123     }
124 
125     /** Get a list of top-level message types declared in this file. */
getMessageTypes()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. */
getEnumTypes()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. */
getServices()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. */
getExtensions()141     public List<FieldDescriptor> getExtensions() {
142       return Collections.unmodifiableList(Arrays.asList(extensions));
143     }
144 
145     /** Get a list of this file's dependencies (imports). */
getDependencies()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). */
getPublicDependencies()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 
Syntax(String name)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. */
getSyntax()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      */
findMessageTypeByName(String name)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      */
findEnumTypeByName(String name)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      */
findServiceByName(String name)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      */
findExtensionByName(String name)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      */
buildFrom( final FileDescriptorProto proto, final FileDescriptor[] dependencies)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      */
buildFrom( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final boolean allowUnknownDependencies)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 
latin1Cat(final String[] strings)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 
findDescriptors( final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)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
internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final FileDescriptor[] dependencies, final InternalDescriptorAssigner descriptorAssigner)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      */
internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final FileDescriptor[] dependencies)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
internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames, final InternalDescriptorAssigner descriptorAssigner)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      */
internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)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      */
internalUpdateFileDescriptor( final FileDescriptor descriptor, final ExtensionRegistry registry)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 {
assignDescriptors(FileDescriptor root)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 
FileDescriptor( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final DescriptorPool pool, boolean allowUnknownDependencies)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. */
FileDescriptor(String packageName, Descriptor message)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. */
crossLink()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      */
setProto(final FileDescriptorProto proto)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 
supportsUnknownEnumValue()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      */
getIndex()646     public int getIndex() {
647       return index;
648     }
649 
650     /** Convert the descriptor to its protocol message representation. */
651     @Override
toProto()652     public DescriptorProto toProto() {
653       return proto;
654     }
655 
656     /** Get the type's unqualified name. */
657     @Override
getName()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
getFullName()675     public String getFullName() {
676       return fullName;
677     }
678 
679     /** Get the {@link FileDescriptor} containing this descriptor. */
680     @Override
getFile()681     public FileDescriptor getFile() {
682       return file;
683     }
684 
685     /** If this is a nested type, get the outer descriptor, otherwise null. */
getContainingType()686     public Descriptor getContainingType() {
687       return containingType;
688     }
689 
690     /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
getOptions()691     public MessageOptions getOptions() {
692       return proto.getOptions();
693     }
694 
695     /** Get a list of this message type's fields. */
getFields()696     public List<FieldDescriptor> getFields() {
697       return Collections.unmodifiableList(Arrays.asList(fields));
698     }
699 
700     /** Get a list of this message type's oneofs. */
getOneofs()701     public List<OneofDescriptor> getOneofs() {
702       return Collections.unmodifiableList(Arrays.asList(oneofs));
703     }
704 
705     /** Get a list of this message type's real oneofs. */
getRealOneofs()706     public List<OneofDescriptor> getRealOneofs() {
707       return Collections.unmodifiableList(Arrays.asList(oneofs).subList(0, realOneofCount));
708     }
709 
710     /** Get a list of this message type's extensions. */
getExtensions()711     public List<FieldDescriptor> getExtensions() {
712       return Collections.unmodifiableList(Arrays.asList(extensions));
713     }
714 
715     /** Get a list of message types nested within this one. */
getNestedTypes()716     public List<Descriptor> getNestedTypes() {
717       return Collections.unmodifiableList(Arrays.asList(nestedTypes));
718     }
719 
720     /** Get a list of enum types nested within this one. */
getEnumTypes()721     public List<EnumDescriptor> getEnumTypes() {
722       return Collections.unmodifiableList(Arrays.asList(enumTypes));
723     }
724 
725     /** Determines if the given field number is an extension. */
isExtensionNumber(final int number)726     public boolean isExtensionNumber(final int number) {
727       for (final DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) {
728         if (range.getStart() <= number && number < range.getEnd()) {
729           return true;
730         }
731       }
732       return false;
733     }
734 
735     /** Determines if the given field number is reserved. */
isReservedNumber(final int number)736     public boolean isReservedNumber(final int number) {
737       for (final DescriptorProto.ReservedRange range : proto.getReservedRangeList()) {
738         if (range.getStart() <= number && number < range.getEnd()) {
739           return true;
740         }
741       }
742       return false;
743     }
744 
745     /** Determines if the given field name is reserved. */
isReservedName(final String name)746     public boolean isReservedName(final String name) {
747       checkNotNull(name);
748       for (final String reservedName : proto.getReservedNameList()) {
749         if (reservedName.equals(name)) {
750           return true;
751         }
752       }
753       return false;
754     }
755 
756     /**
757      * Indicates whether the message can be extended. That is, whether it has any "extensions x to
758      * y" ranges declared on it.
759      */
isExtendable()760     public boolean isExtendable() {
761       return proto.getExtensionRangeList().size() != 0;
762     }
763 
764     /**
765      * Finds a field by name.
766      *
767      * @param name The unqualified name of the field (e.g. "foo"). For protocol buffer messages that
768      *     follow <a
769      *     href=https://developers.google.com/protocol-buffers/docs/style#message_and_field_names>Google's
770      *     guidance on naming</a> this will be a snake case string, such as <pre>song_name</pre>.
771      * @return The field's descriptor, or {@code null} if not found.
772      */
findFieldByName(final String name)773     public FieldDescriptor findFieldByName(final String name) {
774       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
775       if (result != null && result instanceof FieldDescriptor) {
776         return (FieldDescriptor) result;
777       } else {
778         return null;
779       }
780     }
781 
782     /**
783      * Finds a field by field number.
784      *
785      * @param number The field number within this message type.
786      * @return The field's descriptor, or {@code null} if not found.
787      */
findFieldByNumber(final int number)788     public FieldDescriptor findFieldByNumber(final int number) {
789       return file.pool.fieldsByNumber.get(new DescriptorPool.DescriptorIntPair(this, number));
790     }
791 
792     /**
793      * Finds a nested message type by name.
794      *
795      * @param name The unqualified name of the nested type (e.g. "Foo").
796      * @return The types's descriptor, or {@code null} if not found.
797      */
findNestedTypeByName(final String name)798     public Descriptor findNestedTypeByName(final String name) {
799       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
800       if (result != null && result instanceof Descriptor) {
801         return (Descriptor) result;
802       } else {
803         return null;
804       }
805     }
806 
807     /**
808      * Finds a nested enum type by name.
809      *
810      * @param name The unqualified name of the nested type (e.g. "Foo").
811      * @return The types's descriptor, or {@code null} if not found.
812      */
findEnumTypeByName(final String name)813     public EnumDescriptor findEnumTypeByName(final String name) {
814       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
815       if (result != null && result instanceof EnumDescriptor) {
816         return (EnumDescriptor) result;
817       } else {
818         return null;
819       }
820     }
821 
822     private final int index;
823     private DescriptorProto proto;
824     private final String fullName;
825     private final FileDescriptor file;
826     private final Descriptor containingType;
827     private final Descriptor[] nestedTypes;
828     private final EnumDescriptor[] enumTypes;
829     private final FieldDescriptor[] fields;
830     private final FieldDescriptor[] extensions;
831     private final OneofDescriptor[] oneofs;
832     private final int realOneofCount;
833 
834     // Used to create a placeholder when the type cannot be found.
Descriptor(final String fullname)835     Descriptor(final String fullname) throws DescriptorValidationException {
836       String name = fullname;
837       String packageName = "";
838       int pos = fullname.lastIndexOf('.');
839       if (pos != -1) {
840         name = fullname.substring(pos + 1);
841         packageName = fullname.substring(0, pos);
842       }
843       this.index = 0;
844       this.proto =
845           DescriptorProto.newBuilder()
846               .setName(name)
847               .addExtensionRange(
848                   DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build())
849               .build();
850       this.fullName = fullname;
851       this.containingType = null;
852 
853       this.nestedTypes = new Descriptor[0];
854       this.enumTypes = new EnumDescriptor[0];
855       this.fields = new FieldDescriptor[0];
856       this.extensions = new FieldDescriptor[0];
857       this.oneofs = new OneofDescriptor[0];
858       this.realOneofCount = 0;
859 
860       // Create a placeholder FileDescriptor to hold this message.
861       this.file = new FileDescriptor(packageName, this);
862     }
863 
Descriptor( final DescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)864     private Descriptor(
865         final DescriptorProto proto,
866         final FileDescriptor file,
867         final Descriptor parent,
868         final int index)
869         throws DescriptorValidationException {
870       this.index = index;
871       this.proto = proto;
872       fullName = computeFullName(file, parent, proto.getName());
873       this.file = file;
874       containingType = parent;
875 
876       oneofs = new OneofDescriptor[proto.getOneofDeclCount()];
877       for (int i = 0; i < proto.getOneofDeclCount(); i++) {
878         oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i);
879       }
880 
881       nestedTypes = new Descriptor[proto.getNestedTypeCount()];
882       for (int i = 0; i < proto.getNestedTypeCount(); i++) {
883         nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i);
884       }
885 
886       enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
887       for (int i = 0; i < proto.getEnumTypeCount(); i++) {
888         enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i);
889       }
890 
891       fields = new FieldDescriptor[proto.getFieldCount()];
892       for (int i = 0; i < proto.getFieldCount(); i++) {
893         fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false);
894       }
895 
896       extensions = new FieldDescriptor[proto.getExtensionCount()];
897       for (int i = 0; i < proto.getExtensionCount(); i++) {
898         extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true);
899       }
900 
901       for (int i = 0; i < proto.getOneofDeclCount(); i++) {
902         oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()];
903         oneofs[i].fieldCount = 0;
904       }
905       for (int i = 0; i < proto.getFieldCount(); i++) {
906         OneofDescriptor oneofDescriptor = fields[i].getContainingOneof();
907         if (oneofDescriptor != null) {
908           oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
909         }
910       }
911 
912       int syntheticOneofCount = 0;
913       for (OneofDescriptor oneof : this.oneofs) {
914         if (oneof.isSynthetic()) {
915           syntheticOneofCount++;
916         } else {
917           if (syntheticOneofCount > 0) {
918             throw new DescriptorValidationException(this, "Synthetic oneofs must come last.");
919           }
920         }
921       }
922       this.realOneofCount = this.oneofs.length - syntheticOneofCount;
923 
924       file.pool.addSymbol(this);
925     }
926 
927     /** Look up and cross-link all field types, etc. */
crossLink()928     private void crossLink() throws DescriptorValidationException {
929       for (final Descriptor nestedType : nestedTypes) {
930         nestedType.crossLink();
931       }
932 
933       for (final FieldDescriptor field : fields) {
934         field.crossLink();
935       }
936 
937       for (final FieldDescriptor extension : extensions) {
938         extension.crossLink();
939       }
940     }
941 
942     /** See {@link FileDescriptor#setProto}. */
setProto(final DescriptorProto proto)943     private void setProto(final DescriptorProto proto) {
944       this.proto = proto;
945 
946       for (int i = 0; i < nestedTypes.length; i++) {
947         nestedTypes[i].setProto(proto.getNestedType(i));
948       }
949 
950       for (int i = 0; i < oneofs.length; i++) {
951         oneofs[i].setProto(proto.getOneofDecl(i));
952       }
953 
954       for (int i = 0; i < enumTypes.length; i++) {
955         enumTypes[i].setProto(proto.getEnumType(i));
956       }
957 
958       for (int i = 0; i < fields.length; i++) {
959         fields[i].setProto(proto.getField(i));
960       }
961 
962       for (int i = 0; i < extensions.length; i++) {
963         extensions[i].setProto(proto.getExtension(i));
964       }
965     }
966   }
967 
968   // =================================================================
969 
970   /** Describes a field of a message type. */
971   public static final class FieldDescriptor extends GenericDescriptor
972       implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> {
973     /**
974      * Get the index of this descriptor within its parent.
975      *
976      * @see Descriptors.Descriptor#getIndex()
977      */
getIndex()978     public int getIndex() {
979       return index;
980     }
981 
982     /** Convert the descriptor to its protocol message representation. */
983     @Override
toProto()984     public FieldDescriptorProto toProto() {
985       return proto;
986     }
987 
988     /** Get the field's unqualified name. */
989     @Override
getName()990     public String getName() {
991       return proto.getName();
992     }
993 
994     /** Get the field's number. */
995     @Override
getNumber()996     public int getNumber() {
997       return proto.getNumber();
998     }
999 
1000     /**
1001      * Get the field's fully-qualified name.
1002      *
1003      * @see Descriptors.Descriptor#getFullName()
1004      */
1005     @Override
getFullName()1006     public String getFullName() {
1007       return fullName;
1008     }
1009 
1010     /** Get the JSON name of this field. */
getJsonName()1011     public String getJsonName() {
1012       return jsonName;
1013     }
1014 
1015     /**
1016      * Get the field's java type. This is just for convenience. Every {@code
1017      * FieldDescriptorProto.Type} maps to exactly one Java type.
1018      */
getJavaType()1019     public JavaType getJavaType() {
1020       return type.getJavaType();
1021     }
1022 
1023     /** For internal use only. */
1024     @Override
getLiteJavaType()1025     public WireFormat.JavaType getLiteJavaType() {
1026       return getLiteType().getJavaType();
1027     }
1028 
1029     /** Get the {@code FileDescriptor} containing this descriptor. */
1030     @Override
getFile()1031     public FileDescriptor getFile() {
1032       return file;
1033     }
1034 
1035     /** Get the field's declared type. */
getType()1036     public Type getType() {
1037       return type;
1038     }
1039 
1040     /** For internal use only. */
1041     @Override
getLiteType()1042     public WireFormat.FieldType getLiteType() {
1043       return table[type.ordinal()];
1044     }
1045 
1046     /** For internal use only. */
needsUtf8Check()1047     public boolean needsUtf8Check() {
1048       if (type != Type.STRING) {
1049         return false;
1050       }
1051       if (getContainingType().getOptions().getMapEntry()) {
1052         // Always enforce strict UTF-8 checking for map fields.
1053         return true;
1054       }
1055       if (getFile().getSyntax() == Syntax.PROTO3) {
1056         return true;
1057       }
1058       return getFile().getOptions().getJavaStringCheckUtf8();
1059     }
1060 
isMapField()1061     public boolean isMapField() {
1062       return getType() == Type.MESSAGE
1063           && isRepeated()
1064           && getMessageType().getOptions().getMapEntry();
1065     }
1066 
1067     // I'm pretty sure values() constructs a new array every time, since there
1068     // is nothing stopping the caller from mutating the array.  Therefore we
1069     // make a static copy here.
1070     private static final WireFormat.FieldType[] table = WireFormat.FieldType.values();
1071 
1072     /** Is this field declared required? */
isRequired()1073     public boolean isRequired() {
1074       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED;
1075     }
1076 
1077     /** Is this field declared optional? */
isOptional()1078     public boolean isOptional() {
1079       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL;
1080     }
1081 
1082     /** Is this field declared repeated? */
1083     @Override
isRepeated()1084     public boolean isRepeated() {
1085       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
1086     }
1087 
1088     /**
1089      * Does this field have the {@code [packed = true]} option or is this field packable in proto3
1090      * and not explicitly set to unpacked?
1091      */
1092     @Override
isPacked()1093     public boolean isPacked() {
1094       if (!isPackable()) {
1095         return false;
1096       }
1097       if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) {
1098         return getOptions().getPacked();
1099       } else {
1100         return !getOptions().hasPacked() || getOptions().getPacked();
1101       }
1102     }
1103 
1104     /** Can this field be packed? i.e. is it a repeated primitive field? */
isPackable()1105     public boolean isPackable() {
1106       return isRepeated() && getLiteType().isPackable();
1107     }
1108 
1109     /** Returns true if the field had an explicitly-defined default value. */
hasDefaultValue()1110     public boolean hasDefaultValue() {
1111       return proto.hasDefaultValue();
1112     }
1113 
1114     /**
1115      * Returns the field's default value. Valid for all types except for messages and groups. For
1116      * all other types, the object returned is of the same class that would returned by
1117      * Message.getField(this).
1118      */
getDefaultValue()1119     public Object getDefaultValue() {
1120       if (getJavaType() == JavaType.MESSAGE) {
1121         throw new UnsupportedOperationException(
1122             "FieldDescriptor.getDefaultValue() called on an embedded message field.");
1123       }
1124       return defaultValue;
1125     }
1126 
1127     /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
getOptions()1128     public FieldOptions getOptions() {
1129       return proto.getOptions();
1130     }
1131 
1132     /** Is this field an extension? */
isExtension()1133     public boolean isExtension() {
1134       return proto.hasExtendee();
1135     }
1136 
1137     /**
1138      * Get the field's containing type. For extensions, this is the type being extended, not the
1139      * location where the extension was defined. See {@link #getExtensionScope()}.
1140      */
getContainingType()1141     public Descriptor getContainingType() {
1142       return containingType;
1143     }
1144 
1145     /** Get the field's containing oneof. */
getContainingOneof()1146     public OneofDescriptor getContainingOneof() {
1147       return containingOneof;
1148     }
1149 
1150     /** Get the field's containing oneof, only if non-synthetic. */
getRealContainingOneof()1151     public OneofDescriptor getRealContainingOneof() {
1152       return containingOneof != null && !containingOneof.isSynthetic() ? containingOneof : null;
1153     }
1154 
1155     /**
1156      * Returns true if this field was syntactically written with "optional" in the .proto file.
1157      * Excludes singular proto3 fields that do not have a label.
1158      */
hasOptionalKeyword()1159     public boolean hasOptionalKeyword() {
1160       return isProto3Optional
1161           || (file.getSyntax() == Syntax.PROTO2 && isOptional() && getContainingOneof() == null);
1162     }
1163 
1164     /**
1165      * Returns true if this field tracks presence, ie. does the field distinguish between "unset"
1166      * and "present with default value."
1167      *
1168      * <p>This includes required, optional, and oneof fields. It excludes maps, repeated fields, and
1169      * singular proto3 fields without "optional".
1170      *
1171      * <p>For fields where hasPresence() == true, the return value of msg.hasField() is semantically
1172      * meaningful.
1173      */
hasPresence()1174     boolean hasPresence() {
1175       if (isRepeated()) {
1176         return false;
1177       }
1178       return getType() == Type.MESSAGE
1179           || getType() == Type.GROUP
1180           || getContainingOneof() != null
1181           || file.getSyntax() == Syntax.PROTO2;
1182     }
1183 
1184     /**
1185      * For extensions defined nested within message types, gets the outer type. Not valid for
1186      * non-extension fields. For example, consider this {@code .proto} file:
1187      *
1188      * <pre>
1189      *   message Foo {
1190      *     extensions 1000 to max;
1191      *   }
1192      *   extend Foo {
1193      *     optional int32 baz = 1234;
1194      *   }
1195      *   message Bar {
1196      *     extend Foo {
1197      *       optional int32 qux = 4321;
1198      *     }
1199      *   }
1200      * </pre>
1201      *
1202      * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. However, {@code baz}'s
1203      * extension scope is {@code null} while {@code qux}'s extension scope is {@code Bar}.
1204      */
getExtensionScope()1205     public Descriptor getExtensionScope() {
1206       if (!isExtension()) {
1207         throw new UnsupportedOperationException(
1208             String.format("This field is not an extension. (%s)", fullName));
1209       }
1210       return extensionScope;
1211     }
1212 
1213     /** For embedded message and group fields, gets the field's type. */
getMessageType()1214     public Descriptor getMessageType() {
1215       if (getJavaType() != JavaType.MESSAGE) {
1216         throw new UnsupportedOperationException(
1217             String.format("This field is not of message type. (%s)", fullName));
1218       }
1219       return messageType;
1220     }
1221 
1222     /** For enum fields, gets the field's type. */
1223     @Override
getEnumType()1224     public EnumDescriptor getEnumType() {
1225       if (getJavaType() != JavaType.ENUM) {
1226         throw new UnsupportedOperationException(
1227             String.format("This field is not of enum type. (%s)", fullName));
1228       }
1229       return enumType;
1230     }
1231 
1232     /**
1233      * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which
1234      * simply means ascending order by field number. {@code other} must be a field of the same type
1235      * -- i.e. {@code getContainingType()} must return the same {@code Descriptor} for both fields.
1236      *
1237      * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than
1238      *     {@code other}, respectively.
1239      */
1240     @Override
compareTo(final FieldDescriptor other)1241     public int compareTo(final FieldDescriptor other) {
1242       if (other.containingType != containingType) {
1243         throw new IllegalArgumentException(
1244             "FieldDescriptors can only be compared to other FieldDescriptors "
1245                 + "for fields of the same message type.");
1246       }
1247       return getNumber() - other.getNumber();
1248     }
1249 
1250     @Override
toString()1251     public String toString() {
1252       return getFullName();
1253     }
1254 
1255     private final int index;
1256 
1257     private FieldDescriptorProto proto;
1258     private final String fullName;
1259     private final String jsonName;
1260     private final FileDescriptor file;
1261     private final Descriptor extensionScope;
1262     private final boolean isProto3Optional;
1263 
1264     // Possibly initialized during cross-linking.
1265     private Type type;
1266     private Descriptor containingType;
1267     private Descriptor messageType;
1268     private OneofDescriptor containingOneof;
1269     private EnumDescriptor enumType;
1270     private Object defaultValue;
1271 
1272     public enum Type {
1273       DOUBLE(JavaType.DOUBLE),
1274       FLOAT(JavaType.FLOAT),
1275       INT64(JavaType.LONG),
1276       UINT64(JavaType.LONG),
1277       INT32(JavaType.INT),
1278       FIXED64(JavaType.LONG),
1279       FIXED32(JavaType.INT),
1280       BOOL(JavaType.BOOLEAN),
1281       STRING(JavaType.STRING),
1282       GROUP(JavaType.MESSAGE),
1283       MESSAGE(JavaType.MESSAGE),
1284       BYTES(JavaType.BYTE_STRING),
1285       UINT32(JavaType.INT),
1286       ENUM(JavaType.ENUM),
1287       SFIXED32(JavaType.INT),
1288       SFIXED64(JavaType.LONG),
1289       SINT32(JavaType.INT),
1290       SINT64(JavaType.LONG);
1291 
Type(final JavaType javaType)1292       Type(final JavaType javaType) {
1293         this.javaType = javaType;
1294       }
1295 
1296       private JavaType javaType;
1297 
toProto()1298       public FieldDescriptorProto.Type toProto() {
1299         return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
1300       }
1301 
getJavaType()1302       public JavaType getJavaType() {
1303         return javaType;
1304       }
1305 
valueOf(final FieldDescriptorProto.Type type)1306       public static Type valueOf(final FieldDescriptorProto.Type type) {
1307         return values()[type.getNumber() - 1];
1308       }
1309     }
1310 
1311     static {
1312       // Refuse to init if someone added a new declared type.
1313       if (Type.values().length != FieldDescriptorProto.Type.values().length) {
1314         throw new RuntimeException(
1315             "descriptor.proto has a new declared type but Descriptors.java wasn't updated.");
1316       }
1317     }
1318 
1319     public enum JavaType {
1320       INT(0),
1321       LONG(0L),
1322       FLOAT(0F),
1323       DOUBLE(0D),
1324       BOOLEAN(false),
1325       STRING(""),
1326       BYTE_STRING(ByteString.EMPTY),
1327       ENUM(null),
1328       MESSAGE(null);
1329 
JavaType(final Object defaultDefault)1330       JavaType(final Object defaultDefault) {
1331         this.defaultDefault = defaultDefault;
1332       }
1333 
1334       /**
1335        * The default default value for fields of this type, if it's a primitive type. This is meant
1336        * for use inside this file only, hence is private.
1337        */
1338       private final Object defaultDefault;
1339     }
1340 
1341     // This method should match exactly with the ToJsonName() function in C++
1342     // descriptor.cc.
fieldNameToJsonName(String name)1343     private static String fieldNameToJsonName(String name) {
1344       final int length = name.length();
1345       StringBuilder result = new StringBuilder(length);
1346       boolean isNextUpperCase = false;
1347       for (int i = 0; i < length; i++) {
1348         char ch = name.charAt(i);
1349         if (ch == '_') {
1350           isNextUpperCase = true;
1351         } else if (isNextUpperCase) {
1352           // This closely matches the logic for ASCII characters in:
1353           // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689
1354           if ('a' <= ch && ch <= 'z') {
1355             ch = (char) (ch - 'a' + 'A');
1356           }
1357           result.append(ch);
1358           isNextUpperCase = false;
1359         } else {
1360           result.append(ch);
1361         }
1362       }
1363       return result.toString();
1364     }
1365 
FieldDescriptor( final FieldDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index, final boolean isExtension)1366     private FieldDescriptor(
1367         final FieldDescriptorProto proto,
1368         final FileDescriptor file,
1369         final Descriptor parent,
1370         final int index,
1371         final boolean isExtension)
1372         throws DescriptorValidationException {
1373       this.index = index;
1374       this.proto = proto;
1375       fullName = computeFullName(file, parent, proto.getName());
1376       this.file = file;
1377       if (proto.hasJsonName()) {
1378         jsonName = proto.getJsonName();
1379       } else {
1380         jsonName = fieldNameToJsonName(proto.getName());
1381       }
1382 
1383       if (proto.hasType()) {
1384         type = Type.valueOf(proto.getType());
1385       }
1386 
1387       isProto3Optional = proto.getProto3Optional();
1388 
1389       if (getNumber() <= 0) {
1390         throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
1391       }
1392 
1393       if (isExtension) {
1394         if (!proto.hasExtendee()) {
1395           throw new DescriptorValidationException(
1396               this, "FieldDescriptorProto.extendee not set for extension field.");
1397         }
1398         containingType = null; // Will be filled in when cross-linking
1399         if (parent != null) {
1400           extensionScope = parent;
1401         } else {
1402           extensionScope = null;
1403         }
1404 
1405         if (proto.hasOneofIndex()) {
1406           throw new DescriptorValidationException(
1407               this, "FieldDescriptorProto.oneof_index set for extension field.");
1408         }
1409         containingOneof = null;
1410       } else {
1411         if (proto.hasExtendee()) {
1412           throw new DescriptorValidationException(
1413               this, "FieldDescriptorProto.extendee set for non-extension field.");
1414         }
1415         containingType = parent;
1416 
1417         if (proto.hasOneofIndex()) {
1418           if (proto.getOneofIndex() < 0
1419               || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) {
1420             throw new DescriptorValidationException(
1421                 this,
1422                 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName());
1423           }
1424           containingOneof = parent.getOneofs().get(proto.getOneofIndex());
1425           containingOneof.fieldCount++;
1426         } else {
1427           containingOneof = null;
1428         }
1429         extensionScope = null;
1430       }
1431 
1432       file.pool.addSymbol(this);
1433     }
1434 
1435     /** Look up and cross-link all field types, etc. */
crossLink()1436     private void crossLink() throws DescriptorValidationException {
1437       if (proto.hasExtendee()) {
1438         final GenericDescriptor extendee =
1439             file.pool.lookupSymbol(
1440                 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1441         if (!(extendee instanceof Descriptor)) {
1442           throw new DescriptorValidationException(
1443               this, '\"' + proto.getExtendee() + "\" is not a message type.");
1444         }
1445         containingType = (Descriptor) extendee;
1446 
1447         if (!getContainingType().isExtensionNumber(getNumber())) {
1448           throw new DescriptorValidationException(
1449               this,
1450               '\"'
1451                   + getContainingType().getFullName()
1452                   + "\" does not declare "
1453                   + getNumber()
1454                   + " as an extension number.");
1455         }
1456       }
1457 
1458       if (proto.hasTypeName()) {
1459         final GenericDescriptor typeDescriptor =
1460             file.pool.lookupSymbol(
1461                 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1462 
1463         if (!proto.hasType()) {
1464           // Choose field type based on symbol.
1465           if (typeDescriptor instanceof Descriptor) {
1466             type = Type.MESSAGE;
1467           } else if (typeDescriptor instanceof EnumDescriptor) {
1468             type = Type.ENUM;
1469           } else {
1470             throw new DescriptorValidationException(
1471                 this, '\"' + proto.getTypeName() + "\" is not a type.");
1472           }
1473         }
1474 
1475         if (getJavaType() == JavaType.MESSAGE) {
1476           if (!(typeDescriptor instanceof Descriptor)) {
1477             throw new DescriptorValidationException(
1478                 this, '\"' + proto.getTypeName() + "\" is not a message type.");
1479           }
1480           messageType = (Descriptor) typeDescriptor;
1481 
1482           if (proto.hasDefaultValue()) {
1483             throw new DescriptorValidationException(this, "Messages can't have default values.");
1484           }
1485         } else if (getJavaType() == JavaType.ENUM) {
1486           if (!(typeDescriptor instanceof EnumDescriptor)) {
1487             throw new DescriptorValidationException(
1488                 this, '\"' + proto.getTypeName() + "\" is not an enum type.");
1489           }
1490           enumType = (EnumDescriptor) typeDescriptor;
1491         } else {
1492           throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
1493         }
1494       } else {
1495         if (getJavaType() == JavaType.MESSAGE || getJavaType() == JavaType.ENUM) {
1496           throw new DescriptorValidationException(
1497               this, "Field with message or enum type missing type_name.");
1498         }
1499       }
1500 
1501       // Only repeated primitive fields may be packed.
1502       if (proto.getOptions().getPacked() && !isPackable()) {
1503         throw new DescriptorValidationException(
1504             this, "[packed = true] can only be specified for repeated primitive fields.");
1505       }
1506 
1507       // We don't attempt to parse the default value until here because for
1508       // enums we need the enum type's descriptor.
1509       if (proto.hasDefaultValue()) {
1510         if (isRepeated()) {
1511           throw new DescriptorValidationException(
1512               this, "Repeated fields cannot have default values.");
1513         }
1514 
1515         try {
1516           switch (getType()) {
1517             case INT32:
1518             case SINT32:
1519             case SFIXED32:
1520               defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
1521               break;
1522             case UINT32:
1523             case FIXED32:
1524               defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
1525               break;
1526             case INT64:
1527             case SINT64:
1528             case SFIXED64:
1529               defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
1530               break;
1531             case UINT64:
1532             case FIXED64:
1533               defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
1534               break;
1535             case FLOAT:
1536               if (proto.getDefaultValue().equals("inf")) {
1537                 defaultValue = Float.POSITIVE_INFINITY;
1538               } else if (proto.getDefaultValue().equals("-inf")) {
1539                 defaultValue = Float.NEGATIVE_INFINITY;
1540               } else if (proto.getDefaultValue().equals("nan")) {
1541                 defaultValue = Float.NaN;
1542               } else {
1543                 defaultValue = Float.valueOf(proto.getDefaultValue());
1544               }
1545               break;
1546             case DOUBLE:
1547               if (proto.getDefaultValue().equals("inf")) {
1548                 defaultValue = Double.POSITIVE_INFINITY;
1549               } else if (proto.getDefaultValue().equals("-inf")) {
1550                 defaultValue = Double.NEGATIVE_INFINITY;
1551               } else if (proto.getDefaultValue().equals("nan")) {
1552                 defaultValue = Double.NaN;
1553               } else {
1554                 defaultValue = Double.valueOf(proto.getDefaultValue());
1555               }
1556               break;
1557             case BOOL:
1558               defaultValue = Boolean.valueOf(proto.getDefaultValue());
1559               break;
1560             case STRING:
1561               defaultValue = proto.getDefaultValue();
1562               break;
1563             case BYTES:
1564               try {
1565                 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue());
1566               } catch (TextFormat.InvalidEscapeSequenceException e) {
1567                 throw new DescriptorValidationException(
1568                     this, "Couldn't parse default value: " + e.getMessage(), e);
1569               }
1570               break;
1571             case ENUM:
1572               defaultValue = enumType.findValueByName(proto.getDefaultValue());
1573               if (defaultValue == null) {
1574                 throw new DescriptorValidationException(
1575                     this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"');
1576               }
1577               break;
1578             case MESSAGE:
1579             case GROUP:
1580               throw new DescriptorValidationException(this, "Message type had default value.");
1581           }
1582         } catch (NumberFormatException e) {
1583           throw new DescriptorValidationException(
1584               this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e);
1585         }
1586       } else {
1587         // Determine the default default for this field.
1588         if (isRepeated()) {
1589           defaultValue = Collections.emptyList();
1590         } else {
1591           switch (getJavaType()) {
1592             case ENUM:
1593               // We guarantee elsewhere that an enum type always has at least
1594               // one possible value.
1595               defaultValue = enumType.getValues().get(0);
1596               break;
1597             case MESSAGE:
1598               defaultValue = null;
1599               break;
1600             default:
1601               defaultValue = getJavaType().defaultDefault;
1602               break;
1603           }
1604         }
1605       }
1606 
1607       if (!isExtension()) {
1608         file.pool.addFieldByNumber(this);
1609       }
1610 
1611       if (containingType != null && containingType.getOptions().getMessageSetWireFormat()) {
1612         if (isExtension()) {
1613           if (!isOptional() || getType() != Type.MESSAGE) {
1614             throw new DescriptorValidationException(
1615                 this, "Extensions of MessageSets must be optional messages.");
1616           }
1617         } else {
1618           throw new DescriptorValidationException(
1619               this, "MessageSets cannot have fields, only extensions.");
1620         }
1621       }
1622     }
1623 
1624     /** See {@link FileDescriptor#setProto}. */
setProto(final FieldDescriptorProto proto)1625     private void setProto(final FieldDescriptorProto proto) {
1626       this.proto = proto;
1627     }
1628 
1629     /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */
1630     @Override
internalMergeFrom(MessageLite.Builder to, MessageLite from)1631     public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
1632       // FieldDescriptors are only used with non-lite messages so we can just
1633       // down-cast and call mergeFrom directly.
1634       return ((Message.Builder) to).mergeFrom((Message) from);
1635     }
1636 
1637   }
1638 
1639   // =================================================================
1640 
1641   /** Describes an enum type. */
1642   public static final class EnumDescriptor extends GenericDescriptor
1643       implements Internal.EnumLiteMap<EnumValueDescriptor> {
1644     /**
1645      * Get the index of this descriptor within its parent.
1646      *
1647      * @see Descriptors.Descriptor#getIndex()
1648      */
getIndex()1649     public int getIndex() {
1650       return index;
1651     }
1652 
1653     /** Convert the descriptor to its protocol message representation. */
1654     @Override
toProto()1655     public EnumDescriptorProto toProto() {
1656       return proto;
1657     }
1658 
1659     /** Get the type's unqualified name. */
1660     @Override
getName()1661     public String getName() {
1662       return proto.getName();
1663     }
1664 
1665     /**
1666      * Get the type's fully-qualified name.
1667      *
1668      * @see Descriptors.Descriptor#getFullName()
1669      */
1670     @Override
getFullName()1671     public String getFullName() {
1672       return fullName;
1673     }
1674 
1675     /** Get the {@link FileDescriptor} containing this descriptor. */
1676     @Override
getFile()1677     public FileDescriptor getFile() {
1678       return file;
1679     }
1680 
1681     /** If this is a nested type, get the outer descriptor, otherwise null. */
getContainingType()1682     public Descriptor getContainingType() {
1683       return containingType;
1684     }
1685 
1686     /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
getOptions()1687     public EnumOptions getOptions() {
1688       return proto.getOptions();
1689     }
1690 
1691     /** Get a list of defined values for this enum. */
getValues()1692     public List<EnumValueDescriptor> getValues() {
1693       return Collections.unmodifiableList(Arrays.asList(values));
1694     }
1695 
1696     /**
1697      * Find an enum value by name.
1698      *
1699      * @param name The unqualified name of the value (e.g. "FOO").
1700      * @return the value's descriptor, or {@code null} if not found.
1701      */
findValueByName(final String name)1702     public EnumValueDescriptor findValueByName(final String name) {
1703       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
1704       if (result != null && result instanceof EnumValueDescriptor) {
1705         return (EnumValueDescriptor) result;
1706       } else {
1707         return null;
1708       }
1709     }
1710 
1711     /**
1712      * Find an enum value by number. If multiple enum values have the same number, this returns the
1713      * first defined value with that number.
1714      *
1715      * @param number The value's number.
1716      * @return the value's descriptor, or {@code null} if not found.
1717      */
1718     @Override
findValueByNumber(final int number)1719     public EnumValueDescriptor findValueByNumber(final int number) {
1720       return file.pool.enumValuesByNumber.get(new DescriptorPool.DescriptorIntPair(this, number));
1721     }
1722 
1723     /**
1724      * Get the enum value for a number. If no enum value has this number, construct an
1725      * EnumValueDescriptor for it.
1726      */
findValueByNumberCreatingIfUnknown(final int number)1727     public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) {
1728       EnumValueDescriptor result = findValueByNumber(number);
1729       if (result != null) {
1730         return result;
1731       }
1732       // The number represents an unknown enum value.
1733       synchronized (this) {
1734         // Descriptors are compared by object identity so for the same number
1735         // we need to return the same EnumValueDescriptor object. This means
1736         // we have to store created EnumValueDescriptors. However, as there
1737         // are potentially 2G unknown enum values, storing all of these
1738         // objects persistently will consume lots of memory for long-running
1739         // services and it's also unnecessary as not many EnumValueDescriptors
1740         // will be used at the same time.
1741         //
1742         // To solve the problem we take advantage of Java's weak references and
1743         // rely on gc to release unused descriptors.
1744         //
1745         // Here is how it works:
1746         //   * We store unknown EnumValueDescriptors in a WeakHashMap with the
1747         //     value being a weak reference to the descriptor.
1748         //   * The descriptor holds a strong reference to the key so as long
1749         //     as the EnumValueDescriptor is in use, the key will be there
1750         //     and the corresponding map entry will be there. Following-up
1751         //     queries with the same number will return the same descriptor.
1752         //   * If the user no longer uses an unknown EnumValueDescriptor,
1753         //     it will be gc-ed since we only hold a weak reference to it in
1754         //     the map. The key in the corresponding map entry will also be
1755         //     gc-ed as the only strong reference to it is in the descriptor
1756         //     which is just gc-ed. With the key being gone WeakHashMap will
1757         //     then remove the whole entry. This way unknown descriptors will
1758         //     be freed automatically and we don't need to do anything to
1759         //     clean-up unused map entries.
1760 
1761         // Note: We must use "new Integer(number)" here because we don't want
1762         // these Integer objects to be cached.
1763         Integer key = new Integer(number);
1764         WeakReference<EnumValueDescriptor> reference = unknownValues.get(key);
1765         if (reference != null) {
1766           result = reference.get();
1767         }
1768         if (result == null) {
1769           result = new EnumValueDescriptor(file, this, key);
1770           unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result));
1771         }
1772       }
1773       return result;
1774     }
1775 
1776     // Used in tests only.
getUnknownEnumValueDescriptorCount()1777     int getUnknownEnumValueDescriptorCount() {
1778       return unknownValues.size();
1779     }
1780 
1781     private final int index;
1782     private EnumDescriptorProto proto;
1783     private final String fullName;
1784     private final FileDescriptor file;
1785     private final Descriptor containingType;
1786     private EnumValueDescriptor[] values;
1787     private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues =
1788         new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>();
1789 
EnumDescriptor( final EnumDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)1790     private EnumDescriptor(
1791         final EnumDescriptorProto proto,
1792         final FileDescriptor file,
1793         final Descriptor parent,
1794         final int index)
1795         throws DescriptorValidationException {
1796       this.index = index;
1797       this.proto = proto;
1798       fullName = computeFullName(file, parent, proto.getName());
1799       this.file = file;
1800       containingType = parent;
1801 
1802       if (proto.getValueCount() == 0) {
1803         // We cannot allow enums with no values because this would mean there
1804         // would be no valid default value for fields of this type.
1805         throw new DescriptorValidationException(this, "Enums must contain at least one value.");
1806       }
1807 
1808       values = new EnumValueDescriptor[proto.getValueCount()];
1809       for (int i = 0; i < proto.getValueCount(); i++) {
1810         values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i);
1811       }
1812 
1813       file.pool.addSymbol(this);
1814     }
1815 
1816     /** See {@link FileDescriptor#setProto}. */
setProto(final EnumDescriptorProto proto)1817     private void setProto(final EnumDescriptorProto proto) {
1818       this.proto = proto;
1819 
1820       for (int i = 0; i < values.length; i++) {
1821         values[i].setProto(proto.getValue(i));
1822       }
1823     }
1824   }
1825 
1826   // =================================================================
1827 
1828   /**
1829    * Describes one value within an enum type. Note that multiple defined values may have the same
1830    * number. In generated Java code, all values with the same number after the first become aliases
1831    * of the first. However, they still have independent EnumValueDescriptors.
1832    */
1833   public static final class EnumValueDescriptor extends GenericDescriptor
1834       implements Internal.EnumLite {
1835     /**
1836      * Get the index of this descriptor within its parent.
1837      *
1838      * @see Descriptors.Descriptor#getIndex()
1839      */
getIndex()1840     public int getIndex() {
1841       return index;
1842     }
1843 
1844     /** Convert the descriptor to its protocol message representation. */
1845     @Override
toProto()1846     public EnumValueDescriptorProto toProto() {
1847       return proto;
1848     }
1849 
1850     /** Get the value's unqualified name. */
1851     @Override
getName()1852     public String getName() {
1853       return proto.getName();
1854     }
1855 
1856     /** Get the value's number. */
1857     @Override
getNumber()1858     public int getNumber() {
1859       return proto.getNumber();
1860     }
1861 
1862     @Override
toString()1863     public String toString() {
1864       return proto.getName();
1865     }
1866 
1867     /**
1868      * Get the value's fully-qualified name.
1869      *
1870      * @see Descriptors.Descriptor#getFullName()
1871      */
1872     @Override
getFullName()1873     public String getFullName() {
1874       return fullName;
1875     }
1876 
1877     /** Get the {@link FileDescriptor} containing this descriptor. */
1878     @Override
getFile()1879     public FileDescriptor getFile() {
1880       return file;
1881     }
1882 
1883     /** Get the value's enum type. */
getType()1884     public EnumDescriptor getType() {
1885       return type;
1886     }
1887 
1888     /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */
getOptions()1889     public EnumValueOptions getOptions() {
1890       return proto.getOptions();
1891     }
1892 
1893     private final int index;
1894     private EnumValueDescriptorProto proto;
1895     private final String fullName;
1896     private final FileDescriptor file;
1897     private final EnumDescriptor type;
1898 
EnumValueDescriptor( final EnumValueDescriptorProto proto, final FileDescriptor file, final EnumDescriptor parent, final int index)1899     private EnumValueDescriptor(
1900         final EnumValueDescriptorProto proto,
1901         final FileDescriptor file,
1902         final EnumDescriptor parent,
1903         final int index)
1904         throws DescriptorValidationException {
1905       this.index = index;
1906       this.proto = proto;
1907       this.file = file;
1908       type = parent;
1909 
1910       fullName = parent.getFullName() + '.' + proto.getName();
1911 
1912       file.pool.addSymbol(this);
1913       file.pool.addEnumValueByNumber(this);
1914     }
1915 
1916     // Create an unknown enum value.
EnumValueDescriptor( final FileDescriptor file, final EnumDescriptor parent, final Integer number)1917     private EnumValueDescriptor(
1918         final FileDescriptor file, final EnumDescriptor parent, final Integer number) {
1919       String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number;
1920       EnumValueDescriptorProto proto =
1921           EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build();
1922       this.index = -1;
1923       this.proto = proto;
1924       this.file = file;
1925       this.type = parent;
1926       this.fullName = parent.getFullName() + '.' + proto.getName();
1927 
1928       // Don't add this descriptor into pool.
1929     }
1930 
1931     /** See {@link FileDescriptor#setProto}. */
setProto(final EnumValueDescriptorProto proto)1932     private void setProto(final EnumValueDescriptorProto proto) {
1933       this.proto = proto;
1934     }
1935   }
1936 
1937   // =================================================================
1938 
1939   /** Describes a service type. */
1940   public static final class ServiceDescriptor extends GenericDescriptor {
1941     /**
1942      * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
1943      */
getIndex()1944     public int getIndex() {
1945       return index;
1946     }
1947 
1948     /** Convert the descriptor to its protocol message representation. */
1949     @Override
toProto()1950     public ServiceDescriptorProto toProto() {
1951       return proto;
1952     }
1953 
1954     /** Get the type's unqualified name. */
1955     @Override
getName()1956     public String getName() {
1957       return proto.getName();
1958     }
1959 
1960     /**
1961      * Get the type's fully-qualified name.
1962      *
1963      * @see Descriptors.Descriptor#getFullName()
1964      */
1965     @Override
getFullName()1966     public String getFullName() {
1967       return fullName;
1968     }
1969 
1970     /** Get the {@link FileDescriptor} containing this descriptor. */
1971     @Override
getFile()1972     public FileDescriptor getFile() {
1973       return file;
1974     }
1975 
1976     /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
getOptions()1977     public ServiceOptions getOptions() {
1978       return proto.getOptions();
1979     }
1980 
1981     /** Get a list of methods for this service. */
getMethods()1982     public List<MethodDescriptor> getMethods() {
1983       return Collections.unmodifiableList(Arrays.asList(methods));
1984     }
1985 
1986     /**
1987      * Find a method by name.
1988      *
1989      * @param name The unqualified name of the method (e.g. "Foo").
1990      * @return the method's descriptor, or {@code null} if not found.
1991      */
findMethodByName(final String name)1992     public MethodDescriptor findMethodByName(final String name) {
1993       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
1994       if (result != null && result instanceof MethodDescriptor) {
1995         return (MethodDescriptor) result;
1996       } else {
1997         return null;
1998       }
1999     }
2000 
2001     private final int index;
2002     private ServiceDescriptorProto proto;
2003     private final String fullName;
2004     private final FileDescriptor file;
2005     private MethodDescriptor[] methods;
2006 
ServiceDescriptor( final ServiceDescriptorProto proto, final FileDescriptor file, final int index)2007     private ServiceDescriptor(
2008         final ServiceDescriptorProto proto, final FileDescriptor file, final int index)
2009         throws DescriptorValidationException {
2010       this.index = index;
2011       this.proto = proto;
2012       fullName = computeFullName(file, null, proto.getName());
2013       this.file = file;
2014 
2015       methods = new MethodDescriptor[proto.getMethodCount()];
2016       for (int i = 0; i < proto.getMethodCount(); i++) {
2017         methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i);
2018       }
2019 
2020       file.pool.addSymbol(this);
2021     }
2022 
crossLink()2023     private void crossLink() throws DescriptorValidationException {
2024       for (final MethodDescriptor method : methods) {
2025         method.crossLink();
2026       }
2027     }
2028 
2029     /** See {@link FileDescriptor#setProto}. */
setProto(final ServiceDescriptorProto proto)2030     private void setProto(final ServiceDescriptorProto proto) {
2031       this.proto = proto;
2032 
2033       for (int i = 0; i < methods.length; i++) {
2034         methods[i].setProto(proto.getMethod(i));
2035       }
2036     }
2037   }
2038 
2039   // =================================================================
2040 
2041   /** Describes one method within a service type. */
2042   public static final class MethodDescriptor extends GenericDescriptor {
2043     /**
2044      * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
2045      */
getIndex()2046     public int getIndex() {
2047       return index;
2048     }
2049 
2050     /** Convert the descriptor to its protocol message representation. */
2051     @Override
toProto()2052     public MethodDescriptorProto toProto() {
2053       return proto;
2054     }
2055 
2056     /** Get the method's unqualified name. */
2057     @Override
getName()2058     public String getName() {
2059       return proto.getName();
2060     }
2061 
2062     /**
2063      * Get the method's fully-qualified name.
2064      *
2065      * @see Descriptors.Descriptor#getFullName()
2066      */
2067     @Override
getFullName()2068     public String getFullName() {
2069       return fullName;
2070     }
2071 
2072     /** Get the {@link FileDescriptor} containing this descriptor. */
2073     @Override
getFile()2074     public FileDescriptor getFile() {
2075       return file;
2076     }
2077 
2078     /** Get the method's service type. */
getService()2079     public ServiceDescriptor getService() {
2080       return service;
2081     }
2082 
2083     /** Get the method's input type. */
getInputType()2084     public Descriptor getInputType() {
2085       return inputType;
2086     }
2087 
2088     /** Get the method's output type. */
getOutputType()2089     public Descriptor getOutputType() {
2090       return outputType;
2091     }
2092 
2093     /** Get whether or not the inputs are streaming. */
isClientStreaming()2094     public boolean isClientStreaming() {
2095       return proto.getClientStreaming();
2096     }
2097 
2098     /** Get whether or not the outputs are streaming. */
isServerStreaming()2099     public boolean isServerStreaming() {
2100       return proto.getServerStreaming();
2101     }
2102 
2103     /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */
getOptions()2104     public MethodOptions getOptions() {
2105       return proto.getOptions();
2106     }
2107 
2108     private final int index;
2109     private MethodDescriptorProto proto;
2110     private final String fullName;
2111     private final FileDescriptor file;
2112     private final ServiceDescriptor service;
2113 
2114     // Initialized during cross-linking.
2115     private Descriptor inputType;
2116     private Descriptor outputType;
2117 
MethodDescriptor( final MethodDescriptorProto proto, final FileDescriptor file, final ServiceDescriptor parent, final int index)2118     private MethodDescriptor(
2119         final MethodDescriptorProto proto,
2120         final FileDescriptor file,
2121         final ServiceDescriptor parent,
2122         final int index)
2123         throws DescriptorValidationException {
2124       this.index = index;
2125       this.proto = proto;
2126       this.file = file;
2127       service = parent;
2128 
2129       fullName = parent.getFullName() + '.' + proto.getName();
2130 
2131       file.pool.addSymbol(this);
2132     }
2133 
crossLink()2134     private void crossLink() throws DescriptorValidationException {
2135       final GenericDescriptor input =
2136           file.pool.lookupSymbol(
2137               proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2138       if (!(input instanceof Descriptor)) {
2139         throw new DescriptorValidationException(
2140             this, '\"' + proto.getInputType() + "\" is not a message type.");
2141       }
2142       inputType = (Descriptor) input;
2143 
2144       final GenericDescriptor output =
2145           file.pool.lookupSymbol(
2146               proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2147       if (!(output instanceof Descriptor)) {
2148         throw new DescriptorValidationException(
2149             this, '\"' + proto.getOutputType() + "\" is not a message type.");
2150       }
2151       outputType = (Descriptor) output;
2152     }
2153 
2154     /** See {@link FileDescriptor#setProto}. */
setProto(final MethodDescriptorProto proto)2155     private void setProto(final MethodDescriptorProto proto) {
2156       this.proto = proto;
2157     }
2158   }
2159 
2160   // =================================================================
2161 
computeFullName( final FileDescriptor file, final Descriptor parent, final String name)2162   private static String computeFullName(
2163       final FileDescriptor file, final Descriptor parent, final String name) {
2164     if (parent != null) {
2165       return parent.getFullName() + '.' + name;
2166     }
2167 
2168     final String packageName = file.getPackage();
2169     if (!packageName.isEmpty()) {
2170       return packageName + '.' + name;
2171     }
2172 
2173     return name;
2174   }
2175 
2176   // =================================================================
2177 
2178   /**
2179    * All descriptors implement this to make it easier to implement tools like {@code
2180    * DescriptorPool}.
2181    */
2182   public abstract static class GenericDescriptor {
2183 
2184     // Private constructor to prevent subclasses outside of com.google.protobuf.Descriptors
GenericDescriptor()2185     private GenericDescriptor() {}
2186 
toProto()2187     public abstract Message toProto();
2188 
getName()2189     public abstract String getName();
2190 
getFullName()2191     public abstract String getFullName();
2192 
getFile()2193     public abstract FileDescriptor getFile();
2194   }
2195 
2196   /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */
2197   public static class DescriptorValidationException extends Exception {
2198     private static final long serialVersionUID = 5750205775490483148L;
2199 
2200     /** Gets the full name of the descriptor where the error occurred. */
getProblemSymbolName()2201     public String getProblemSymbolName() {
2202       return name;
2203     }
2204 
2205     /** Gets the protocol message representation of the invalid descriptor. */
getProblemProto()2206     public Message getProblemProto() {
2207       return proto;
2208     }
2209 
2210     /** Gets a human-readable description of the error. */
getDescription()2211     public String getDescription() {
2212       return description;
2213     }
2214 
2215     private final String name;
2216     private final Message proto;
2217     private final String description;
2218 
DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description)2219     private DescriptorValidationException(
2220         final GenericDescriptor problemDescriptor, final String description) {
2221       super(problemDescriptor.getFullName() + ": " + description);
2222 
2223       // Note that problemDescriptor may be partially uninitialized, so we
2224       // don't want to expose it directly to the user.  So, we only provide
2225       // the name and the original proto.
2226       name = problemDescriptor.getFullName();
2227       proto = problemDescriptor.toProto();
2228       this.description = description;
2229     }
2230 
DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description, final Throwable cause)2231     private DescriptorValidationException(
2232         final GenericDescriptor problemDescriptor,
2233         final String description,
2234         final Throwable cause) {
2235       this(problemDescriptor, description);
2236       initCause(cause);
2237     }
2238 
DescriptorValidationException( final FileDescriptor problemDescriptor, final String description)2239     private DescriptorValidationException(
2240         final FileDescriptor problemDescriptor, final String description) {
2241       super(problemDescriptor.getName() + ": " + description);
2242 
2243       // Note that problemDescriptor may be partially uninitialized, so we
2244       // don't want to expose it directly to the user.  So, we only provide
2245       // the name and the original proto.
2246       name = problemDescriptor.getName();
2247       proto = problemDescriptor.toProto();
2248       this.description = description;
2249     }
2250   }
2251 
2252   // =================================================================
2253 
2254   /**
2255    * A private helper class which contains lookup tables containing all the descriptors defined in a
2256    * particular file.
2257    */
2258   private static final class DescriptorPool {
2259 
2260     /** Defines what subclass of descriptors to search in the descriptor pool. */
2261     enum SearchFilter {
2262       TYPES_ONLY,
2263       AGGREGATES_ONLY,
2264       ALL_SYMBOLS
2265     }
2266 
DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies)2267     DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) {
2268       this.dependencies = new HashSet<FileDescriptor>();
2269       this.allowUnknownDependencies = allowUnknownDependencies;
2270 
2271       for (int i = 0; i < dependencies.length; i++) {
2272         this.dependencies.add(dependencies[i]);
2273         importPublicDependencies(dependencies[i]);
2274       }
2275 
2276       for (final FileDescriptor dependency : this.dependencies) {
2277         try {
2278           addPackage(dependency.getPackage(), dependency);
2279         } catch (DescriptorValidationException e) {
2280           // Can't happen, because addPackage() only fails when the name
2281           // conflicts with a non-package, but we have not yet added any
2282           // non-packages at this point.
2283           throw new AssertionError(e);
2284         }
2285       }
2286     }
2287 
2288     /** Find and put public dependencies of the file into dependencies set. */
importPublicDependencies(final FileDescriptor file)2289     private void importPublicDependencies(final FileDescriptor file) {
2290       for (FileDescriptor dependency : file.getPublicDependencies()) {
2291         if (dependencies.add(dependency)) {
2292           importPublicDependencies(dependency);
2293         }
2294       }
2295     }
2296 
2297     private final Set<FileDescriptor> dependencies;
2298     private boolean allowUnknownDependencies;
2299 
2300     private final Map<String, GenericDescriptor> descriptorsByName =
2301         new HashMap<String, GenericDescriptor>();
2302     private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
2303         new HashMap<DescriptorIntPair, FieldDescriptor>();
2304     private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
2305         new HashMap<DescriptorIntPair, EnumValueDescriptor>();
2306 
2307     /** Find a generic descriptor by fully-qualified name. */
findSymbol(final String fullName)2308     GenericDescriptor findSymbol(final String fullName) {
2309       return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
2310     }
2311 
2312     /**
2313      * Find a descriptor by fully-qualified name and given option to only search valid field type
2314      * descriptors.
2315      */
findSymbol(final String fullName, final SearchFilter filter)2316     GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) {
2317       GenericDescriptor result = descriptorsByName.get(fullName);
2318       if (result != null) {
2319         if ((filter == SearchFilter.ALL_SYMBOLS)
2320             || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2321             || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2322           return result;
2323         }
2324       }
2325 
2326       for (final FileDescriptor dependency : dependencies) {
2327         result = dependency.pool.descriptorsByName.get(fullName);
2328         if (result != null) {
2329           if ((filter == SearchFilter.ALL_SYMBOLS)
2330               || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2331               || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2332             return result;
2333           }
2334         }
2335       }
2336 
2337       return null;
2338     }
2339 
2340     /** Checks if the descriptor is a valid type for a message field. */
isType(GenericDescriptor descriptor)2341     boolean isType(GenericDescriptor descriptor) {
2342       return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor);
2343     }
2344 
2345     /** Checks if the descriptor is a valid namespace type. */
isAggregate(GenericDescriptor descriptor)2346     boolean isAggregate(GenericDescriptor descriptor) {
2347       return (descriptor instanceof Descriptor)
2348           || (descriptor instanceof EnumDescriptor)
2349           || (descriptor instanceof PackageDescriptor)
2350           || (descriptor instanceof ServiceDescriptor);
2351     }
2352 
2353     /**
2354      * Look up a type descriptor by name, relative to some other descriptor. The name may be
2355      * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name
2356      * lookup semantics are used to search for the matching descriptor.
2357      */
lookupSymbol( final String name, final GenericDescriptor relativeTo, final DescriptorPool.SearchFilter filter)2358     GenericDescriptor lookupSymbol(
2359         final String name,
2360         final GenericDescriptor relativeTo,
2361         final DescriptorPool.SearchFilter filter)
2362         throws DescriptorValidationException {
2363       // TODO(kenton):  This could be optimized in a number of ways.
2364 
2365       GenericDescriptor result;
2366       String fullname;
2367       if (name.startsWith(".")) {
2368         // Fully-qualified name.
2369         fullname = name.substring(1);
2370         result = findSymbol(fullname, filter);
2371       } else {
2372         // If "name" is a compound identifier, we want to search for the
2373         // first component of it, then search within it for the rest.
2374         // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
2375         // defined in multiple parent scopes, we only want to find "Bar.baz" in
2376         // the innermost one.  E.g., the following should produce an error:
2377         //   message Bar { message Baz {} }
2378         //   message Foo {
2379         //     message Bar {
2380         //     }
2381         //     optional Bar.Baz baz = 1;
2382         //   }
2383         // So, we look for just "Foo" first, then look for "Bar.baz" within it
2384         // if found.
2385         final int firstPartLength = name.indexOf('.');
2386         final String firstPart;
2387         if (firstPartLength == -1) {
2388           firstPart = name;
2389         } else {
2390           firstPart = name.substring(0, firstPartLength);
2391         }
2392 
2393         // We will search each parent scope of "relativeTo" looking for the
2394         // symbol.
2395         final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName());
2396 
2397         while (true) {
2398           // Chop off the last component of the scope.
2399           final int dotpos = scopeToTry.lastIndexOf(".");
2400           if (dotpos == -1) {
2401             fullname = name;
2402             result = findSymbol(name, filter);
2403             break;
2404           } else {
2405             scopeToTry.setLength(dotpos + 1);
2406 
2407             // Append firstPart and try to find
2408             scopeToTry.append(firstPart);
2409             result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY);
2410 
2411             if (result != null) {
2412               if (firstPartLength != -1) {
2413                 // We only found the first part of the symbol.  Now look for
2414                 // the whole thing.  If this fails, we *don't* want to keep
2415                 // searching parent scopes.
2416                 scopeToTry.setLength(dotpos + 1);
2417                 scopeToTry.append(name);
2418                 result = findSymbol(scopeToTry.toString(), filter);
2419               }
2420               fullname = scopeToTry.toString();
2421               break;
2422             }
2423 
2424             // Not found.  Remove the name so we can try again.
2425             scopeToTry.setLength(dotpos);
2426           }
2427         }
2428       }
2429 
2430       if (result == null) {
2431         if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) {
2432           logger.warning(
2433               "The descriptor for message type \""
2434                   + name
2435                   + "\" can not be found and a placeholder is created for it");
2436           // We create a dummy message descriptor here regardless of the
2437           // expected type. If the type should be message, this dummy
2438           // descriptor will work well and if the type should be enum, a
2439           // DescriptorValidationException will be thrown latter. In either
2440           // case, the code works as expected: we allow unknown message types
2441           // but not unknown enum types.
2442           result = new Descriptor(fullname);
2443           // Add the placeholder file as a dependency so we can find the
2444           // placeholder symbol when resolving other references.
2445           this.dependencies.add(result.getFile());
2446           return result;
2447         } else {
2448           throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined.");
2449         }
2450       } else {
2451         return result;
2452       }
2453     }
2454 
2455     /**
2456      * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an
2457      * error.
2458      */
addSymbol(final GenericDescriptor descriptor)2459     void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException {
2460       validateSymbolName(descriptor);
2461 
2462       final String fullName = descriptor.getFullName();
2463 
2464       final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
2465       if (old != null) {
2466         descriptorsByName.put(fullName, old);
2467 
2468         if (descriptor.getFile() == old.getFile()) {
2469           final int dotpos = fullName.lastIndexOf('.');
2470           if (dotpos == -1) {
2471             throw new DescriptorValidationException(
2472                 descriptor, '\"' + fullName + "\" is already defined.");
2473           } else {
2474             throw new DescriptorValidationException(
2475                 descriptor,
2476                 '\"'
2477                     + fullName.substring(dotpos + 1)
2478                     + "\" is already defined in \""
2479                     + fullName.substring(0, dotpos)
2480                     + "\".");
2481           }
2482         } else {
2483           throw new DescriptorValidationException(
2484               descriptor,
2485               '\"'
2486                   + fullName
2487                   + "\" is already defined in file \""
2488                   + old.getFile().getName()
2489                   + "\".");
2490         }
2491       }
2492     }
2493 
2494     /**
2495      * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so
2496      * that someone cannot define, say, a message type that has the same name as an existing
2497      * package.
2498      */
2499     private static final class PackageDescriptor extends GenericDescriptor {
2500       @Override
toProto()2501       public Message toProto() {
2502         return file.toProto();
2503       }
2504 
2505       @Override
getName()2506       public String getName() {
2507         return name;
2508       }
2509 
2510       @Override
getFullName()2511       public String getFullName() {
2512         return fullName;
2513       }
2514 
2515       @Override
getFile()2516       public FileDescriptor getFile() {
2517         return file;
2518       }
2519 
PackageDescriptor(final String name, final String fullName, final FileDescriptor file)2520       PackageDescriptor(final String name, final String fullName, final FileDescriptor file) {
2521         this.file = file;
2522         this.fullName = fullName;
2523         this.name = name;
2524       }
2525 
2526       private final String name;
2527       private final String fullName;
2528       private final FileDescriptor file;
2529     }
2530 
2531     /**
2532      * Adds a package to the symbol tables. If a package by the same name already exists, that is
2533      * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If
2534      * the package has multiple components, this also adds the parent package(s).
2535      */
addPackage(final String fullName, final FileDescriptor file)2536     void addPackage(final String fullName, final FileDescriptor file)
2537         throws DescriptorValidationException {
2538       final int dotpos = fullName.lastIndexOf('.');
2539       final String name;
2540       if (dotpos == -1) {
2541         name = fullName;
2542       } else {
2543         addPackage(fullName.substring(0, dotpos), file);
2544         name = fullName.substring(dotpos + 1);
2545       }
2546 
2547       final GenericDescriptor old =
2548           descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file));
2549       if (old != null) {
2550         descriptorsByName.put(fullName, old);
2551         if (!(old instanceof PackageDescriptor)) {
2552           throw new DescriptorValidationException(
2553               file,
2554               '\"'
2555                   + name
2556                   + "\" is already defined (as something other than a "
2557                   + "package) in file \""
2558                   + old.getFile().getName()
2559                   + "\".");
2560         }
2561       }
2562     }
2563 
2564     /** A (GenericDescriptor, int) pair, used as a map key. */
2565     private static final class DescriptorIntPair {
2566       private final GenericDescriptor descriptor;
2567       private final int number;
2568 
DescriptorIntPair(final GenericDescriptor descriptor, final int number)2569       DescriptorIntPair(final GenericDescriptor descriptor, final int number) {
2570         this.descriptor = descriptor;
2571         this.number = number;
2572       }
2573 
2574       @Override
hashCode()2575       public int hashCode() {
2576         return descriptor.hashCode() * ((1 << 16) - 1) + number;
2577       }
2578 
2579       @Override
equals(final Object obj)2580       public boolean equals(final Object obj) {
2581         if (!(obj instanceof DescriptorIntPair)) {
2582           return false;
2583         }
2584         final DescriptorIntPair other = (DescriptorIntPair) obj;
2585         return descriptor == other.descriptor && number == other.number;
2586       }
2587     }
2588 
2589     /**
2590      * Adds a field to the fieldsByNumber table. Throws an exception if a field with the same
2591      * containing type and number already exists.
2592      */
addFieldByNumber(final FieldDescriptor field)2593     void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException {
2594       final DescriptorIntPair key =
2595           new DescriptorIntPair(field.getContainingType(), field.getNumber());
2596       final FieldDescriptor old = fieldsByNumber.put(key, field);
2597       if (old != null) {
2598         fieldsByNumber.put(key, old);
2599         throw new DescriptorValidationException(
2600             field,
2601             "Field number "
2602                 + field.getNumber()
2603                 + " has already been used in \""
2604                 + field.getContainingType().getFullName()
2605                 + "\" by field \""
2606                 + old.getName()
2607                 + "\".");
2608       }
2609     }
2610 
2611     /**
2612      * Adds an enum value to the enumValuesByNumber table. If an enum value with the same type and
2613      * number already exists, does nothing. (This is allowed; the first value define with the number
2614      * takes precedence.)
2615      */
addEnumValueByNumber(final EnumValueDescriptor value)2616     void addEnumValueByNumber(final EnumValueDescriptor value) {
2617       final DescriptorIntPair key = new DescriptorIntPair(value.getType(), value.getNumber());
2618       final EnumValueDescriptor old = enumValuesByNumber.put(key, value);
2619       if (old != null) {
2620         enumValuesByNumber.put(key, old);
2621         // Not an error:  Multiple enum values may have the same number, but
2622         // we only want the first one in the map.
2623       }
2624     }
2625 
2626     /**
2627      * Verifies that the descriptor's name is valid (i.e. it contains only letters, digits, and
2628      * underscores, and does not start with a digit).
2629      */
validateSymbolName(final GenericDescriptor descriptor)2630     static void validateSymbolName(final GenericDescriptor descriptor)
2631         throws DescriptorValidationException {
2632       final String name = descriptor.getName();
2633       if (name.length() == 0) {
2634         throw new DescriptorValidationException(descriptor, "Missing name.");
2635       }
2636 
2637       // Non-ASCII characters are not valid in protobuf identifiers, even
2638       // if they are letters or digits.
2639       // The first character must be a letter or '_'.
2640       // Subsequent characters may be letters, numbers, or digits.
2641       for (int i = 0; i < name.length(); i++) {
2642         final char c = name.charAt(i);
2643         if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
2644           || (c == '_')
2645           || ('0' <= c && c <= '9' && i > 0)) {
2646           // Valid
2647           continue;
2648         }
2649         throw new DescriptorValidationException(
2650             descriptor, '\"' + name + "\" is not a valid identifier.");
2651       }
2652     }
2653   }
2654 
2655   /** Describes an oneof of a message type. */
2656   public static final class OneofDescriptor extends GenericDescriptor {
2657     /** Get the index of this descriptor within its parent. */
getIndex()2658     public int getIndex() {
2659       return index;
2660     }
2661 
2662     @Override
getName()2663     public String getName() {
2664       return proto.getName();
2665     }
2666 
2667     @Override
getFile()2668     public FileDescriptor getFile() {
2669       return file;
2670     }
2671 
2672     @Override
getFullName()2673     public String getFullName() {
2674       return fullName;
2675     }
2676 
getContainingType()2677     public Descriptor getContainingType() {
2678       return containingType;
2679     }
2680 
getFieldCount()2681     public int getFieldCount() {
2682       return fieldCount;
2683     }
2684 
getOptions()2685     public OneofOptions getOptions() {
2686       return proto.getOptions();
2687     }
2688 
isSynthetic()2689     public boolean isSynthetic() {
2690       return fields.length == 1 && fields[0].isProto3Optional;
2691     }
2692 
2693     /** Get a list of this message type's fields. */
getFields()2694     public List<FieldDescriptor> getFields() {
2695       return Collections.unmodifiableList(Arrays.asList(fields));
2696     }
2697 
getField(int index)2698     public FieldDescriptor getField(int index) {
2699       return fields[index];
2700     }
2701 
2702     @Override
toProto()2703     public OneofDescriptorProto toProto() {
2704       return proto;
2705     }
2706 
setProto(final OneofDescriptorProto proto)2707     private void setProto(final OneofDescriptorProto proto) {
2708       this.proto = proto;
2709     }
2710 
OneofDescriptor( final OneofDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)2711     private OneofDescriptor(
2712         final OneofDescriptorProto proto,
2713         final FileDescriptor file,
2714         final Descriptor parent,
2715         final int index)
2716         throws DescriptorValidationException {
2717       this.proto = proto;
2718       fullName = computeFullName(file, parent, proto.getName());
2719       this.file = file;
2720       this.index = index;
2721 
2722       containingType = parent;
2723       fieldCount = 0;
2724     }
2725 
2726     private final int index;
2727     private OneofDescriptorProto proto;
2728     private final String fullName;
2729     private final FileDescriptor file;
2730 
2731     private Descriptor containingType;
2732     private int fieldCount;
2733     private FieldDescriptor[] fields;
2734   }
2735 }
2736