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