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