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