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