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