• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.protobuf.Internal.checkNotNull;
11 
12 import com.google.protobuf.DescriptorProtos.DescriptorProto;
13 import com.google.protobuf.DescriptorProtos.Edition;
14 import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
15 import com.google.protobuf.DescriptorProtos.EnumOptions;
16 import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto;
17 import com.google.protobuf.DescriptorProtos.EnumValueOptions;
18 import com.google.protobuf.DescriptorProtos.FeatureSet;
19 import com.google.protobuf.DescriptorProtos.FeatureSetDefaults;
20 import com.google.protobuf.DescriptorProtos.FeatureSetDefaults.FeatureSetEditionDefault;
21 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
22 import com.google.protobuf.DescriptorProtos.FieldOptions;
23 import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
24 import com.google.protobuf.DescriptorProtos.FileOptions;
25 import com.google.protobuf.DescriptorProtos.MessageOptions;
26 import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
27 import com.google.protobuf.DescriptorProtos.MethodOptions;
28 import com.google.protobuf.DescriptorProtos.OneofDescriptorProto;
29 import com.google.protobuf.DescriptorProtos.OneofOptions;
30 import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
31 import com.google.protobuf.DescriptorProtos.ServiceOptions;
32 import com.google.protobuf.Descriptors.DescriptorValidationException;
33 import com.google.protobuf.JavaFeaturesProto.JavaFeatures;
34 import java.lang.ref.ReferenceQueue;
35 import java.lang.ref.WeakReference;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.Comparator;
40 import java.util.HashMap;
41 import java.util.IdentityHashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.concurrent.ConcurrentHashMap;
46 import java.util.logging.Logger;
47 
48 /**
49  * Contains a collection of classes which describe protocol message types.
50  *
51  * <p>Every message type has a {@link Descriptor}, which lists all its fields and other information
52  * about a type. You can get a message type's descriptor by calling {@code
53  * MessageType.getDescriptor()}, or (given a message object of the type) {@code
54  * message.getDescriptorForType()}. Furthermore, each message is associated with a {@link
55  * FileDescriptor} for a relevant {@code .proto} file. You can obtain it by calling {@code
56  * Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors for all the messages defined
57  * in that file, and file descriptors for all the imported {@code .proto} files.
58  *
59  * <p>Descriptors are built from DescriptorProtos, as defined in {@code
60  * google/protobuf/descriptor.proto}.
61  *
62  * @author kenton@google.com Kenton Varda
63  */
64 @CheckReturnValue
65 public final class Descriptors {
66   private static final Logger logger = Logger.getLogger(Descriptors.class.getName());
67   private static final int[] EMPTY_INT_ARRAY = new int[0];
68   private static final Descriptor[] EMPTY_DESCRIPTORS = new Descriptor[0];
69   private static final FieldDescriptor[] EMPTY_FIELD_DESCRIPTORS = new FieldDescriptor[0];
70   private static final EnumDescriptor[] EMPTY_ENUM_DESCRIPTORS = new EnumDescriptor[0];
71   private static final ServiceDescriptor[] EMPTY_SERVICE_DESCRIPTORS = new ServiceDescriptor[0];
72   private static final OneofDescriptor[] EMPTY_ONEOF_DESCRIPTORS = new OneofDescriptor[0];
73   private static final ConcurrentHashMap<Integer, FeatureSet> FEATURE_CACHE =
74       new ConcurrentHashMap<>();
75 
76   @SuppressWarnings("NonFinalStaticField")
77   private static volatile FeatureSetDefaults javaEditionDefaults = null;
78 
79   /** Sets the default feature mappings used during the build. Exposed for tests. */
setTestJavaEditionDefaults(FeatureSetDefaults defaults)80   static void setTestJavaEditionDefaults(FeatureSetDefaults defaults) {
81     javaEditionDefaults = defaults;
82   }
83 
84   /** Gets the default feature mappings used during the build. */
getJavaEditionDefaults()85   static FeatureSetDefaults getJavaEditionDefaults() {
86     // Force explicit initialization before synchronized block which can trigger initialization in
87     // `JavaFeaturesProto.registerAllExtensions()` and `FeatureSetdefaults.parseFrom()` calls.
88     // Otherwise, this can result in deadlock if another threads holds the static init block's
89     // implicit lock. This operation should be cheap if initialization has already occurred.
90     Descriptor unused1 = FeatureSetDefaults.getDescriptor();
91     FileDescriptor unused2 = JavaFeaturesProto.getDescriptor();
92     if (javaEditionDefaults == null) {
93       synchronized (Descriptors.class) {
94         if (javaEditionDefaults == null) {
95           try {
96             ExtensionRegistry registry = ExtensionRegistry.newInstance();
97             registry.add(JavaFeaturesProto.java_);
98             setTestJavaEditionDefaults(
99                 FeatureSetDefaults.parseFrom(
100                     JavaEditionDefaults.PROTOBUF_INTERNAL_JAVA_EDITION_DEFAULTS.getBytes(
101                         Internal.ISO_8859_1),
102                     registry));
103           } catch (Exception e) {
104             throw new AssertionError(e);
105           }
106         }
107       }
108     }
109     return javaEditionDefaults;
110   }
111 
getEditionDefaults(Edition edition)112   static FeatureSet getEditionDefaults(Edition edition) {
113     FeatureSetDefaults javaEditionDefaults = getJavaEditionDefaults();
114     if (edition.getNumber() < javaEditionDefaults.getMinimumEdition().getNumber()) {
115       throw new IllegalArgumentException(
116           "Edition "
117               + edition
118               + " is lower than the minimum supported edition "
119               + javaEditionDefaults.getMinimumEdition()
120               + "!");
121     }
122     if (edition.getNumber() > javaEditionDefaults.getMaximumEdition().getNumber()) {
123       throw new IllegalArgumentException(
124           "Edition "
125               + edition
126               + " is greater than the maximum supported edition "
127               + javaEditionDefaults.getMaximumEdition()
128               + "!");
129     }
130     FeatureSetEditionDefault found = null;
131     for (FeatureSetEditionDefault editionDefault : javaEditionDefaults.getDefaultsList()) {
132       if (editionDefault.getEdition().getNumber() > edition.getNumber()) {
133         break;
134       }
135       found = editionDefault;
136     }
137     if (found == null) {
138       throw new IllegalArgumentException(
139           "Edition " + edition + " does not have a valid default FeatureSet!");
140     }
141     return found.getFixedFeatures().toBuilder().mergeFrom(found.getOverridableFeatures()).build();
142   }
143 
internFeatures(FeatureSet features)144   private static FeatureSet internFeatures(FeatureSet features) {
145     FeatureSet cached = FEATURE_CACHE.putIfAbsent(features.hashCode(), features);
146     if (cached == null) {
147       return features;
148     }
149     return cached;
150   }
151 
152   /**
153    * Describes a {@code .proto} file, including everything defined within. That includes, in
154    * particular, descriptors for all the messages and file descriptors for all other imported {@code
155    * .proto} files (dependencies).
156    */
157   public static final class FileDescriptor extends GenericDescriptor {
158     /** Convert the descriptor to its protocol message representation. */
159     @Override
toProto()160     public FileDescriptorProto toProto() {
161       return proto;
162     }
163 
164     /** Get the file name. */
165     @Override
getName()166     public String getName() {
167       return proto.getName();
168     }
169 
170     /** Returns this object. */
171     @Override
getFile()172     public FileDescriptor getFile() {
173       return this;
174     }
175 
176     /** Returns the same as getName(). */
177     @Override
getFullName()178     public String getFullName() {
179       return proto.getName();
180     }
181 
182     /**
183      * Get the proto package name. This is the package name given by the {@code package} statement
184      * in the {@code .proto} file, which differs from the Java package.
185      */
getPackage()186     public String getPackage() {
187       return proto.getPackage();
188     }
189 
190     /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
getOptions()191     public FileOptions getOptions() {
192       if (this.options == null) {
193         FileOptions strippedOptions = this.proto.getOptions();
194         if (strippedOptions.hasFeatures()) {
195           // Clients should be using feature accessor methods, not accessing features on the
196           // options
197           // proto.
198           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
199         }
200         synchronized (this) {
201           if (this.options == null) {
202             this.options = strippedOptions;
203           }
204         }
205       }
206       return this.options;
207     }
208 
209     /** Get a list of top-level message types declared in this file. */
getMessageTypes()210     public List<Descriptor> getMessageTypes() {
211       return Collections.unmodifiableList(Arrays.asList(messageTypes));
212     }
213 
214     /** Get a list of top-level enum types declared in this file. */
getEnumTypes()215     public List<EnumDescriptor> getEnumTypes() {
216       return Collections.unmodifiableList(Arrays.asList(enumTypes));
217     }
218 
219     /** Get a list of top-level services declared in this file. */
getServices()220     public List<ServiceDescriptor> getServices() {
221       return Collections.unmodifiableList(Arrays.asList(services));
222     }
223 
224     /** Get a list of top-level extensions declared in this file. */
getExtensions()225     public List<FieldDescriptor> getExtensions() {
226       return Collections.unmodifiableList(Arrays.asList(extensions));
227     }
228 
229     /** Get a list of this file's dependencies (imports). */
getDependencies()230     public List<FileDescriptor> getDependencies() {
231       return Collections.unmodifiableList(Arrays.asList(dependencies));
232     }
233 
234     /** Get a list of this file's public dependencies (public imports). */
getPublicDependencies()235     public List<FileDescriptor> getPublicDependencies() {
236       return Collections.unmodifiableList(Arrays.asList(publicDependencies));
237     }
238 
239     /** Get the edition of the .proto file. */
getEdition()240     Edition getEdition() {
241       switch (proto.getSyntax()) {
242         case "editions":
243           return proto.getEdition();
244         case "proto3":
245           return Edition.EDITION_PROTO3;
246         default:
247           return Edition.EDITION_PROTO2;
248       }
249     }
250 
copyHeadingTo(FileDescriptorProto.Builder protoBuilder)251     public void copyHeadingTo(FileDescriptorProto.Builder protoBuilder) {
252       protoBuilder.setName(getName()).setSyntax(proto.getSyntax());
253       if (!getPackage().isEmpty()) {
254         protoBuilder.setPackage(getPackage());
255       }
256       if (proto.getSyntax().equals("editions")) {
257         protoBuilder.setEdition(proto.getEdition());
258       }
259       if (proto.hasOptions() && !proto.getOptions().equals(FileOptions.getDefaultInstance())) {
260         protoBuilder.setOptions(proto.getOptions());
261       }
262     }
263 
264     /**
265      * Find a message type in the file by name. Does not find nested types.
266      *
267      * @param name The unqualified type name to look for.
268      * @return The message type's descriptor, or {@code null} if not found.
269      */
findMessageTypeByName(String name)270     public Descriptor findMessageTypeByName(String name) {
271       // Don't allow looking up nested types.  This will make optimization
272       // easier later.
273       if (name.indexOf('.') != -1) {
274         return null;
275       }
276       final String packageName = getPackage();
277       if (!packageName.isEmpty()) {
278         name = packageName + '.' + name;
279       }
280       final GenericDescriptor result = pool.findSymbol(name);
281       if (result instanceof Descriptor && result.getFile() == this) {
282         return (Descriptor) result;
283       } else {
284         return null;
285       }
286     }
287 
288     /**
289      * Find an enum type in the file by name. Does not find nested types.
290      *
291      * @param name The unqualified type name to look for.
292      * @return The enum type's descriptor, or {@code null} if not found.
293      */
findEnumTypeByName(String name)294     public EnumDescriptor findEnumTypeByName(String name) {
295       // Don't allow looking up nested types.  This will make optimization
296       // easier later.
297       if (name.indexOf('.') != -1) {
298         return null;
299       }
300       final String packageName = getPackage();
301       if (!packageName.isEmpty()) {
302         name = packageName + '.' + name;
303       }
304       final GenericDescriptor result = pool.findSymbol(name);
305       if (result instanceof EnumDescriptor && result.getFile() == this) {
306         return (EnumDescriptor) result;
307       } else {
308         return null;
309       }
310     }
311 
312     /**
313      * Find a service type in the file by name.
314      *
315      * @param name The unqualified type name to look for.
316      * @return The service type's descriptor, or {@code null} if not found.
317      */
findServiceByName(String name)318     public ServiceDescriptor findServiceByName(String name) {
319       // Don't allow looking up nested types.  This will make optimization
320       // easier later.
321       if (name.indexOf('.') != -1) {
322         return null;
323       }
324       final String packageName = getPackage();
325       if (!packageName.isEmpty()) {
326         name = packageName + '.' + name;
327       }
328       final GenericDescriptor result = pool.findSymbol(name);
329       if (result instanceof ServiceDescriptor && result.getFile() == this) {
330         return (ServiceDescriptor) result;
331       } else {
332         return null;
333       }
334     }
335 
336     /**
337      * Find an extension in the file by name. Does not find extensions nested inside message types.
338      *
339      * @param name The unqualified extension name to look for.
340      * @return The extension's descriptor, or {@code null} if not found.
341      */
findExtensionByName(String name)342     public FieldDescriptor findExtensionByName(String name) {
343       if (name.indexOf('.') != -1) {
344         return null;
345       }
346       final String packageName = getPackage();
347       if (!packageName.isEmpty()) {
348         name = packageName + '.' + name;
349       }
350       final GenericDescriptor result = pool.findSymbol(name);
351       if (result instanceof FieldDescriptor && result.getFile() == this) {
352         return (FieldDescriptor) result;
353       } else {
354         return null;
355       }
356     }
357 
358     /**
359      * Construct a {@code FileDescriptor}.
360      *
361      * @param proto the protocol message form of the FileDescriptort
362      * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies.
363      * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur
364      *     for a number of reasons; for instance, because a field has an undefined type or because
365      *     two messages were defined with the same name.
366      */
buildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)367     public static FileDescriptor buildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
368         throws DescriptorValidationException {
369       return buildFrom(proto, dependencies, false);
370     }
371 
372     /**
373      * Construct a {@code FileDescriptor}.
374      *
375      * @param proto the protocol message form of the FileDescriptor
376      * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies
377      * @param allowUnknownDependencies if true, non-existing dependencies will be ignored and
378      *     undefined message types will be replaced with a placeholder type. Undefined enum types
379      *     still cause a DescriptorValidationException.
380      * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur
381      *     for a number of reasons; for instance, because a field has an undefined type or because
382      *     two messages were defined with the same name.
383      */
buildFrom( FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies)384     public static FileDescriptor buildFrom(
385         FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies)
386         throws DescriptorValidationException {
387       return buildFrom(proto, dependencies, allowUnknownDependencies, false);
388     }
389 
buildFrom( FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies, boolean allowUnresolvedFeatures)390     private static FileDescriptor buildFrom(
391         FileDescriptorProto proto,
392         FileDescriptor[] dependencies,
393         boolean allowUnknownDependencies,
394         boolean allowUnresolvedFeatures)
395         throws DescriptorValidationException {
396       // Building descriptors involves two steps:  translating and linking.
397       // In the translation step (implemented by FileDescriptor's
398       // constructor), we build an object tree mirroring the
399       // FileDescriptorProto's tree and put all of the descriptors into the
400       // DescriptorPool's lookup tables.  In the linking step, we look up all
401       // type references in the DescriptorPool, so that, for example, a
402       // FieldDescriptor for an embedded message contains a pointer directly
403       // to the Descriptor for that message's type.  We also detect undefined
404       // types in the linking step.
405       DescriptorPool pool = new DescriptorPool(dependencies, allowUnknownDependencies);
406       FileDescriptor result =
407           new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
408       result.crossLink();
409       // Skip feature resolution until later for calls from gencode.
410       if (!allowUnresolvedFeatures) {
411         // We do not need to force feature resolution for proto1 dependencies
412         // since dependencies from non-gencode should already be fully feature resolved.
413         result.resolveAllFeaturesInternal();
414       }
415       return result;
416     }
417 
latin1Cat(final String[] strings)418     private static byte[] latin1Cat(final String[] strings) {
419       // Hack:  We can't embed a raw byte array inside generated Java code
420       //   (at least, not efficiently), but we can embed Strings.  So, the
421       //   protocol compiler embeds the FileDescriptorProto as a giant
422       //   string literal which is passed to this function to construct the
423       //   file's FileDescriptor.  The string literal contains only 8-bit
424       //   characters, each one representing a byte of the FileDescriptorProto's
425       //   serialized form.  So, if we convert it to bytes in ISO-8859-1, we
426       //   should get the original bytes that we want.
427       // Literal strings are limited to 64k, so it may be split into multiple strings.
428       if (strings.length == 1) {
429         return strings[0].getBytes(Internal.ISO_8859_1);
430       }
431       StringBuilder descriptorData = new StringBuilder();
432       for (String part : strings) {
433         descriptorData.append(part);
434       }
435       return descriptorData.toString().getBytes(Internal.ISO_8859_1);
436     }
437 
findDescriptors( final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)438     private static FileDescriptor[] findDescriptors(
439         final Class<?> descriptorOuterClass,
440         final String[] dependencyClassNames,
441         final String[] dependencyFileNames) {
442       List<FileDescriptor> descriptors = new ArrayList<>();
443       for (int i = 0; i < dependencyClassNames.length; i++) {
444         try {
445           Class<?> clazz = descriptorOuterClass.getClassLoader().loadClass(dependencyClassNames[i]);
446           descriptors.add((FileDescriptor) clazz.getField("descriptor").get(null));
447         } catch (Exception e) {
448           // We allow unknown dependencies by default. If a dependency cannot
449           // be found we only generate a warning.
450           logger.warning("Descriptors for \"" + dependencyFileNames[i] + "\" can not be found.");
451         }
452       }
453       return descriptors.toArray(new FileDescriptor[0]);
454     }
455 
456     /**
457      * This method is to be called by generated code only. It is equivalent to {@code buildFrom}
458      * except that the {@code FileDescriptorProto} is encoded in protocol buffer wire format.
459      */
internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final FileDescriptor[] dependencies)460     public static FileDescriptor internalBuildGeneratedFileFrom(
461         final String[] descriptorDataParts, final FileDescriptor[] dependencies) {
462       final byte[] descriptorBytes = latin1Cat(descriptorDataParts);
463 
464       FileDescriptorProto proto;
465       try {
466         proto = FileDescriptorProto.parseFrom(descriptorBytes);
467       } catch (InvalidProtocolBufferException e) {
468         throw new IllegalArgumentException(
469             "Failed to parse protocol buffer descriptor for generated code.", e);
470       }
471 
472       try {
473         // When building descriptors for generated code, we allow unknown
474         // dependencies by default and delay feature resolution until later.
475         return buildFrom(proto, dependencies, true, true);
476       } catch (DescriptorValidationException e) {
477         throw new IllegalArgumentException(
478             "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
479       }
480     }
481 
482     /**
483      * This method is to be called by generated code only. It uses Java reflection to load the
484      * dependencies' descriptors.
485      */
internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)486     public static FileDescriptor internalBuildGeneratedFileFrom(
487         final String[] descriptorDataParts,
488         final Class<?> descriptorOuterClass,
489         final String[] dependencyClassNames,
490         final String[] dependencyFileNames) {
491       FileDescriptor[] dependencies =
492           findDescriptors(descriptorOuterClass, dependencyClassNames, dependencyFileNames);
493       return internalBuildGeneratedFileFrom(descriptorDataParts, dependencies);
494     }
495 
496     /**
497      * This method is to be called by generated code only. It updates the FileDescriptorProto
498      * associated with the descriptor by parsing it again with the given ExtensionRegistry. This is
499      * needed to recognize custom options.
500      */
internalUpdateFileDescriptor( FileDescriptor descriptor, ExtensionRegistry registry)501     public static void internalUpdateFileDescriptor(
502         FileDescriptor descriptor, ExtensionRegistry registry) {
503       ByteString bytes = descriptor.proto.toByteString();
504       try {
505         FileDescriptorProto proto = FileDescriptorProto.parseFrom(bytes, registry);
506         descriptor.setProto(proto);
507       } catch (InvalidProtocolBufferException e) {
508         throw new IllegalArgumentException(
509             "Failed to parse protocol buffer descriptor for generated code.", e);
510       }
511     }
512 
513     /**
514      * This class should be used by generated code only. When calling {@link
515      * FileDescriptor#internalBuildGeneratedFileFrom}, the caller provides a callback implementing
516      * this interface. The callback is called after the FileDescriptor has been constructed, in
517      * order to assign all the global variables defined in the generated code which point at parts
518      * of the FileDescriptor. The callback returns an ExtensionRegistry which contains any
519      * extensions which might be used in the descriptor -- that is, extensions of the various
520      * "Options" messages defined in descriptor.proto. The callback may also return null to indicate
521      * that no extensions are used in the descriptor.
522      *
523      * <p>This interface is deprecated. Use the return value of internalBuildGeneratedFrom()
524      * instead.
525      */
526     @Deprecated
527     public interface InternalDescriptorAssigner {
assignDescriptors(FileDescriptor root)528       ExtensionRegistry assignDescriptors(FileDescriptor root);
529     }
530 
531     private FileDescriptorProto proto;
532     private volatile FileOptions options;
533     private final Descriptor[] messageTypes;
534     private final EnumDescriptor[] enumTypes;
535     private final ServiceDescriptor[] services;
536     private final FieldDescriptor[] extensions;
537     private final FileDescriptor[] dependencies;
538     private final FileDescriptor[] publicDependencies;
539     private final DescriptorPool pool;
540     private boolean featuresResolved;
541 
FileDescriptor( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final DescriptorPool pool, boolean allowUnknownDependencies)542     private FileDescriptor(
543         final FileDescriptorProto proto,
544         final FileDescriptor[] dependencies,
545         final DescriptorPool pool,
546         boolean allowUnknownDependencies)
547         throws DescriptorValidationException {
548       this.pool = pool;
549       this.proto = proto;
550       this.dependencies = dependencies.clone();
551       this.featuresResolved = false;
552       HashMap<String, FileDescriptor> nameToFileMap = new HashMap<>();
553       for (FileDescriptor file : dependencies) {
554         nameToFileMap.put(file.getName(), file);
555       }
556       List<FileDescriptor> publicDependencies = new ArrayList<>();
557       for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
558         int index = proto.getPublicDependency(i);
559         if (index < 0 || index >= proto.getDependencyCount()) {
560           throw new DescriptorValidationException(this, "Invalid public dependency index.");
561         }
562         String name = proto.getDependency(index);
563         FileDescriptor file = nameToFileMap.get(name);
564         if (file == null) {
565           if (!allowUnknownDependencies) {
566             throw new DescriptorValidationException(this, "Invalid public dependency: " + name);
567           }
568           // Ignore unknown dependencies.
569         } else {
570           publicDependencies.add(file);
571         }
572       }
573       this.publicDependencies = new FileDescriptor[publicDependencies.size()];
574       publicDependencies.toArray(this.publicDependencies);
575 
576       pool.addPackage(getPackage(), this);
577 
578       messageTypes =
579           (proto.getMessageTypeCount() > 0)
580               ? new Descriptor[proto.getMessageTypeCount()]
581               : EMPTY_DESCRIPTORS;
582       for (int i = 0; i < proto.getMessageTypeCount(); i++) {
583         messageTypes[i] = new Descriptor(proto.getMessageType(i), this, null, i);
584       }
585 
586       enumTypes =
587           (proto.getEnumTypeCount() > 0)
588               ? new EnumDescriptor[proto.getEnumTypeCount()]
589               : EMPTY_ENUM_DESCRIPTORS;
590       for (int i = 0; i < proto.getEnumTypeCount(); i++) {
591         enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i);
592       }
593 
594       services =
595           (proto.getServiceCount() > 0)
596               ? new ServiceDescriptor[proto.getServiceCount()]
597               : EMPTY_SERVICE_DESCRIPTORS;
598       for (int i = 0; i < proto.getServiceCount(); i++) {
599         services[i] = new ServiceDescriptor(proto.getService(i), this, i);
600       }
601 
602       extensions =
603           (proto.getExtensionCount() > 0)
604               ? new FieldDescriptor[proto.getExtensionCount()]
605               : EMPTY_FIELD_DESCRIPTORS;
606       for (int i = 0; i < proto.getExtensionCount(); i++) {
607         extensions[i] = new FieldDescriptor(proto.getExtension(i), this, null, i, true);
608       }
609     }
610 
611     /** Create a placeholder FileDescriptor for a message Descriptor. */
FileDescriptor(String packageName, Descriptor message)612     FileDescriptor(String packageName, Descriptor message) throws DescriptorValidationException {
613       this.parent = null;
614       this.pool = new DescriptorPool(new FileDescriptor[0], true);
615       this.proto =
616           FileDescriptorProto.newBuilder()
617               .setName(message.getFullName() + ".placeholder.proto")
618               .setPackage(packageName)
619               .addMessageType(message.toProto())
620               .build();
621       this.dependencies = new FileDescriptor[0];
622       this.publicDependencies = new FileDescriptor[0];
623       this.featuresResolved = false;
624 
625       messageTypes = new Descriptor[] {message};
626       enumTypes = EMPTY_ENUM_DESCRIPTORS;
627       services = EMPTY_SERVICE_DESCRIPTORS;
628       extensions = EMPTY_FIELD_DESCRIPTORS;
629 
630       pool.addPackage(packageName, this);
631       pool.addSymbol(message);
632     }
633 
resolveAllFeaturesImmutable()634     public void resolveAllFeaturesImmutable() {
635       try {
636         resolveAllFeaturesInternal();
637       } catch (DescriptorValidationException e) {
638         throw new IllegalArgumentException("Invalid features for \"" + proto.getName() + "\".", e);
639       }
640     }
641 
642     /**
643      * This method is to be called by generated code only. It resolves features for the descriptor
644      * and all of its children.
645      */
resolveAllFeaturesInternal()646     private void resolveAllFeaturesInternal() throws DescriptorValidationException {
647       if (this.featuresResolved) {
648         return;
649       }
650 
651       synchronized (this) {
652         if (this.featuresResolved) {
653           return;
654         }
655         resolveFeatures(proto.getOptions().getFeatures());
656 
657         for (Descriptor messageType : messageTypes) {
658           messageType.resolveAllFeatures();
659         }
660 
661         for (EnumDescriptor enumType : enumTypes) {
662           enumType.resolveAllFeatures();
663         }
664 
665         for (ServiceDescriptor service : services) {
666           service.resolveAllFeatures();
667         }
668 
669         for (FieldDescriptor extension : extensions) {
670           extension.resolveAllFeatures();
671         }
672         this.featuresResolved = true;
673       }
674     }
675 
676     @Override
inferLegacyProtoFeatures()677     FeatureSet inferLegacyProtoFeatures() {
678       FeatureSet.Builder features = FeatureSet.newBuilder();
679       if (getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) {
680         return features.build();
681       }
682 
683       if (getEdition() == Edition.EDITION_PROTO2) {
684         if (proto.getOptions().getJavaStringCheckUtf8()) {
685           features.setExtension(
686               JavaFeaturesProto.java_,
687               JavaFeatures.newBuilder()
688                   .setUtf8Validation(JavaFeatures.Utf8Validation.VERIFY)
689                   .build());
690         }
691       }
692       return features.build();
693     }
694 
695     @Override
hasInferredLegacyProtoFeatures()696     boolean hasInferredLegacyProtoFeatures() {
697       if (getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) {
698         return false;
699       }
700       if (getEdition() == Edition.EDITION_PROTO2) {
701         if (proto.getOptions().getJavaStringCheckUtf8()) {
702           return true;
703         }
704       }
705       return false;
706     }
707 
708     /** Look up and cross-link all field types, etc. */
crossLink()709     private void crossLink() throws DescriptorValidationException {
710       for (final Descriptor messageType : messageTypes) {
711         messageType.crossLink();
712       }
713 
714       for (final ServiceDescriptor service : services) {
715         service.crossLink();
716       }
717 
718       for (final FieldDescriptor extension : extensions) {
719         extension.crossLink();
720       }
721     }
722 
723     /**
724      * Replace our {@link FileDescriptorProto} with the given one, which is identical except that it
725      * might contain extensions that weren't present in the original. This method is needed for
726      * bootstrapping when a file defines custom options. The options may be defined in the file
727      * itself, so we can't actually parse them until we've constructed the descriptors, but to
728      * construct the descriptors we have to have parsed the descriptor protos. So, we have to parse
729      * the descriptor protos a second time after constructing the descriptors.
730      */
setProto(final FileDescriptorProto proto)731     private synchronized void setProto(final FileDescriptorProto proto) {
732       this.proto = proto;
733       this.options = null;
734       try {
735         resolveFeatures(proto.getOptions().getFeatures());
736 
737         for (int i = 0; i < messageTypes.length; i++) {
738           messageTypes[i].setProto(proto.getMessageType(i));
739         }
740 
741         for (int i = 0; i < enumTypes.length; i++) {
742           enumTypes[i].setProto(proto.getEnumType(i));
743         }
744 
745         for (int i = 0; i < services.length; i++) {
746           services[i].setProto(proto.getService(i));
747         }
748 
749         for (int i = 0; i < extensions.length; i++) {
750           extensions[i].setProto(proto.getExtension(i));
751         }
752       } catch (DescriptorValidationException e) {
753         throw new IllegalArgumentException("Invalid features for \"" + proto.getName() + "\".", e);
754       }
755     }
756   }
757 
758   // =================================================================
759 
760   /** Describes a message type. */
761   public static final class Descriptor extends GenericDescriptor {
762     /**
763      * Get the index of this descriptor within its parent. In other words, given a {@link
764      * FileDescriptor} {@code file}, the following is true:
765      *
766      * <pre>
767      *   for all i in [0, file.getMessageTypeCount()):
768      *     file.getMessageType(i).getIndex() == i
769      * </pre>
770      *
771      * Similarly, for a {@link Descriptor} {@code messageType}:
772      *
773      * <pre>
774      *   for all i in [0, messageType.getNestedTypeCount()):
775      *     messageType.getNestedType(i).getIndex() == i
776      * </pre>
777      */
getIndex()778     public int getIndex() {
779       return index;
780     }
781 
782     /** Convert the descriptor to its protocol message representation. */
783     @Override
toProto()784     public DescriptorProto toProto() {
785       return proto;
786     }
787 
788     /** Get the type's unqualified name. */
789     @Override
getName()790     public String getName() {
791       return proto.getName();
792     }
793 
794     /**
795      * Get the type's fully-qualified name, within the proto language's namespace. This differs from
796      * the Java name. For example, given this {@code .proto}:
797      *
798      * <pre>
799      *   package foo.bar;
800      *   option java_package = "com.example.protos"
801      *   message Baz {}
802      * </pre>
803      *
804      * {@code Baz}'s full name is "foo.bar.Baz".
805      */
806     @Override
getFullName()807     public String getFullName() {
808       return fullName;
809     }
810 
811     /** Get the {@link FileDescriptor} containing this descriptor. */
812     @Override
getFile()813     public FileDescriptor getFile() {
814       return file;
815     }
816 
817     /** If this is a nested type, get the outer descriptor, otherwise null. */
getContainingType()818     public Descriptor getContainingType() {
819       return containingType;
820     }
821 
822     /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
getOptions()823     public MessageOptions getOptions() {
824       if (this.options == null) {
825         MessageOptions strippedOptions = this.proto.getOptions();
826         if (strippedOptions.hasFeatures()) {
827           // Clients should be using feature accessor methods, not accessing features on the
828           // options
829           // proto.
830           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
831         }
832         synchronized (this) {
833           if (this.options == null) {
834             this.options = strippedOptions;
835           }
836         }
837       }
838       return this.options;
839     }
840 
841     /** Get a list of this message type's fields. */
getFields()842     public List<FieldDescriptor> getFields() {
843       return Collections.unmodifiableList(Arrays.asList(fields));
844     }
845 
846     /** Get a list of this message type's oneofs. */
getOneofs()847     public List<OneofDescriptor> getOneofs() {
848       return Collections.unmodifiableList(Arrays.asList(oneofs));
849     }
850 
851     /** Get a list of this message type's real oneofs. */
getRealOneofs()852     public List<OneofDescriptor> getRealOneofs() {
853       return Collections.unmodifiableList(Arrays.asList(oneofs).subList(0, realOneofCount));
854     }
855 
856     /** Get a list of this message type's extensions. */
getExtensions()857     public List<FieldDescriptor> getExtensions() {
858       return Collections.unmodifiableList(Arrays.asList(extensions));
859     }
860 
861     /** Get a list of message types nested within this one. */
getNestedTypes()862     public List<Descriptor> getNestedTypes() {
863       return Collections.unmodifiableList(Arrays.asList(nestedTypes));
864     }
865 
866     /** Get a list of enum types nested within this one. */
getEnumTypes()867     public List<EnumDescriptor> getEnumTypes() {
868       return Collections.unmodifiableList(Arrays.asList(enumTypes));
869     }
870 
871     /** Determines if the given field number is an extension. */
isExtensionNumber(final int number)872     public boolean isExtensionNumber(final int number) {
873       int index = Arrays.binarySearch(extensionRangeLowerBounds, number);
874       if (index < 0) {
875         index = ~index - 1;
876       }
877       // extensionRangeLowerBounds[index] is the biggest value <= number
878       return index >= 0 && number < extensionRangeUpperBounds[index];
879     }
880 
881     /** Determines if the given field number is reserved. */
isReservedNumber(final int number)882     public boolean isReservedNumber(final int number) {
883       for (final DescriptorProto.ReservedRange range : proto.getReservedRangeList()) {
884         if (range.getStart() <= number && number < range.getEnd()) {
885           return true;
886         }
887       }
888       return false;
889     }
890 
891     /** Determines if the given field name is reserved. */
isReservedName(final String name)892     public boolean isReservedName(final String name) {
893       checkNotNull(name);
894       for (final String reservedName : proto.getReservedNameList()) {
895         if (reservedName.equals(name)) {
896           return true;
897         }
898       }
899       return false;
900     }
901 
902     /**
903      * Indicates whether the message can be extended. That is, whether it has any "extensions x to
904      * y" ranges declared on it.
905      */
isExtendable()906     public boolean isExtendable() {
907       return !proto.getExtensionRangeList().isEmpty();
908     }
909 
910     /**
911      * Finds a field by name.
912      *
913      * @param name The unqualified name of the field (e.g. "foo"). For protocol buffer messages that
914      *     follow <a
915      *     href=https://developers.google.com/protocol-buffers/docs/style#message_and_field_names>Google's
916      *     guidance on naming</a> this will be a snake case string, such as
917      *     <pre>song_name</pre>
918      *     .
919      * @return The field's descriptor, or {@code null} if not found.
920      */
findFieldByName(final String name)921     public FieldDescriptor findFieldByName(final String name) {
922       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
923       if (result instanceof FieldDescriptor) {
924         return (FieldDescriptor) result;
925       } else {
926         return null;
927       }
928     }
929 
930     /**
931      * Finds a field by field number.
932      *
933      * @param number The field number within this message type.
934      * @return The field's descriptor, or {@code null} if not found.
935      */
findFieldByNumber(final int number)936     public FieldDescriptor findFieldByNumber(final int number) {
937       return binarySearch(
938           fieldsSortedByNumber, fieldsSortedByNumber.length, FieldDescriptor.NUMBER_GETTER, number);
939     }
940 
941     /**
942      * Finds a nested message type by name.
943      *
944      * @param name The unqualified name of the nested type such as "Foo"
945      * @return The types's descriptor, or {@code null} if not found.
946      */
findNestedTypeByName(final String name)947     public Descriptor findNestedTypeByName(final String name) {
948       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
949       if (result instanceof Descriptor) {
950         return (Descriptor) result;
951       } else {
952         return null;
953       }
954     }
955 
956     /**
957      * Finds a nested enum type by name.
958      *
959      * @param name The unqualified name of the nested type such as "Foo"
960      * @return The types's descriptor, or {@code null} if not found.
961      */
findEnumTypeByName(final String name)962     public EnumDescriptor findEnumTypeByName(final String name) {
963       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
964       if (result instanceof EnumDescriptor) {
965         return (EnumDescriptor) result;
966       } else {
967         return null;
968       }
969     }
970 
971     private final int index;
972     private DescriptorProto proto;
973     private volatile MessageOptions options;
974     private final String fullName;
975     private final FileDescriptor file;
976     private final Descriptor containingType;
977     private final Descriptor[] nestedTypes;
978     private final EnumDescriptor[] enumTypes;
979     private final FieldDescriptor[] fields;
980     private final FieldDescriptor[] fieldsSortedByNumber;
981     private final FieldDescriptor[] extensions;
982     private final OneofDescriptor[] oneofs;
983     private final int realOneofCount;
984 
985     private final int[] extensionRangeLowerBounds;
986     private final int[] extensionRangeUpperBounds;
987 
988     // Used to create a placeholder when the type cannot be found.
Descriptor(final String fullname)989     Descriptor(final String fullname) throws DescriptorValidationException {
990       String name = fullname;
991       String packageName = "";
992       int pos = fullname.lastIndexOf('.');
993       if (pos != -1) {
994         name = fullname.substring(pos + 1);
995         packageName = fullname.substring(0, pos);
996       }
997       this.index = 0;
998       this.proto =
999           DescriptorProto.newBuilder()
1000               .setName(name)
1001               .addExtensionRange(
1002                   DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build())
1003               .build();
1004       this.fullName = fullname;
1005       this.containingType = null;
1006 
1007       this.nestedTypes = EMPTY_DESCRIPTORS;
1008       this.enumTypes = EMPTY_ENUM_DESCRIPTORS;
1009       this.fields = EMPTY_FIELD_DESCRIPTORS;
1010       this.fieldsSortedByNumber = EMPTY_FIELD_DESCRIPTORS;
1011       this.extensions = EMPTY_FIELD_DESCRIPTORS;
1012       this.oneofs = EMPTY_ONEOF_DESCRIPTORS;
1013       this.realOneofCount = 0;
1014 
1015       // Create a placeholder FileDescriptor to hold this message.
1016       this.file = new FileDescriptor(packageName, this);
1017       this.parent = this.file;
1018 
1019       extensionRangeLowerBounds = new int[] {1};
1020       extensionRangeUpperBounds = new int[] {536870912};
1021     }
1022 
Descriptor( final DescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)1023     private Descriptor(
1024         final DescriptorProto proto,
1025         final FileDescriptor file,
1026         final Descriptor parent,
1027         final int index)
1028         throws DescriptorValidationException {
1029       if (parent == null) {
1030         this.parent = file;
1031       } else {
1032         this.parent = parent;
1033       }
1034       this.index = index;
1035       this.proto = proto;
1036       fullName = computeFullName(file, parent, proto.getName());
1037       this.file = file;
1038       containingType = parent;
1039 
1040       oneofs =
1041           (proto.getOneofDeclCount() > 0)
1042               ? new OneofDescriptor[proto.getOneofDeclCount()]
1043               : EMPTY_ONEOF_DESCRIPTORS;
1044       for (int i = 0; i < proto.getOneofDeclCount(); i++) {
1045         oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i);
1046       }
1047 
1048       nestedTypes =
1049           (proto.getNestedTypeCount() > 0)
1050               ? new Descriptor[proto.getNestedTypeCount()]
1051               : EMPTY_DESCRIPTORS;
1052       for (int i = 0; i < proto.getNestedTypeCount(); i++) {
1053         nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i);
1054       }
1055 
1056       enumTypes =
1057           (proto.getEnumTypeCount() > 0)
1058               ? new EnumDescriptor[proto.getEnumTypeCount()]
1059               : EMPTY_ENUM_DESCRIPTORS;
1060       for (int i = 0; i < proto.getEnumTypeCount(); i++) {
1061         enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i);
1062       }
1063 
1064       fields =
1065           (proto.getFieldCount() > 0)
1066               ? new FieldDescriptor[proto.getFieldCount()]
1067               : EMPTY_FIELD_DESCRIPTORS;
1068       for (int i = 0; i < proto.getFieldCount(); i++) {
1069         fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false);
1070       }
1071       this.fieldsSortedByNumber =
1072           (proto.getFieldCount() > 0) ? fields.clone() : EMPTY_FIELD_DESCRIPTORS;
1073 
1074       extensions =
1075           (proto.getExtensionCount() > 0)
1076               ? new FieldDescriptor[proto.getExtensionCount()]
1077               : EMPTY_FIELD_DESCRIPTORS;
1078       for (int i = 0; i < proto.getExtensionCount(); i++) {
1079         extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true);
1080       }
1081 
1082       for (int i = 0; i < proto.getOneofDeclCount(); i++) {
1083         oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()];
1084         oneofs[i].fieldCount = 0;
1085       }
1086       for (int i = 0; i < proto.getFieldCount(); i++) {
1087         OneofDescriptor oneofDescriptor = fields[i].getContainingOneof();
1088         if (oneofDescriptor != null) {
1089           oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
1090         }
1091       }
1092 
1093       int syntheticOneofCount = 0;
1094       for (OneofDescriptor oneof : this.oneofs) {
1095         if (oneof.isSynthetic()) {
1096           syntheticOneofCount++;
1097         } else {
1098           if (syntheticOneofCount > 0) {
1099             throw new DescriptorValidationException(this, "Synthetic oneofs must come last.");
1100           }
1101         }
1102       }
1103       this.realOneofCount = this.oneofs.length - syntheticOneofCount;
1104 
1105       file.pool.addSymbol(this);
1106 
1107       // NOTE: The defined extension ranges are guaranteed to be disjoint.
1108       if (proto.getExtensionRangeCount() > 0) {
1109         extensionRangeLowerBounds = new int[proto.getExtensionRangeCount()];
1110         extensionRangeUpperBounds = new int[proto.getExtensionRangeCount()];
1111         int i = 0;
1112         for (final DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) {
1113           extensionRangeLowerBounds[i] = range.getStart();
1114           extensionRangeUpperBounds[i] = range.getEnd();
1115           i++;
1116         }
1117         // Since the ranges are disjoint, sorting these independently must still produce the correct
1118         // order.
1119         Arrays.sort(extensionRangeLowerBounds);
1120         Arrays.sort(extensionRangeUpperBounds);
1121       } else {
1122         extensionRangeLowerBounds = EMPTY_INT_ARRAY;
1123         extensionRangeUpperBounds = EMPTY_INT_ARRAY;
1124       }
1125     }
1126 
1127     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()1128     private void resolveAllFeatures() throws DescriptorValidationException {
1129       resolveFeatures(proto.getOptions().getFeatures());
1130 
1131       for (Descriptor nestedType : nestedTypes) {
1132         nestedType.resolveAllFeatures();
1133       }
1134 
1135       for (EnumDescriptor enumType : enumTypes) {
1136         enumType.resolveAllFeatures();
1137       }
1138 
1139       // Oneofs must be resolved before any children oneof fields.
1140       for (OneofDescriptor oneof : oneofs) {
1141         oneof.resolveAllFeatures();
1142       }
1143 
1144       for (FieldDescriptor field : fields) {
1145         field.resolveAllFeatures();
1146       }
1147 
1148       for (FieldDescriptor extension : extensions) {
1149         extension.resolveAllFeatures();
1150       }
1151     }
1152 
1153     /** Look up and cross-link all field types, etc. */
crossLink()1154     private void crossLink() throws DescriptorValidationException {
1155       for (final Descriptor nestedType : nestedTypes) {
1156         nestedType.crossLink();
1157       }
1158 
1159       for (final FieldDescriptor field : fields) {
1160         field.crossLink();
1161       }
1162       Arrays.sort(fieldsSortedByNumber);
1163       validateNoDuplicateFieldNumbers();
1164 
1165       for (final FieldDescriptor extension : extensions) {
1166         extension.crossLink();
1167       }
1168     }
1169 
validateNoDuplicateFieldNumbers()1170     private void validateNoDuplicateFieldNumbers() throws DescriptorValidationException {
1171       for (int i = 0; i + 1 < fieldsSortedByNumber.length; i++) {
1172         FieldDescriptor old = fieldsSortedByNumber[i];
1173         FieldDescriptor field = fieldsSortedByNumber[i + 1];
1174         if (old.getNumber() == field.getNumber()) {
1175           throw new DescriptorValidationException(
1176               field,
1177               "Field number "
1178                   + field.getNumber()
1179                   + " has already been used in \""
1180                   + field.getContainingType().getFullName()
1181                   + "\" by field \""
1182                   + old.getName()
1183                   + "\".");
1184         }
1185       }
1186     }
1187 
1188     /** See {@link FileDescriptor#setProto}. */
setProto(final DescriptorProto proto)1189     private void setProto(final DescriptorProto proto) throws DescriptorValidationException {
1190       this.proto = proto;
1191       this.options = null;
1192       resolveFeatures(proto.getOptions().getFeatures());
1193 
1194       for (int i = 0; i < nestedTypes.length; i++) {
1195         nestedTypes[i].setProto(proto.getNestedType(i));
1196       }
1197 
1198       for (int i = 0; i < oneofs.length; i++) {
1199         oneofs[i].setProto(proto.getOneofDecl(i));
1200       }
1201 
1202       for (int i = 0; i < enumTypes.length; i++) {
1203         enumTypes[i].setProto(proto.getEnumType(i));
1204       }
1205 
1206       for (int i = 0; i < fields.length; i++) {
1207         fields[i].setProto(proto.getField(i));
1208       }
1209 
1210       for (int i = 0; i < extensions.length; i++) {
1211         extensions[i].setProto(proto.getExtension(i));
1212       }
1213     }
1214   }
1215 
1216   // =================================================================
1217 
1218   /** Describes a field of a message type. */
1219   public static final class FieldDescriptor extends GenericDescriptor
1220       implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> {
1221     private static final NumberGetter<FieldDescriptor> NUMBER_GETTER =
1222         new NumberGetter<FieldDescriptor>() {
1223           @Override
1224           public int getNumber(FieldDescriptor fieldDescriptor) {
1225             return fieldDescriptor.getNumber();
1226           }
1227         };
1228 
1229     /**
1230      * Get the index of this descriptor within its parent.
1231      *
1232      * @see Descriptors.Descriptor#getIndex()
1233      */
getIndex()1234     public int getIndex() {
1235       return index;
1236     }
1237 
1238     /** Convert the descriptor to its protocol message representation. */
1239     @Override
toProto()1240     public FieldDescriptorProto toProto() {
1241       return proto;
1242     }
1243 
1244     /** Get the field's unqualified name. */
1245     @Override
getName()1246     public String getName() {
1247       return proto.getName();
1248     }
1249 
1250     /** Get the field's number. */
1251     @Override
getNumber()1252     public int getNumber() {
1253       return proto.getNumber();
1254     }
1255 
1256     /**
1257      * Get the field's fully-qualified name.
1258      *
1259      * @see Descriptors.Descriptor#getFullName()
1260      */
1261     @Override
getFullName()1262     public String getFullName() {
1263       return fullName;
1264     }
1265 
1266     /** Get the JSON name of this field. */
getJsonName()1267     public String getJsonName() {
1268       String result = jsonName;
1269       if (result != null) {
1270         return result;
1271       } else if (proto.hasJsonName()) {
1272         return jsonName = proto.getJsonName();
1273       } else {
1274         return jsonName = fieldNameToJsonName(proto.getName());
1275       }
1276     }
1277 
1278     /**
1279      * Get the field's java type. This is just for convenience. Every {@code
1280      * FieldDescriptorProto.Type} maps to exactly one Java type.
1281      */
getJavaType()1282     public JavaType getJavaType() {
1283       return getType().getJavaType();
1284     }
1285 
1286     /** For internal use only. */
1287     @Override
getLiteJavaType()1288     public WireFormat.JavaType getLiteJavaType() {
1289       return getLiteType().getJavaType();
1290     }
1291 
1292     /** Get the {@code FileDescriptor} containing this descriptor. */
1293     @Override
getFile()1294     public FileDescriptor getFile() {
1295       return file;
1296     }
1297 
1298     /** Get the field's declared type. */
getType()1299     public Type getType() {
1300       // Override delimited messages as legacy group type. Leaves unresolved messages as-is
1301       // since these are used before feature resolution when parsing java feature set defaults
1302       // (custom options) into unknown fields.
1303       if (type == Type.MESSAGE
1304           && !(messageType != null && messageType.toProto().getOptions().getMapEntry())
1305           && !(containingType != null && containingType.toProto().getOptions().getMapEntry())
1306           && this.features != null
1307           && getFeatures().getMessageEncoding() == FeatureSet.MessageEncoding.DELIMITED) {
1308         return Type.GROUP;
1309       }
1310       return type;
1311     }
1312 
1313     /** For internal use only. */
1314     @Override
getLiteType()1315     public WireFormat.FieldType getLiteType() {
1316       return table[getType().ordinal()];
1317     }
1318 
1319     /** For internal use only. */
needsUtf8Check()1320     public boolean needsUtf8Check() {
1321       if (getType() != Type.STRING) {
1322         return false;
1323       }
1324       if (getContainingType().toProto().getOptions().getMapEntry()) {
1325         // Always enforce strict UTF-8 checking for map fields.
1326         return true;
1327       }
1328       if (getFeatures()
1329           .getExtension(JavaFeaturesProto.java_)
1330           .getUtf8Validation()
1331           .equals(JavaFeatures.Utf8Validation.VERIFY)) {
1332         return true;
1333       }
1334       return getFeatures().getUtf8Validation().equals(FeatureSet.Utf8Validation.VERIFY);
1335     }
1336 
isMapField()1337     public boolean isMapField() {
1338       return getType() == Type.MESSAGE
1339           && isRepeated()
1340           && getMessageType().toProto().getOptions().getMapEntry();
1341     }
1342 
1343     // I'm pretty sure values() constructs a new array every time, since there
1344     // is nothing stopping the caller from mutating the array.  Therefore we
1345     // make a static copy here.
1346     private static final WireFormat.FieldType[] table = WireFormat.FieldType.values();
1347 
1348     /** Is this field declared required? */
isRequired()1349     public boolean isRequired() {
1350       return getFeatures().getFieldPresence()
1351           == DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED;
1352     }
1353 
1354     /** Is this field declared optional? */
isOptional()1355     public boolean isOptional() {
1356       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL
1357           && getFeatures().getFieldPresence()
1358               != DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED;
1359     }
1360 
1361     /** Is this field declared repeated? */
1362     @Override
isRepeated()1363     public boolean isRepeated() {
1364       return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
1365     }
1366 
1367     /**
1368      * Does this field have the {@code [packed = true]} option or is this field packable in proto3
1369      * and not explicitly set to unpacked?
1370      */
1371     @Override
isPacked()1372     public boolean isPacked() {
1373       if (!isPackable()) {
1374         return false;
1375       }
1376       return getFeatures()
1377           .getRepeatedFieldEncoding()
1378           .equals(FeatureSet.RepeatedFieldEncoding.PACKED);
1379     }
1380 
1381     /** Can this field be packed? That is, is it a repeated primitive field? */
isPackable()1382     public boolean isPackable() {
1383       return isRepeated() && getLiteType().isPackable();
1384     }
1385 
1386     /** Returns true if the field had an explicitly-defined default value. */
hasDefaultValue()1387     public boolean hasDefaultValue() {
1388       return proto.hasDefaultValue();
1389     }
1390 
1391     /**
1392      * Returns the field's default value. Valid for all types except for messages and groups. For
1393      * all other types, the object returned is of the same class that would returned by
1394      * Message.getField(this).
1395      */
getDefaultValue()1396     public Object getDefaultValue() {
1397       if (getJavaType() == JavaType.MESSAGE) {
1398         throw new UnsupportedOperationException(
1399             "FieldDescriptor.getDefaultValue() called on an embedded message field.");
1400       }
1401       return defaultValue;
1402     }
1403 
1404     /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
getOptions()1405     public FieldOptions getOptions() {
1406       if (this.options == null) {
1407         FieldOptions strippedOptions = this.proto.getOptions();
1408         if (strippedOptions.hasFeatures()) {
1409           // Clients should be using feature accessor methods, not accessing features on the
1410           // options
1411           // proto.
1412           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
1413         }
1414         synchronized (this) {
1415           if (this.options == null) {
1416             this.options = strippedOptions;
1417           }
1418         }
1419       }
1420       return this.options;
1421     }
1422 
1423     /** Is this field an extension? */
isExtension()1424     public boolean isExtension() {
1425       return proto.hasExtendee();
1426     }
1427 
1428     /**
1429      * Get the field's containing type. For extensions, this is the type being extended, not the
1430      * location where the extension was defined. See {@link #getExtensionScope()}.
1431      */
getContainingType()1432     public Descriptor getContainingType() {
1433       return containingType;
1434     }
1435 
1436     /** Get the field's containing oneof. */
getContainingOneof()1437     public OneofDescriptor getContainingOneof() {
1438       return containingOneof;
1439     }
1440 
1441     /** Get the field's containing oneof, only if non-synthetic. */
getRealContainingOneof()1442     public OneofDescriptor getRealContainingOneof() {
1443       return containingOneof != null && !containingOneof.isSynthetic() ? containingOneof : null;
1444     }
1445 
1446     /**
1447      * Returns true if this field was syntactically written with "optional" in the .proto file.
1448      * Excludes singular proto3 fields that do not have a label.
1449      */
hasOptionalKeyword()1450     boolean hasOptionalKeyword() {
1451       return isProto3Optional
1452           || (file.getEdition() == Edition.EDITION_PROTO2
1453               && isOptional()
1454               && getContainingOneof() == null);
1455     }
1456 
1457     /**
1458      * Returns true if this field tracks presence, ie. does the field distinguish between "unset"
1459      * and "present with default value."
1460      *
1461      * <p>This includes required, optional, and oneof fields. It excludes maps, repeated fields, and
1462      * singular proto3 fields without "optional".
1463      *
1464      * <p>For fields where hasPresence() == true, the return value of msg.hasField() is semantically
1465      * meaningful.
1466      */
hasPresence()1467     public boolean hasPresence() {
1468       if (isRepeated()) {
1469         return false;
1470       }
1471       return isProto3Optional
1472           || getType() == Type.MESSAGE
1473           || getType() == Type.GROUP
1474           || isExtension()
1475           || getContainingOneof() != null
1476           || getFeatures().getFieldPresence() != DescriptorProtos.FeatureSet.FieldPresence.IMPLICIT;
1477     }
1478 
1479     /**
1480      * Returns true if this field is structured like the synthetic field of a proto2 group. This
1481      * allows us to expand our treatment of delimited fields without breaking proto2 files that have
1482      * been upgraded to editions.
1483      */
isGroupLike()1484     boolean isGroupLike() {
1485       if (getType() != Type.GROUP) {
1486         // Groups are always tag-delimited.
1487         return false;
1488       }
1489 
1490       if (!getMessageType().getName().toLowerCase().equals(getName())) {
1491         // Group fields always are always the lowercase type name.
1492         return false;
1493       }
1494 
1495       if (getMessageType().getFile() != getFile()) {
1496         // Groups could only be defined in the same file they're used.
1497         return false;
1498       }
1499 
1500       // Group messages are always defined in the same scope as the field.  File level extensions
1501       // will compare NULL == NULL here, which is why the file comparison above is necessary to
1502       // ensure both come from the same file.
1503       return isExtension()
1504           ? getMessageType().getContainingType() == getExtensionScope()
1505           : getMessageType().getContainingType() == getContainingType();
1506     }
1507 
1508     /**
1509      * For extensions defined nested within message types, gets the outer type. Not valid for
1510      * non-extension fields. For example, consider this {@code .proto} file:
1511      *
1512      * <pre>
1513      *   message Foo {
1514      *     extensions 1000 to max;
1515      *   }
1516      *   extend Foo {
1517      *     optional int32 baz = 1234;
1518      *   }
1519      *   message Bar {
1520      *     extend Foo {
1521      *       optional int32 moo = 4321;
1522      *     }
1523      *   }
1524      * </pre>
1525      *
1526      * Both {@code baz}'s and {@code moo}'s containing type is {@code Foo}. However, {@code baz}'s
1527      * extension scope is {@code null} while {@code moo}'s extension scope is {@code Bar}.
1528      */
getExtensionScope()1529     public Descriptor getExtensionScope() {
1530       if (!isExtension()) {
1531         throw new UnsupportedOperationException(
1532             String.format("This field is not an extension. (%s)", fullName));
1533       }
1534       return extensionScope;
1535     }
1536 
1537     /** For embedded message and group fields, gets the field's type. */
getMessageType()1538     public Descriptor getMessageType() {
1539       if (getJavaType() != JavaType.MESSAGE) {
1540         throw new UnsupportedOperationException(
1541             String.format("This field is not of message type. (%s)", fullName));
1542       }
1543       return messageType;
1544     }
1545 
1546     /** For enum fields, gets the field's type. */
1547     @Override
getEnumType()1548     public EnumDescriptor getEnumType() {
1549       if (getJavaType() != JavaType.ENUM) {
1550         throw new UnsupportedOperationException(
1551             String.format("This field is not of enum type. (%s)", fullName));
1552       }
1553       return enumType;
1554     }
1555 
1556     /**
1557      * Determines if the given enum field is treated as closed based on legacy non-conformant
1558      * behavior.
1559      *
1560      * <p>Conformant behavior determines closedness based on the enum and can be queried using
1561      * {@code EnumDescriptor.isClosed()}.
1562      *
1563      * <p>Some runtimes currently have a quirk where non-closed enums are treated as closed when
1564      * used as the type of fields defined in a `syntax = proto2;` file. This quirk is not present in
1565      * all runtimes; as of writing, we know that:
1566      *
1567      * <ul>
1568      *   <li>C++, Java, and C++-based Python share this quirk.
1569      *   <li>UPB and UPB-based Python do not.
1570      *   <li>PHP and Ruby treat all enums as open regardless of declaration.
1571      * </ul>
1572      *
1573      * <p>Care should be taken when using this function to respect the target runtime's enum
1574      * handling quirks.
1575      */
legacyEnumFieldTreatedAsClosed()1576     public boolean legacyEnumFieldTreatedAsClosed() {
1577       // Don't check JavaFeaturesProto extension for files without dependencies.
1578       // This is especially important for descriptor.proto since getting the JavaFeaturesProto
1579       // extension itself involves calling legacyEnumFieldTreatedAsClosed() which would otherwise
1580       // infinite loop.
1581       if (getFile().getDependencies().isEmpty()) {
1582         return getType() == Type.ENUM && enumType.isClosed();
1583       }
1584 
1585       return getType() == Type.ENUM
1586           && (getFeatures().getExtension(JavaFeaturesProto.java_).getLegacyClosedEnum()
1587               || enumType.isClosed());
1588     }
1589 
1590     /**
1591      * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which
1592      * simply means ascending order by field number. {@code other} must be a field of the same type.
1593      * That is, {@code getContainingType()} must return the same {@code Descriptor} for both fields.
1594      *
1595      * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than
1596      *     {@code other}, respectively
1597      */
1598     @Override
compareTo(final FieldDescriptor other)1599     public int compareTo(final FieldDescriptor other) {
1600       if (other.containingType != containingType) {
1601         throw new IllegalArgumentException(
1602             "FieldDescriptors can only be compared to other FieldDescriptors "
1603                 + "for fields of the same message type.");
1604       }
1605       return getNumber() - other.getNumber();
1606     }
1607 
1608     @Override
toString()1609     public String toString() {
1610       return getFullName();
1611     }
1612 
1613     private final int index;
1614 
1615     private FieldDescriptorProto proto;
1616     private volatile FieldOptions options;
1617     private final String fullName;
1618     private String jsonName;
1619     private final FileDescriptor file;
1620     private final Descriptor extensionScope;
1621     private final boolean isProto3Optional;
1622 
1623     // Possibly initialized during cross-linking.
1624     private Type type;
1625     private Descriptor containingType;
1626     private Descriptor messageType;
1627     private OneofDescriptor containingOneof;
1628     private EnumDescriptor enumType;
1629     private Object defaultValue;
1630 
1631     public enum Type {
1632       DOUBLE(JavaType.DOUBLE),
1633       FLOAT(JavaType.FLOAT),
1634       INT64(JavaType.LONG),
1635       UINT64(JavaType.LONG),
1636       INT32(JavaType.INT),
1637       FIXED64(JavaType.LONG),
1638       FIXED32(JavaType.INT),
1639       BOOL(JavaType.BOOLEAN),
1640       STRING(JavaType.STRING),
1641       GROUP(JavaType.MESSAGE),
1642       MESSAGE(JavaType.MESSAGE),
1643       BYTES(JavaType.BYTE_STRING),
1644       UINT32(JavaType.INT),
1645       ENUM(JavaType.ENUM),
1646       SFIXED32(JavaType.INT),
1647       SFIXED64(JavaType.LONG),
1648       SINT32(JavaType.INT),
1649       SINT64(JavaType.LONG);
1650 
1651       // Private copy to avoid repeated allocations from calls to values() in valueOf().
1652       private static final Type[] types = values();
1653 
Type(JavaType javaType)1654       Type(JavaType javaType) {
1655         this.javaType = javaType;
1656       }
1657 
1658       private final JavaType javaType;
1659 
toProto()1660       public FieldDescriptorProto.Type toProto() {
1661         return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
1662       }
1663 
getJavaType()1664       public JavaType getJavaType() {
1665         return javaType;
1666       }
1667 
valueOf(final FieldDescriptorProto.Type type)1668       public static Type valueOf(final FieldDescriptorProto.Type type) {
1669         return types[type.getNumber() - 1];
1670       }
1671     }
1672 
1673     static {
1674       // Refuse to init if someone added a new declared type.
1675       if (Type.types.length != FieldDescriptorProto.Type.values().length) {
1676         throw new RuntimeException(
1677             "descriptor.proto has a new declared type but Descriptors.java wasn't updated.");
1678       }
1679     }
1680 
1681     public enum JavaType {
1682       INT(0),
1683       LONG(0L),
1684       FLOAT(0F),
1685       DOUBLE(0D),
1686       BOOLEAN(false),
1687       STRING(""),
1688       BYTE_STRING(ByteString.EMPTY),
1689       ENUM(null),
1690       MESSAGE(null);
1691 
JavaType(final Object defaultDefault)1692       JavaType(final Object defaultDefault) {
1693         this.defaultDefault = defaultDefault;
1694       }
1695 
1696       /**
1697        * The default default value for fields of this type, if it's a primitive type. This is meant
1698        * for use inside this file only, hence is private.
1699        */
1700       private final Object defaultDefault;
1701     }
1702 
1703     // This method should match exactly with the ToJsonName() function in C++
1704     // descriptor.cc.
fieldNameToJsonName(String name)1705     private static String fieldNameToJsonName(String name) {
1706       final int length = name.length();
1707       StringBuilder result = new StringBuilder(length);
1708       boolean isNextUpperCase = false;
1709       for (int i = 0; i < length; i++) {
1710         char ch = name.charAt(i);
1711         if (ch == '_') {
1712           isNextUpperCase = true;
1713         } else if (isNextUpperCase) {
1714           // This closely matches the logic for ASCII characters in:
1715           // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689
1716           if ('a' <= ch && ch <= 'z') {
1717             ch = (char) (ch - 'a' + 'A');
1718           }
1719           result.append(ch);
1720           isNextUpperCase = false;
1721         } else {
1722           result.append(ch);
1723         }
1724       }
1725       return result.toString();
1726     }
1727 
FieldDescriptor( final FieldDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index, final boolean isExtension)1728     private FieldDescriptor(
1729         final FieldDescriptorProto proto,
1730         final FileDescriptor file,
1731         final Descriptor parent,
1732         final int index,
1733         final boolean isExtension)
1734         throws DescriptorValidationException {
1735       this.parent = parent;
1736       this.index = index;
1737       this.proto = proto;
1738       fullName = computeFullName(file, parent, proto.getName());
1739       this.file = file;
1740 
1741       if (proto.hasType()) {
1742         type = Type.valueOf(proto.getType());
1743       }
1744 
1745       isProto3Optional = proto.getProto3Optional();
1746 
1747       if (getNumber() <= 0) {
1748         throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
1749       }
1750 
1751       if (isExtension) {
1752         if (!proto.hasExtendee()) {
1753           throw new DescriptorValidationException(
1754               this, "FieldDescriptorProto.extendee not set for extension field.");
1755         }
1756         containingType = null; // Will be filled in when cross-linking
1757         if (parent != null) {
1758           extensionScope = parent;
1759         } else {
1760           extensionScope = null;
1761           this.parent = file;
1762         }
1763 
1764         if (proto.hasOneofIndex()) {
1765           throw new DescriptorValidationException(
1766               this, "FieldDescriptorProto.oneof_index set for extension field.");
1767         }
1768         containingOneof = null;
1769       } else {
1770         if (proto.hasExtendee()) {
1771           throw new DescriptorValidationException(
1772               this, "FieldDescriptorProto.extendee set for non-extension field.");
1773         }
1774         containingType = parent;
1775 
1776         if (proto.hasOneofIndex()) {
1777           if (proto.getOneofIndex() < 0
1778               || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) {
1779             throw new DescriptorValidationException(
1780                 this,
1781                 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName());
1782           }
1783           containingOneof = parent.getOneofs().get(proto.getOneofIndex());
1784           containingOneof.fieldCount++;
1785           this.parent = containingOneof;
1786         } else {
1787           containingOneof = null;
1788         }
1789         extensionScope = null;
1790       }
1791 
1792       file.pool.addSymbol(this);
1793     }
1794 
1795     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()1796     private void resolveAllFeatures() throws DescriptorValidationException {
1797       resolveFeatures(proto.getOptions().getFeatures());
1798     }
1799 
1800     @Override
inferLegacyProtoFeatures()1801     FeatureSet inferLegacyProtoFeatures() {
1802       FeatureSet.Builder features = FeatureSet.newBuilder();
1803       if (getFile().getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) {
1804         return features.build();
1805       }
1806 
1807       if (proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED) {
1808         features.setFieldPresence(FeatureSet.FieldPresence.LEGACY_REQUIRED);
1809       }
1810 
1811       if (proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP) {
1812         features.setMessageEncoding(FeatureSet.MessageEncoding.DELIMITED);
1813       }
1814 
1815       if (getFile().getEdition() == Edition.EDITION_PROTO2 && proto.getOptions().getPacked()) {
1816         features.setRepeatedFieldEncoding(FeatureSet.RepeatedFieldEncoding.PACKED);
1817       }
1818 
1819       if (getFile().getEdition() == Edition.EDITION_PROTO3) {
1820         if (proto.getOptions().hasPacked() && !proto.getOptions().getPacked()) {
1821           features.setRepeatedFieldEncoding(FeatureSet.RepeatedFieldEncoding.EXPANDED);
1822         }
1823 
1824       }
1825       return features.build();
1826     }
1827 
1828     @Override
hasInferredLegacyProtoFeatures()1829     boolean hasInferredLegacyProtoFeatures() {
1830       if (getFile().getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) {
1831         return false;
1832       }
1833 
1834       if (proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED) {
1835         return true;
1836       }
1837 
1838       if (proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP) {
1839         return true;
1840       }
1841 
1842       if (proto.getOptions().getPacked()) {
1843         return true;
1844       }
1845 
1846       if (getFile().getEdition() == Edition.EDITION_PROTO3) {
1847         if (proto.getOptions().hasPacked() && !proto.getOptions().getPacked()) {
1848           return true;
1849         }
1850 
1851       }
1852       return false;
1853     }
1854 
1855     @Override
validateFeatures()1856     void validateFeatures() throws DescriptorValidationException {
1857       if (containingType != null
1858           && containingType.toProto().getOptions().getMessageSetWireFormat()) {
1859         if (isExtension()) {
1860           if (!isOptional() || getType() != Type.MESSAGE) {
1861             throw new DescriptorValidationException(
1862                 this, "Extensions of MessageSets must be optional messages.");
1863           }
1864         }
1865       }
1866     }
1867 
1868     /** Look up and cross-link all field types, etc. */
crossLink()1869     private void crossLink() throws DescriptorValidationException {
1870       if (proto.hasExtendee()) {
1871         final GenericDescriptor extendee =
1872             file.pool.lookupSymbol(
1873                 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1874         if (!(extendee instanceof Descriptor)) {
1875           throw new DescriptorValidationException(
1876               this, '\"' + proto.getExtendee() + "\" is not a message type.");
1877         }
1878         containingType = (Descriptor) extendee;
1879 
1880         if (!getContainingType().isExtensionNumber(getNumber())) {
1881           throw new DescriptorValidationException(
1882               this,
1883               '\"'
1884                   + getContainingType().getFullName()
1885                   + "\" does not declare "
1886                   + getNumber()
1887                   + " as an extension number.");
1888         }
1889       }
1890 
1891       if (proto.hasTypeName()) {
1892         final GenericDescriptor typeDescriptor =
1893             file.pool.lookupSymbol(
1894                 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1895 
1896         if (!proto.hasType()) {
1897           // Choose field type based on symbol.
1898           if (typeDescriptor instanceof Descriptor) {
1899             type = Type.MESSAGE;
1900           } else if (typeDescriptor instanceof EnumDescriptor) {
1901             type = Type.ENUM;
1902           } else {
1903             throw new DescriptorValidationException(
1904                 this, '\"' + proto.getTypeName() + "\" is not a type.");
1905           }
1906         }
1907 
1908         // Use raw type since inferred type considers messageType which may not be fully cross
1909         // linked yet.
1910         if (type.getJavaType() == JavaType.MESSAGE) {
1911           if (!(typeDescriptor instanceof Descriptor)) {
1912             throw new DescriptorValidationException(
1913                 this, '\"' + proto.getTypeName() + "\" is not a message type.");
1914           }
1915           messageType = (Descriptor) typeDescriptor;
1916 
1917           if (proto.hasDefaultValue()) {
1918             throw new DescriptorValidationException(this, "Messages can't have default values.");
1919           }
1920         } else if (type.getJavaType() == JavaType.ENUM) {
1921           if (!(typeDescriptor instanceof EnumDescriptor)) {
1922             throw new DescriptorValidationException(
1923                 this, '\"' + proto.getTypeName() + "\" is not an enum type.");
1924           }
1925           enumType = (EnumDescriptor) typeDescriptor;
1926         } else {
1927           throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
1928         }
1929       } else {
1930         if (type.getJavaType() == JavaType.MESSAGE || type.getJavaType() == JavaType.ENUM) {
1931           throw new DescriptorValidationException(
1932               this, "Field with message or enum type missing type_name.");
1933         }
1934       }
1935 
1936       // Only repeated primitive fields may be packed.
1937       if (proto.getOptions().getPacked() && !isPackable()) {
1938         throw new DescriptorValidationException(
1939             this, "[packed = true] can only be specified for repeated primitive fields.");
1940       }
1941 
1942       // We don't attempt to parse the default value until here because for
1943       // enums we need the enum type's descriptor.
1944       if (proto.hasDefaultValue()) {
1945         if (isRepeated()) {
1946           throw new DescriptorValidationException(
1947               this, "Repeated fields cannot have default values.");
1948         }
1949 
1950         try {
1951           switch (type) {
1952             case INT32:
1953             case SINT32:
1954             case SFIXED32:
1955               defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
1956               break;
1957             case UINT32:
1958             case FIXED32:
1959               defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
1960               break;
1961             case INT64:
1962             case SINT64:
1963             case SFIXED64:
1964               defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
1965               break;
1966             case UINT64:
1967             case FIXED64:
1968               defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
1969               break;
1970             case FLOAT:
1971               if (proto.getDefaultValue().equals("inf")) {
1972                 defaultValue = Float.POSITIVE_INFINITY;
1973               } else if (proto.getDefaultValue().equals("-inf")) {
1974                 defaultValue = Float.NEGATIVE_INFINITY;
1975               } else if (proto.getDefaultValue().equals("nan")) {
1976                 defaultValue = Float.NaN;
1977               } else {
1978                 defaultValue = Float.valueOf(proto.getDefaultValue());
1979               }
1980               break;
1981             case DOUBLE:
1982               if (proto.getDefaultValue().equals("inf")) {
1983                 defaultValue = Double.POSITIVE_INFINITY;
1984               } else if (proto.getDefaultValue().equals("-inf")) {
1985                 defaultValue = Double.NEGATIVE_INFINITY;
1986               } else if (proto.getDefaultValue().equals("nan")) {
1987                 defaultValue = Double.NaN;
1988               } else {
1989                 defaultValue = Double.valueOf(proto.getDefaultValue());
1990               }
1991               break;
1992             case BOOL:
1993               defaultValue = Boolean.valueOf(proto.getDefaultValue());
1994               break;
1995             case STRING:
1996               defaultValue = proto.getDefaultValue();
1997               break;
1998             case BYTES:
1999               try {
2000                 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue());
2001               } catch (TextFormat.InvalidEscapeSequenceException e) {
2002                 throw new DescriptorValidationException(
2003                     this, "Couldn't parse default value: " + e.getMessage(), e);
2004               }
2005               break;
2006             case ENUM:
2007               defaultValue = enumType.findValueByName(proto.getDefaultValue());
2008               if (defaultValue == null) {
2009                 throw new DescriptorValidationException(
2010                     this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"');
2011               }
2012               break;
2013             case MESSAGE:
2014             case GROUP:
2015               throw new DescriptorValidationException(this, "Message type had default value.");
2016           }
2017         } catch (NumberFormatException e) {
2018           throw new DescriptorValidationException(
2019               this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e);
2020         }
2021       } else {
2022         // Determine the default default for this field.
2023         if (isRepeated()) {
2024           defaultValue = Collections.emptyList();
2025         } else {
2026           switch (type.getJavaType()) {
2027             case ENUM:
2028               // We guarantee elsewhere that an enum type always has at least
2029               // one possible value.
2030               defaultValue = enumType.getValues().get(0);
2031               break;
2032             case MESSAGE:
2033               defaultValue = null;
2034               break;
2035             default:
2036               defaultValue = type.getJavaType().defaultDefault;
2037               break;
2038           }
2039         }
2040       }
2041     }
2042 
2043     /** See {@link FileDescriptor#setProto}. */
setProto(final FieldDescriptorProto proto)2044     private void setProto(final FieldDescriptorProto proto) throws DescriptorValidationException {
2045       this.proto = proto;
2046       this.options = null;
2047       resolveFeatures(proto.getOptions().getFeatures());
2048     }
2049 
2050     /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */
2051     @Override
internalMergeFrom(MessageLite.Builder to, MessageLite from)2052     public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
2053       // FieldDescriptors are only used with non-lite messages so we can just
2054       // down-cast and call mergeFrom directly.
2055       return ((Message.Builder) to).mergeFrom((Message) from);
2056     }
2057   }
2058 
2059   // =================================================================
2060 
2061   /** Describes an enum type. */
2062   public static final class EnumDescriptor extends GenericDescriptor
2063       implements Internal.EnumLiteMap<EnumValueDescriptor> {
2064     /**
2065      * Get the index of this descriptor within its parent.
2066      *
2067      * @see Descriptors.Descriptor#getIndex()
2068      */
getIndex()2069     public int getIndex() {
2070       return index;
2071     }
2072 
2073     /** Convert the descriptor to its protocol message representation. */
2074     @Override
toProto()2075     public EnumDescriptorProto toProto() {
2076       return proto;
2077     }
2078 
2079     /** Get the type's unqualified name. */
2080     @Override
getName()2081     public String getName() {
2082       return proto.getName();
2083     }
2084 
2085     /**
2086      * Get the type's fully-qualified name.
2087      *
2088      * @see Descriptors.Descriptor#getFullName()
2089      */
2090     @Override
getFullName()2091     public String getFullName() {
2092       return fullName;
2093     }
2094 
2095     /** Get the {@link FileDescriptor} containing this descriptor. */
2096     @Override
getFile()2097     public FileDescriptor getFile() {
2098       return file;
2099     }
2100 
2101     /**
2102      * Determines if the given enum is closed.
2103      *
2104      * <p>Closed enum means that it:
2105      *
2106      * <ul>
2107      *   <li>Has a fixed set of values, rather than being equivalent to an int32.
2108      *   <li>Encountering values not in this set causes them to be treated as unknown fields.
2109      *   <li>The first value (i.e., the default) may be nonzero.
2110      * </ul>
2111      *
2112      * <p>WARNING: Some runtimes currently have a quirk where non-closed enums are treated as closed
2113      * when used as the type of fields defined in a `syntax = proto2;` file. This quirk is not
2114      * present in all runtimes; as of writing, we know that:
2115      *
2116      * <ul>
2117      *   <li> C++, Java, and C++-based Python share this quirk.
2118      *   <li> UPB and UPB-based Python do not.
2119      *   <li> PHP and Ruby treat all enums as open regardless of declaration.
2120      * </ul>
2121      *
2122      * <p>Care should be taken when using this function to respect the target runtime's enum
2123      * handling quirks.
2124      */
isClosed()2125     public boolean isClosed() {
2126       return getFeatures().getEnumType() == DescriptorProtos.FeatureSet.EnumType.CLOSED;
2127     }
2128 
2129     /** If this is a nested type, get the outer descriptor, otherwise null. */
getContainingType()2130     public Descriptor getContainingType() {
2131       return containingType;
2132     }
2133 
2134     /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
getOptions()2135     public EnumOptions getOptions() {
2136       if (this.options == null) {
2137         EnumOptions strippedOptions = this.proto.getOptions();
2138         if (strippedOptions.hasFeatures()) {
2139           // Clients should be using feature accessor methods, not accessing features on the
2140           // options
2141           // proto.
2142           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
2143         }
2144         synchronized (this) {
2145           if (this.options == null) {
2146             this.options = strippedOptions;
2147           }
2148         }
2149       }
2150       return this.options;
2151     }
2152 
2153     /** Get a list of defined values for this enum. */
getValues()2154     public List<EnumValueDescriptor> getValues() {
2155       return Collections.unmodifiableList(Arrays.asList(values));
2156     }
2157 
2158     /** Determines if the given field number is reserved. */
isReservedNumber(final int number)2159     public boolean isReservedNumber(final int number) {
2160       for (final EnumDescriptorProto.EnumReservedRange range : proto.getReservedRangeList()) {
2161         if (range.getStart() <= number && number <= range.getEnd()) {
2162           return true;
2163         }
2164       }
2165       return false;
2166     }
2167 
2168     /** Determines if the given field name is reserved. */
isReservedName(final String name)2169     public boolean isReservedName(final String name) {
2170       checkNotNull(name);
2171       for (final String reservedName : proto.getReservedNameList()) {
2172         if (reservedName.equals(name)) {
2173           return true;
2174         }
2175       }
2176       return false;
2177     }
2178 
2179     /**
2180      * Find an enum value by name.
2181      *
2182      * @param name the unqualified name of the value such as "FOO"
2183      * @return the value's descriptor, or {@code null} if not found
2184      */
findValueByName(final String name)2185     public EnumValueDescriptor findValueByName(final String name) {
2186       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
2187       if (result instanceof EnumValueDescriptor) {
2188         return (EnumValueDescriptor) result;
2189       } else {
2190         return null;
2191       }
2192     }
2193 
2194     /**
2195      * Find an enum value by number. If multiple enum values have the same number, this returns the
2196      * first defined value with that number.
2197      *
2198      * @param number The value's number.
2199      * @return the value's descriptor, or {@code null} if not found.
2200      */
2201     @Override
findValueByNumber(final int number)2202     public EnumValueDescriptor findValueByNumber(final int number) {
2203       return binarySearch(
2204           valuesSortedByNumber, distinctNumbers, EnumValueDescriptor.NUMBER_GETTER, number);
2205     }
2206 
2207     private static class UnknownEnumValueReference extends WeakReference<EnumValueDescriptor> {
2208       private final int number;
2209 
UnknownEnumValueReference(int number, EnumValueDescriptor descriptor)2210       private UnknownEnumValueReference(int number, EnumValueDescriptor descriptor) {
2211         super(descriptor);
2212         this.number = number;
2213       }
2214     }
2215 
2216     /**
2217      * Get the enum value for a number. If no enum value has this number, construct an
2218      * EnumValueDescriptor for it.
2219      */
findValueByNumberCreatingIfUnknown(final int number)2220     public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) {
2221       EnumValueDescriptor result = findValueByNumber(number);
2222       if (result != null) {
2223         return result;
2224       }
2225       // The number represents an unknown enum value.
2226       synchronized (this) {
2227         if (cleanupQueue == null) {
2228           cleanupQueue = new ReferenceQueue<>();
2229           unknownValues = new HashMap<>();
2230         } else {
2231           while (true) {
2232             UnknownEnumValueReference toClean = (UnknownEnumValueReference) cleanupQueue.poll();
2233             if (toClean == null) {
2234               break;
2235             }
2236             unknownValues.remove(toClean.number);
2237           }
2238         }
2239 
2240         // There are two ways we can be missing a value: it wasn't in the map, or the reference
2241         // has been GC'd.  (It may even have been GC'd since we cleaned up the references a few
2242         // lines of code ago.)  So get out the reference, if it's still present...
2243         WeakReference<EnumValueDescriptor> reference = unknownValues.get(number);
2244         result = (reference == null) ? null : reference.get();
2245 
2246         if (result == null) {
2247           result = new EnumValueDescriptor(this, number);
2248           unknownValues.put(number, new UnknownEnumValueReference(number, result));
2249         }
2250       }
2251       return result;
2252     }
2253 
2254     // Used in tests only.
getUnknownEnumValueDescriptorCount()2255     int getUnknownEnumValueDescriptorCount() {
2256       return unknownValues.size();
2257     }
2258 
2259     private final int index;
2260     private EnumDescriptorProto proto;
2261     private volatile EnumOptions options;
2262     private final String fullName;
2263     private final FileDescriptor file;
2264     private final Descriptor containingType;
2265     private final EnumValueDescriptor[] values;
2266     private final EnumValueDescriptor[] valuesSortedByNumber;
2267     private final int distinctNumbers;
2268     private Map<Integer, WeakReference<EnumValueDescriptor>> unknownValues = null;
2269     private ReferenceQueue<EnumValueDescriptor> cleanupQueue = null;
2270 
EnumDescriptor( final EnumDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)2271     private EnumDescriptor(
2272         final EnumDescriptorProto proto,
2273         final FileDescriptor file,
2274         final Descriptor parent,
2275         final int index)
2276         throws DescriptorValidationException {
2277       if (parent == null) {
2278         this.parent = file;
2279       } else {
2280         this.parent = parent;
2281       }
2282       this.index = index;
2283       this.proto = proto;
2284       fullName = computeFullName(file, parent, proto.getName());
2285       this.file = file;
2286       containingType = parent;
2287 
2288       if (proto.getValueCount() == 0) {
2289         // We cannot allow enums with no values because this would mean there
2290         // would be no valid default value for fields of this type.
2291         throw new DescriptorValidationException(this, "Enums must contain at least one value.");
2292       }
2293 
2294       values = new EnumValueDescriptor[proto.getValueCount()];
2295       for (int i = 0; i < proto.getValueCount(); i++) {
2296         values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i);
2297       }
2298       valuesSortedByNumber = values.clone();
2299       Arrays.sort(valuesSortedByNumber, EnumValueDescriptor.BY_NUMBER);
2300       // deduplicate
2301       int j = 0;
2302       for (int i = 1; i < proto.getValueCount(); i++) {
2303         EnumValueDescriptor oldValue = valuesSortedByNumber[j];
2304         EnumValueDescriptor newValue = valuesSortedByNumber[i];
2305         if (oldValue.getNumber() != newValue.getNumber()) {
2306           valuesSortedByNumber[++j] = newValue;
2307         }
2308       }
2309       this.distinctNumbers = j + 1;
2310       Arrays.fill(valuesSortedByNumber, distinctNumbers, proto.getValueCount(), null);
2311 
2312       file.pool.addSymbol(this);
2313     }
2314 
2315     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()2316     private void resolveAllFeatures() throws DescriptorValidationException {
2317       resolveFeatures(proto.getOptions().getFeatures());
2318 
2319       for (EnumValueDescriptor value : values) {
2320         value.resolveAllFeatures();
2321       }
2322     }
2323 
2324     /** See {@link FileDescriptor#setProto}. */
setProto(final EnumDescriptorProto proto)2325     private void setProto(final EnumDescriptorProto proto) throws DescriptorValidationException {
2326       this.proto = proto;
2327       this.options = null;
2328       resolveFeatures(proto.getOptions().getFeatures());
2329 
2330       for (int i = 0; i < values.length; i++) {
2331         values[i].setProto(proto.getValue(i));
2332       }
2333     }
2334   }
2335 
2336   // =================================================================
2337 
2338   /**
2339    * Describes one value within an enum type. Note that multiple defined values may have the same
2340    * number. In generated Java code, all values with the same number after the first become aliases
2341    * of the first. However, they still have independent EnumValueDescriptors.
2342    */
2343   @SuppressWarnings("ShouldNotSubclass")
2344   public static final class EnumValueDescriptor extends GenericDescriptor
2345       implements Internal.EnumLite {
2346     static final Comparator<EnumValueDescriptor> BY_NUMBER =
2347         new Comparator<EnumValueDescriptor>() {
2348           @Override
2349           public int compare(EnumValueDescriptor o1, EnumValueDescriptor o2) {
2350             return Integer.compare(o1.getNumber(), o2.getNumber());
2351           }
2352         };
2353 
2354     static final NumberGetter<EnumValueDescriptor> NUMBER_GETTER =
2355         new NumberGetter<EnumValueDescriptor>() {
2356           @Override
2357           public int getNumber(EnumValueDescriptor enumValueDescriptor) {
2358             return enumValueDescriptor.getNumber();
2359           }
2360         };
2361 
2362     /**
2363      * Get the index of this descriptor within its parent.
2364      *
2365      * @see Descriptors.Descriptor#getIndex()
2366      */
getIndex()2367     public int getIndex() {
2368       return index;
2369     }
2370 
2371     /** Convert the descriptor to its protocol message representation. */
2372     @Override
toProto()2373     public EnumValueDescriptorProto toProto() {
2374       return proto;
2375     }
2376 
2377     /** Get the value's unqualified name. */
2378     @Override
getName()2379     public String getName() {
2380       return proto.getName();
2381     }
2382 
2383     /** Get the value's number. */
2384     @Override
getNumber()2385     public int getNumber() {
2386       return proto.getNumber();
2387     }
2388 
2389     @Override
toString()2390     public String toString() {
2391       return proto.getName();
2392     }
2393 
2394     /**
2395      * Get the value's fully-qualified name.
2396      *
2397      * @see Descriptors.Descriptor#getFullName()
2398      */
2399     @Override
getFullName()2400     public String getFullName() {
2401       return fullName;
2402     }
2403 
2404     /** Get the {@link FileDescriptor} containing this descriptor. */
2405     @Override
getFile()2406     public FileDescriptor getFile() {
2407       return type.file;
2408     }
2409 
2410     /** Get the value's enum type. */
getType()2411     public EnumDescriptor getType() {
2412       return type;
2413     }
2414 
2415     /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */
getOptions()2416     public EnumValueOptions getOptions() {
2417       if (this.options == null) {
2418         EnumValueOptions strippedOptions = this.proto.getOptions();
2419         if (strippedOptions.hasFeatures()) {
2420           // Clients should be using feature accessor methods, not accessing features on the
2421           // options
2422           // proto.
2423           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
2424         }
2425         synchronized (this) {
2426           if (this.options == null) {
2427             this.options = strippedOptions;
2428           }
2429         }
2430       }
2431       return this.options;
2432     }
2433 
2434     private final int index;
2435     private EnumValueDescriptorProto proto;
2436     private volatile EnumValueOptions options;
2437     private final String fullName;
2438     private final EnumDescriptor type;
2439 
EnumValueDescriptor( final EnumValueDescriptorProto proto, final FileDescriptor file, final EnumDescriptor parent, final int index)2440     private EnumValueDescriptor(
2441         final EnumValueDescriptorProto proto,
2442         final FileDescriptor file,
2443         final EnumDescriptor parent,
2444         final int index)
2445         throws DescriptorValidationException {
2446       this.parent = parent;
2447       this.index = index;
2448       this.proto = proto;
2449       this.type = parent;
2450       this.fullName = parent.getFullName() + '.' + proto.getName();
2451       file.pool.addSymbol(this);
2452     }
2453 
2454     // Create an unknown enum value.
EnumValueDescriptor(final EnumDescriptor parent, final Integer number)2455     private EnumValueDescriptor(final EnumDescriptor parent, final Integer number) {
2456       String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number;
2457       EnumValueDescriptorProto proto =
2458           EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build();
2459       this.parent = parent;
2460       this.index = -1;
2461       this.proto = proto;
2462       this.type = parent;
2463       this.fullName = parent.getFullName() + '.' + proto.getName();
2464 
2465       // Don't add this descriptor into pool.
2466     }
2467 
2468     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()2469     private void resolveAllFeatures() throws DescriptorValidationException {
2470       resolveFeatures(proto.getOptions().getFeatures());
2471     }
2472 
2473     /** See {@link FileDescriptor#setProto}. */
setProto(final EnumValueDescriptorProto proto)2474     private void setProto(final EnumValueDescriptorProto proto)
2475         throws DescriptorValidationException {
2476       this.proto = proto;
2477       this.options = null;
2478       resolveFeatures(proto.getOptions().getFeatures());
2479     }
2480   }
2481 
2482   // =================================================================
2483 
2484   /** Describes a service type. */
2485   public static final class ServiceDescriptor extends GenericDescriptor {
2486     /**
2487      * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
2488      */
getIndex()2489     public int getIndex() {
2490       return index;
2491     }
2492 
2493     /** Convert the descriptor to its protocol message representation. */
2494     @Override
toProto()2495     public ServiceDescriptorProto toProto() {
2496       return proto;
2497     }
2498 
2499     /** Get the type's unqualified name. */
2500     @Override
getName()2501     public String getName() {
2502       return proto.getName();
2503     }
2504 
2505     /**
2506      * Get the type's fully-qualified name.
2507      *
2508      * @see Descriptors.Descriptor#getFullName()
2509      */
2510     @Override
getFullName()2511     public String getFullName() {
2512       return fullName;
2513     }
2514 
2515     /** Get the {@link FileDescriptor} containing this descriptor. */
2516     @Override
getFile()2517     public FileDescriptor getFile() {
2518       return file;
2519     }
2520 
2521     /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
getOptions()2522     public ServiceOptions getOptions() {
2523       if (this.options == null) {
2524         ServiceOptions strippedOptions = this.proto.getOptions();
2525         if (strippedOptions.hasFeatures()) {
2526           // Clients should be using feature accessor methods, not accessing features on the
2527           // options
2528           // proto.
2529           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
2530         }
2531         synchronized (this) {
2532           if (this.options == null) {
2533             this.options = strippedOptions;
2534           }
2535         }
2536       }
2537       return this.options;
2538     }
2539 
2540     /** Get a list of methods for this service. */
getMethods()2541     public List<MethodDescriptor> getMethods() {
2542       return Collections.unmodifiableList(Arrays.asList(methods));
2543     }
2544 
2545     /**
2546      * Find a method by name.
2547      *
2548      * @param name the unqualified name of the method such as "Foo"
2549      * @return the method's descriptor, or {@code null} if not found
2550      */
findMethodByName(final String name)2551     public MethodDescriptor findMethodByName(final String name) {
2552       final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
2553       if (result instanceof MethodDescriptor) {
2554         return (MethodDescriptor) result;
2555       } else {
2556         return null;
2557       }
2558     }
2559 
2560     private final int index;
2561     private ServiceDescriptorProto proto;
2562     private volatile ServiceOptions options;
2563     private final String fullName;
2564     private final FileDescriptor file;
2565     private MethodDescriptor[] methods;
2566 
ServiceDescriptor( final ServiceDescriptorProto proto, final FileDescriptor file, final int index)2567     private ServiceDescriptor(
2568         final ServiceDescriptorProto proto, final FileDescriptor file, final int index)
2569         throws DescriptorValidationException {
2570       this.parent = file;
2571       this.index = index;
2572       this.proto = proto;
2573       fullName = computeFullName(file, null, proto.getName());
2574       this.file = file;
2575 
2576       methods = new MethodDescriptor[proto.getMethodCount()];
2577       for (int i = 0; i < proto.getMethodCount(); i++) {
2578         methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i);
2579       }
2580 
2581       file.pool.addSymbol(this);
2582     }
2583 
2584     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()2585     private void resolveAllFeatures() throws DescriptorValidationException {
2586       resolveFeatures(proto.getOptions().getFeatures());
2587 
2588       for (MethodDescriptor method : methods) {
2589         method.resolveAllFeatures();
2590       }
2591     }
2592 
crossLink()2593     private void crossLink() throws DescriptorValidationException {
2594       for (final MethodDescriptor method : methods) {
2595         method.crossLink();
2596       }
2597     }
2598 
2599     /** See {@link FileDescriptor#setProto}. */
setProto(final ServiceDescriptorProto proto)2600     private void setProto(final ServiceDescriptorProto proto) throws DescriptorValidationException {
2601       this.proto = proto;
2602       this.options = null;
2603       resolveFeatures(proto.getOptions().getFeatures());
2604 
2605       for (int i = 0; i < methods.length; i++) {
2606         methods[i].setProto(proto.getMethod(i));
2607       }
2608     }
2609   }
2610 
2611   // =================================================================
2612 
2613   /** Describes one method within a service type. */
2614   public static final class MethodDescriptor extends GenericDescriptor {
2615     /**
2616      * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
2617      */
getIndex()2618     public int getIndex() {
2619       return index;
2620     }
2621 
2622     /** Convert the descriptor to its protocol message representation. */
2623     @Override
toProto()2624     public MethodDescriptorProto toProto() {
2625       return proto;
2626     }
2627 
2628     /** Get the method's unqualified name. */
2629     @Override
getName()2630     public String getName() {
2631       return proto.getName();
2632     }
2633 
2634     /**
2635      * Get the method's fully-qualified name.
2636      *
2637      * @see Descriptors.Descriptor#getFullName()
2638      */
2639     @Override
getFullName()2640     public String getFullName() {
2641       return fullName;
2642     }
2643 
2644     /** Get the {@link FileDescriptor} containing this descriptor. */
2645     @Override
getFile()2646     public FileDescriptor getFile() {
2647       return file;
2648     }
2649 
2650     /** Get the method's service type. */
getService()2651     public ServiceDescriptor getService() {
2652       return service;
2653     }
2654 
2655     /** Get the method's input type. */
getInputType()2656     public Descriptor getInputType() {
2657       return inputType;
2658     }
2659 
2660     /** Get the method's output type. */
getOutputType()2661     public Descriptor getOutputType() {
2662       return outputType;
2663     }
2664 
2665     /** Get whether or not the inputs are streaming. */
isClientStreaming()2666     public boolean isClientStreaming() {
2667       return proto.getClientStreaming();
2668     }
2669 
2670     /** Get whether or not the outputs are streaming. */
isServerStreaming()2671     public boolean isServerStreaming() {
2672       return proto.getServerStreaming();
2673     }
2674 
2675     /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */
getOptions()2676     public MethodOptions getOptions() {
2677       if (this.options == null) {
2678         MethodOptions strippedOptions = this.proto.getOptions();
2679         if (strippedOptions.hasFeatures()) {
2680           // Clients should be using feature accessor methods, not accessing features on the
2681           // options
2682           // proto.
2683           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
2684         }
2685         synchronized (this) {
2686           if (this.options == null) {
2687             this.options = strippedOptions;
2688           }
2689         }
2690       }
2691       return this.options;
2692     }
2693 
2694     private final int index;
2695     private MethodDescriptorProto proto;
2696     private volatile MethodOptions options;
2697     private final String fullName;
2698     private final FileDescriptor file;
2699     private final ServiceDescriptor service;
2700 
2701     // Initialized during cross-linking.
2702     private Descriptor inputType;
2703     private Descriptor outputType;
2704 
MethodDescriptor( final MethodDescriptorProto proto, final FileDescriptor file, final ServiceDescriptor parent, final int index)2705     private MethodDescriptor(
2706         final MethodDescriptorProto proto,
2707         final FileDescriptor file,
2708         final ServiceDescriptor parent,
2709         final int index)
2710         throws DescriptorValidationException {
2711       this.parent = parent;
2712       this.index = index;
2713       this.proto = proto;
2714       this.file = file;
2715       service = parent;
2716 
2717       fullName = parent.getFullName() + '.' + proto.getName();
2718 
2719       file.pool.addSymbol(this);
2720     }
2721 
2722     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()2723     private void resolveAllFeatures() throws DescriptorValidationException {
2724       resolveFeatures(proto.getOptions().getFeatures());
2725     }
2726 
crossLink()2727     private void crossLink() throws DescriptorValidationException {
2728       final GenericDescriptor input =
2729           getFile()
2730               .pool
2731               .lookupSymbol(proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2732       if (!(input instanceof Descriptor)) {
2733         throw new DescriptorValidationException(
2734             this, '\"' + proto.getInputType() + "\" is not a message type.");
2735       }
2736       inputType = (Descriptor) input;
2737 
2738       final GenericDescriptor output =
2739           getFile()
2740               .pool
2741               .lookupSymbol(proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2742       if (!(output instanceof Descriptor)) {
2743         throw new DescriptorValidationException(
2744             this, '\"' + proto.getOutputType() + "\" is not a message type.");
2745       }
2746       outputType = (Descriptor) output;
2747     }
2748 
2749     /** See {@link FileDescriptor#setProto}. */
setProto(final MethodDescriptorProto proto)2750     private void setProto(final MethodDescriptorProto proto) throws DescriptorValidationException {
2751       this.proto = proto;
2752       this.options = null;
2753       resolveFeatures(proto.getOptions().getFeatures());
2754     }
2755   }
2756 
2757   // =================================================================
2758 
computeFullName( final FileDescriptor file, final Descriptor parent, final String name)2759   private static String computeFullName(
2760       final FileDescriptor file, final Descriptor parent, final String name) {
2761     if (parent != null) {
2762       return parent.getFullName() + '.' + name;
2763     }
2764 
2765     final String packageName = file.getPackage();
2766     if (!packageName.isEmpty()) {
2767       return packageName + '.' + name;
2768     }
2769 
2770     return name;
2771   }
2772 
2773   // =================================================================
2774 
2775   /**
2776    * All descriptors implement this to make it easier to implement tools like {@code
2777    * DescriptorPool}.
2778    */
2779   public abstract static class GenericDescriptor {
2780     // Private constructor to prevent subclasses outside of com.google.protobuf.Descriptors
GenericDescriptor()2781     private GenericDescriptor() {}
2782 
toProto()2783     public abstract Message toProto();
2784 
getName()2785     public abstract String getName();
2786 
getFullName()2787     public abstract String getFullName();
2788 
getFile()2789     public abstract FileDescriptor getFile();
2790 
resolveFeatures(FeatureSet unresolvedFeatures)2791     void resolveFeatures(FeatureSet unresolvedFeatures) throws DescriptorValidationException {
2792       if (this.parent != null
2793           && unresolvedFeatures.equals(FeatureSet.getDefaultInstance())
2794           && !hasInferredLegacyProtoFeatures()) {
2795         this.features = this.parent.features;
2796         validateFeatures();
2797         return;
2798       }
2799 
2800       // Java features from a custom pool (i.e. buildFrom) may end up in unknown fields or
2801       // use a different descriptor from the generated pool used by the Java runtime.
2802       boolean hasPossibleCustomJavaFeature = false;
2803       for (FieldDescriptor f : unresolvedFeatures.getExtensionFields().keySet()) {
2804         if (f.getNumber() == JavaFeaturesProto.java_.getNumber()
2805             && f != JavaFeaturesProto.java_.getDescriptor()) {
2806           hasPossibleCustomJavaFeature = true;
2807           continue;
2808         }
2809       }
2810       boolean hasPossibleUnknownJavaFeature =
2811           !unresolvedFeatures.getUnknownFields().isEmpty()
2812               && (unresolvedFeatures
2813                       .getUnknownFields()
2814                       .hasField(JavaFeaturesProto.java_.getNumber())
2815               );
2816       if (hasPossibleCustomJavaFeature || hasPossibleUnknownJavaFeature) {
2817         ExtensionRegistry registry = ExtensionRegistry.newInstance();
2818         registry.add(JavaFeaturesProto.java_);
2819         ByteString bytes = unresolvedFeatures.toByteString();
2820         try {
2821           unresolvedFeatures = FeatureSet.parseFrom(bytes, registry);
2822         } catch (InvalidProtocolBufferException e) {
2823           throw new DescriptorValidationException(
2824               this, "Failed to parse features with Java feature extension registry.", e);
2825         }
2826       }
2827 
2828       FeatureSet.Builder features;
2829       if (this.parent == null) {
2830         Edition edition = getFile().getEdition();
2831         features = getEditionDefaults(edition).toBuilder();
2832       } else {
2833         features = this.parent.features.toBuilder();
2834       }
2835       features.mergeFrom(inferLegacyProtoFeatures());
2836       features.mergeFrom(unresolvedFeatures);
2837       this.features = internFeatures(features.build());
2838       validateFeatures();
2839     }
2840 
inferLegacyProtoFeatures()2841     FeatureSet inferLegacyProtoFeatures() {
2842       return FeatureSet.getDefaultInstance();
2843     }
2844 
hasInferredLegacyProtoFeatures()2845     boolean hasInferredLegacyProtoFeatures() {
2846       return false;
2847     }
2848 
validateFeatures()2849     void validateFeatures() throws DescriptorValidationException {}
2850 
getFeatures()2851     FeatureSet getFeatures() {
2852       // TODO: Remove lazy resolution of unresolved features for legacy syntax for
2853       // compatibility with older <4.26.x gencode in the next breaking release.
2854       if (this.features == null
2855           && (getFile().getEdition() == Edition.EDITION_PROTO2
2856               || getFile().getEdition() == Edition.EDITION_PROTO3)) {
2857         getFile().resolveAllFeaturesImmutable();
2858       }
2859       if (this.features == null) {
2860         throw new NullPointerException(
2861             String.format("Features not yet loaded for %s.", getFullName()));
2862       }
2863       return this.features;
2864     }
2865 
2866     GenericDescriptor parent;
2867     volatile FeatureSet features;
2868   }
2869 
2870   /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */
2871   public static class DescriptorValidationException extends Exception {
2872     private static final long serialVersionUID = 5750205775490483148L;
2873 
2874     /** Gets the full name of the descriptor where the error occurred. */
getProblemSymbolName()2875     public String getProblemSymbolName() {
2876       return name;
2877     }
2878 
2879     /** Gets the protocol message representation of the invalid descriptor. */
getProblemProto()2880     public Message getProblemProto() {
2881       return proto;
2882     }
2883 
2884     /** Gets a human-readable description of the error. */
getDescription()2885     public String getDescription() {
2886       return description;
2887     }
2888 
2889     private final String name;
2890     private final Message proto;
2891     private final String description;
2892 
DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description)2893     private DescriptorValidationException(
2894         final GenericDescriptor problemDescriptor, final String description) {
2895       super(problemDescriptor.getFullName() + ": " + description);
2896 
2897       // Note that problemDescriptor may be partially uninitialized, so we
2898       // don't want to expose it directly to the user.  So, we only provide
2899       // the name and the original proto.
2900       name = problemDescriptor.getFullName();
2901       proto = problemDescriptor.toProto();
2902       this.description = description;
2903     }
2904 
DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description, final Throwable cause)2905     private DescriptorValidationException(
2906         final GenericDescriptor problemDescriptor,
2907         final String description,
2908         final Throwable cause) {
2909       this(problemDescriptor, description);
2910       initCause(cause);
2911     }
2912 
DescriptorValidationException( final FileDescriptor problemDescriptor, final String description)2913     private DescriptorValidationException(
2914         final FileDescriptor problemDescriptor, final String description) {
2915       super(problemDescriptor.getName() + ": " + description);
2916 
2917       // Note that problemDescriptor may be partially uninitialized, so we
2918       // don't want to expose it directly to the user.  So, we only provide
2919       // the name and the original proto.
2920       name = problemDescriptor.getName();
2921       proto = problemDescriptor.toProto();
2922       this.description = description;
2923     }
2924   }
2925 
2926   // =================================================================
2927 
2928   /**
2929    * A private helper class which contains lookup tables containing all the descriptors defined in a
2930    * particular file.
2931    */
2932   private static final class DescriptorPool {
2933 
2934     /** Defines what subclass of descriptors to search in the descriptor pool. */
2935     enum SearchFilter {
2936       TYPES_ONLY,
2937       AGGREGATES_ONLY,
2938       ALL_SYMBOLS
2939     }
2940 
DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies)2941     DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) {
2942       this.dependencies =
2943           Collections.newSetFromMap(
2944               new IdentityHashMap<FileDescriptor, Boolean>(dependencies.length));
2945       this.allowUnknownDependencies = allowUnknownDependencies;
2946 
2947       for (Descriptors.FileDescriptor dependency : dependencies) {
2948         this.dependencies.add(dependency);
2949         importPublicDependencies(dependency);
2950       }
2951 
2952       for (final FileDescriptor dependency : this.dependencies) {
2953         try {
2954           addPackage(dependency.getPackage(), dependency);
2955         } catch (DescriptorValidationException e) {
2956           // Can't happen, because addPackage() only fails when the name
2957           // conflicts with a non-package, but we have not yet added any
2958           // non-packages at this point.
2959           throw new AssertionError(e);
2960         }
2961       }
2962     }
2963 
2964     /** Find and put public dependencies of the file into dependencies set. */
importPublicDependencies(final FileDescriptor file)2965     private void importPublicDependencies(final FileDescriptor file) {
2966       for (FileDescriptor dependency : file.getPublicDependencies()) {
2967         if (dependencies.add(dependency)) {
2968           importPublicDependencies(dependency);
2969         }
2970       }
2971     }
2972 
2973     private final Set<FileDescriptor> dependencies;
2974     private final boolean allowUnknownDependencies;
2975 
2976     private final Map<String, GenericDescriptor> descriptorsByName = new HashMap<>();
2977 
2978     /** Find a generic descriptor by fully-qualified name. */
findSymbol(final String fullName)2979     GenericDescriptor findSymbol(final String fullName) {
2980       return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
2981     }
2982 
2983     /**
2984      * Find a descriptor by fully-qualified name and given option to only search valid field type
2985      * descriptors.
2986      */
findSymbol(final String fullName, final SearchFilter filter)2987     GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) {
2988       GenericDescriptor result = descriptorsByName.get(fullName);
2989       if (result != null) {
2990         if ((filter == SearchFilter.ALL_SYMBOLS)
2991             || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2992             || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2993           return result;
2994         }
2995       }
2996 
2997       for (final FileDescriptor dependency : dependencies) {
2998         result = dependency.pool.descriptorsByName.get(fullName);
2999         if (result != null) {
3000           if ((filter == SearchFilter.ALL_SYMBOLS)
3001               || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
3002               || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
3003             return result;
3004           }
3005         }
3006       }
3007 
3008       return null;
3009     }
3010 
3011     /** Checks if the descriptor is a valid type for a message field. */
isType(GenericDescriptor descriptor)3012     boolean isType(GenericDescriptor descriptor) {
3013       return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor);
3014     }
3015 
3016     /** Checks if the descriptor is a valid namespace type. */
isAggregate(GenericDescriptor descriptor)3017     boolean isAggregate(GenericDescriptor descriptor) {
3018       return (descriptor instanceof Descriptor)
3019           || (descriptor instanceof EnumDescriptor)
3020           || (descriptor instanceof PackageDescriptor)
3021           || (descriptor instanceof ServiceDescriptor);
3022     }
3023 
3024     /**
3025      * Look up a type descriptor by name, relative to some other descriptor. The name may be
3026      * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name
3027      * lookup semantics are used to search for the matching descriptor.
3028      */
lookupSymbol( final String name, final GenericDescriptor relativeTo, final DescriptorPool.SearchFilter filter)3029     GenericDescriptor lookupSymbol(
3030         final String name,
3031         final GenericDescriptor relativeTo,
3032         final DescriptorPool.SearchFilter filter)
3033         throws DescriptorValidationException {
3034 
3035       GenericDescriptor result;
3036       String fullname;
3037       if (name.startsWith(".")) {
3038         // Fully-qualified name.
3039         fullname = name.substring(1);
3040         result = findSymbol(fullname, filter);
3041       } else {
3042         // If "name" is a compound identifier, we want to search for the
3043         // first component of it, then search within it for the rest.
3044         // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
3045         // defined in multiple parent scopes, we only want to find "Bar.baz" in
3046         // the innermost one.  E.g., the following should produce an error:
3047         //   message Bar { message Baz {} }
3048         //   message Foo {
3049         //     message Bar {
3050         //     }
3051         //     optional Bar.Baz baz = 1;
3052         //   }
3053         // So, we look for just "Foo" first, then look for "Bar.baz" within it
3054         // if found.
3055         final int firstPartLength = name.indexOf('.');
3056         final String firstPart;
3057         if (firstPartLength == -1) {
3058           firstPart = name;
3059         } else {
3060           firstPart = name.substring(0, firstPartLength);
3061         }
3062 
3063         // We will search each parent scope of "relativeTo" looking for the
3064         // symbol.
3065         final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName());
3066 
3067         while (true) {
3068           // Chop off the last component of the scope.
3069           final int dotpos = scopeToTry.lastIndexOf(".");
3070           if (dotpos == -1) {
3071             fullname = name;
3072             result = findSymbol(name, filter);
3073             break;
3074           } else {
3075             scopeToTry.setLength(dotpos + 1);
3076 
3077             // Append firstPart and try to find
3078             scopeToTry.append(firstPart);
3079             result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY);
3080 
3081             if (result != null) {
3082               if (firstPartLength != -1) {
3083                 // We only found the first part of the symbol.  Now look for
3084                 // the whole thing.  If this fails, we *don't* want to keep
3085                 // searching parent scopes.
3086                 scopeToTry.setLength(dotpos + 1);
3087                 scopeToTry.append(name);
3088                 result = findSymbol(scopeToTry.toString(), filter);
3089               }
3090               fullname = scopeToTry.toString();
3091               break;
3092             }
3093 
3094             // Not found.  Remove the name so we can try again.
3095             scopeToTry.setLength(dotpos);
3096           }
3097         }
3098       }
3099 
3100       if (result == null) {
3101         if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) {
3102           logger.warning(
3103               "The descriptor for message type \""
3104                   + name
3105                   + "\" cannot be found and a placeholder is created for it");
3106           // We create a dummy message descriptor here regardless of the
3107           // expected type. If the type should be message, this dummy
3108           // descriptor will work well and if the type should be enum, a
3109           // DescriptorValidationException will be thrown later. In either
3110           // case, the code works as expected: we allow unknown message types
3111           // but not unknown enum types.
3112           result = new Descriptor(fullname);
3113           // Add the placeholder file as a dependency so we can find the
3114           // placeholder symbol when resolving other references.
3115           this.dependencies.add(result.getFile());
3116           return result;
3117         } else {
3118           throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined.");
3119         }
3120       } else {
3121         return result;
3122       }
3123     }
3124 
3125     /**
3126      * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an
3127      * error.
3128      */
addSymbol(final GenericDescriptor descriptor)3129     void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException {
3130       validateSymbolName(descriptor);
3131 
3132       final String fullName = descriptor.getFullName();
3133 
3134       final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
3135       if (old != null) {
3136         descriptorsByName.put(fullName, old);
3137 
3138         if (descriptor.getFile() == old.getFile()) {
3139           final int dotpos = fullName.lastIndexOf('.');
3140           if (dotpos == -1) {
3141             throw new DescriptorValidationException(
3142                 descriptor, '\"' + fullName + "\" is already defined.");
3143           } else {
3144             throw new DescriptorValidationException(
3145                 descriptor,
3146                 '\"'
3147                     + fullName.substring(dotpos + 1)
3148                     + "\" is already defined in \""
3149                     + fullName.substring(0, dotpos)
3150                     + "\".");
3151           }
3152         } else {
3153           throw new DescriptorValidationException(
3154               descriptor,
3155               '\"'
3156                   + fullName
3157                   + "\" is already defined in file \""
3158                   + old.getFile().getName()
3159                   + "\".");
3160         }
3161       }
3162     }
3163 
3164     /**
3165      * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so
3166      * that someone cannot define, say, a message type that has the same name as an existing
3167      * package.
3168      */
3169     private static final class PackageDescriptor extends GenericDescriptor {
3170       @Override
toProto()3171       public Message toProto() {
3172         return file.toProto();
3173       }
3174 
3175       @Override
getName()3176       public String getName() {
3177         return name;
3178       }
3179 
3180       @Override
getFullName()3181       public String getFullName() {
3182         return fullName;
3183       }
3184 
3185       @Override
getFile()3186       public FileDescriptor getFile() {
3187         return file;
3188       }
3189 
PackageDescriptor(final String name, final String fullName, final FileDescriptor file)3190       PackageDescriptor(final String name, final String fullName, final FileDescriptor file) {
3191         this.file = file;
3192         this.fullName = fullName;
3193         this.name = name;
3194       }
3195 
3196       private final String name;
3197       private final String fullName;
3198       private final FileDescriptor file;
3199     }
3200 
3201     /**
3202      * Adds a package to the symbol tables. If a package by the same name already exists, that is
3203      * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If
3204      * the package has multiple components, this also adds the parent package(s).
3205      */
addPackage(final String fullName, final FileDescriptor file)3206     void addPackage(final String fullName, final FileDescriptor file)
3207         throws DescriptorValidationException {
3208       final int dotpos = fullName.lastIndexOf('.');
3209       final String name;
3210       if (dotpos == -1) {
3211         name = fullName;
3212       } else {
3213         addPackage(fullName.substring(0, dotpos), file);
3214         name = fullName.substring(dotpos + 1);
3215       }
3216 
3217       final GenericDescriptor old =
3218           descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file));
3219       if (old != null) {
3220         descriptorsByName.put(fullName, old);
3221         if (!(old instanceof PackageDescriptor)) {
3222           throw new DescriptorValidationException(
3223               file,
3224               '\"'
3225                   + name
3226                   + "\" is already defined (as something other than a "
3227                   + "package) in file \""
3228                   + old.getFile().getName()
3229                   + "\".");
3230         }
3231       }
3232     }
3233 
3234     /**
3235      * Verifies that the descriptor's name is valid. That is, it contains only letters, digits, and
3236      * underscores, and does not start with a digit.
3237      */
validateSymbolName(final GenericDescriptor descriptor)3238     static void validateSymbolName(final GenericDescriptor descriptor)
3239         throws DescriptorValidationException {
3240       final String name = descriptor.getName();
3241       if (name.length() == 0) {
3242         throw new DescriptorValidationException(descriptor, "Missing name.");
3243       }
3244 
3245       // Non-ASCII characters are not valid in protobuf identifiers, even
3246       // if they are letters or digits.
3247       // The first character must be a letter or '_'.
3248       // Subsequent characters may be letters, numbers, or digits.
3249       for (int i = 0; i < name.length(); i++) {
3250         final char c = name.charAt(i);
3251         if (('a' <= c && c <= 'z')
3252             || ('A' <= c && c <= 'Z')
3253             || (c == '_')
3254             || ('0' <= c && c <= '9' && i > 0)) {
3255           // Valid
3256           continue;
3257         }
3258         throw new DescriptorValidationException(
3259             descriptor, '\"' + name + "\" is not a valid identifier.");
3260       }
3261     }
3262   }
3263 
3264   /** Describes a oneof of a message type. */
3265   public static final class OneofDescriptor extends GenericDescriptor {
3266     /** Get the index of this descriptor within its parent. */
getIndex()3267     public int getIndex() {
3268       return index;
3269     }
3270 
3271     @Override
getName()3272     public String getName() {
3273       return proto.getName();
3274     }
3275 
3276     @Override
getFile()3277     public FileDescriptor getFile() {
3278       return file;
3279     }
3280 
3281     @Override
getFullName()3282     public String getFullName() {
3283       return fullName;
3284     }
3285 
getContainingType()3286     public Descriptor getContainingType() {
3287       return containingType;
3288     }
3289 
getFieldCount()3290     public int getFieldCount() {
3291       return fieldCount;
3292     }
3293 
getOptions()3294     public OneofOptions getOptions() {
3295       if (this.options == null) {
3296         OneofOptions strippedOptions = this.proto.getOptions();
3297         if (strippedOptions.hasFeatures()) {
3298           // Clients should be using feature accessor methods, not accessing features on the
3299           // options
3300           // proto.
3301           strippedOptions = strippedOptions.toBuilder().clearFeatures().build();
3302         }
3303         synchronized (this) {
3304           if (this.options == null) {
3305             this.options = strippedOptions;
3306           }
3307         }
3308       }
3309       return this.options;
3310     }
3311 
3312     /** Get a list of this message type's fields. */
getFields()3313     public List<FieldDescriptor> getFields() {
3314       return Collections.unmodifiableList(Arrays.asList(fields));
3315     }
3316 
getField(int index)3317     public FieldDescriptor getField(int index) {
3318       return fields[index];
3319     }
3320 
3321     @Override
toProto()3322     public OneofDescriptorProto toProto() {
3323       return proto;
3324     }
3325 
isSynthetic()3326     boolean isSynthetic() {
3327       return fields.length == 1 && fields[0].isProto3Optional;
3328     }
3329 
3330     /** See {@link FileDescriptor#resolveAllFeatures}. */
resolveAllFeatures()3331     private void resolveAllFeatures() throws DescriptorValidationException {
3332       resolveFeatures(proto.getOptions().getFeatures());
3333     }
3334 
setProto(final OneofDescriptorProto proto)3335     private void setProto(final OneofDescriptorProto proto) throws DescriptorValidationException {
3336       this.proto = proto;
3337       this.options = null;
3338       resolveFeatures(proto.getOptions().getFeatures());
3339     }
3340 
OneofDescriptor( final OneofDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)3341     private OneofDescriptor(
3342         final OneofDescriptorProto proto,
3343         final FileDescriptor file,
3344         final Descriptor parent,
3345         final int index) {
3346       this.parent = parent;
3347       this.proto = proto;
3348       fullName = computeFullName(file, parent, proto.getName());
3349       this.file = file;
3350       this.index = index;
3351 
3352       containingType = parent;
3353       fieldCount = 0;
3354     }
3355 
3356     private final int index;
3357     private OneofDescriptorProto proto;
3358     private volatile OneofOptions options;
3359     private final String fullName;
3360     private final FileDescriptor file;
3361 
3362     private Descriptor containingType;
3363     private int fieldCount;
3364     private FieldDescriptor[] fields;
3365   }
3366 
binarySearch(T[] array, int size, NumberGetter<T> getter, int number)3367   private static <T> T binarySearch(T[] array, int size, NumberGetter<T> getter, int number) {
3368     int left = 0;
3369     int right = size - 1;
3370 
3371     while (left <= right) {
3372       int mid = (left + right) / 2;
3373       T midValue = array[mid];
3374       int midValueNumber = getter.getNumber(midValue);
3375       if (number < midValueNumber) {
3376         right = mid - 1;
3377       } else if (number > midValueNumber) {
3378         left = mid + 1;
3379       } else {
3380         return midValue;
3381       }
3382     }
3383     return null;
3384   }
3385 
3386   private interface NumberGetter<T> {
getNumber(T t)3387     int getNumber(T t);
3388   }
3389 }
3390