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