hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From st...@apache.org
Subject [33/51] [partial] hbase git commit: Revert "HBASE-17056 Remove checked in PB generated files Selective add of dependency on" Revert for now. Build unstable and some interesting issues around CLASSPATH
Date Fri, 07 Jul 2017 05:01:07 GMT
http://git-wip-us.apache.org/repos/asf/hbase/blob/6786b2b6/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/Descriptors.java
----------------------------------------------------------------------
diff --git a/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/Descriptors.java b/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/Descriptors.java
new file mode 100644
index 0000000..62ccd19
--- /dev/null
+++ b/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/Descriptors.java
@@ -0,0 +1,2547 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package org.apache.hadoop.hbase.shaded.com.google.protobuf;
+
+import org.apache.hadoop.hbase.shaded.com.google.protobuf.DescriptorProtos.*;
+import org.apache.hadoop.hbase.shaded.com.google.protobuf.Descriptors.FileDescriptor.Syntax;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Logger;
+
+/**
+ * Contains a collection of classes which describe protocol message types.
+ *
+ * Every message type has a {@link Descriptor}, which lists all
+ * its fields and other information about a type.  You can get a message
+ * type's descriptor by calling {@code MessageType.getDescriptor()}, or
+ * (given a message object of the type) {@code message.getDescriptorForType()}.
+ * Furthermore, each message is associated with a {@link FileDescriptor} for
+ * a relevant {@code .proto} file. You can obtain it by calling
+ * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors
+ * for all the messages defined in that file, and file descriptors for all the
+ * imported {@code .proto} files.
+ *
+ * Descriptors are built from DescriptorProtos, as defined in
+ * {@code google/protobuf/descriptor.proto}.
+ *
+ * @author kenton@google.com Kenton Varda
+ */
+public final class Descriptors {
+  private static final Logger logger =
+      Logger.getLogger(Descriptors.class.getName());
+  /**
+   * Describes a {@code .proto} file, including everything defined within.
+   * That includes, in particular, descriptors for all the messages and
+   * file descriptors for all other imported {@code .proto} files
+   * (dependencies).
+   */
+  public static final class FileDescriptor extends GenericDescriptor {
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public FileDescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the file name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /** Returns this object. */
+    @Override
+    public FileDescriptor getFile() {
+      return this;
+    }
+
+    /** Returns the same as getName(). */
+    @Override
+    public String getFullName() {
+      return proto.getName();
+    }
+
+    /**
+     * Get the proto package name.  This is the package name given by the
+     * {@code package} statement in the {@code .proto} file, which differs
+     * from the Java package.
+     */
+    public String getPackage() { return proto.getPackage(); }
+
+    /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
+    public FileOptions getOptions() { return proto.getOptions(); }
+
+    /** Get a list of top-level message types declared in this file. */
+    public List<Descriptor> getMessageTypes() {
+      return Collections.unmodifiableList(Arrays.asList(messageTypes));
+    }
+
+    /** Get a list of top-level enum types declared in this file. */
+    public List<EnumDescriptor> getEnumTypes() {
+      return Collections.unmodifiableList(Arrays.asList(enumTypes));
+    }
+
+    /** Get a list of top-level services declared in this file. */
+    public List<ServiceDescriptor> getServices() {
+      return Collections.unmodifiableList(Arrays.asList(services));
+    }
+
+    /** Get a list of top-level extensions declared in this file. */
+    public List<FieldDescriptor> getExtensions() {
+      return Collections.unmodifiableList(Arrays.asList(extensions));
+    }
+
+    /** Get a list of this file's dependencies (imports). */
+    public List<FileDescriptor> getDependencies() {
+      return Collections.unmodifiableList(Arrays.asList(dependencies));
+    }
+
+    /** Get a list of this file's public dependencies (public imports). */
+    public List<FileDescriptor> getPublicDependencies() {
+      return Collections.unmodifiableList(Arrays.asList(publicDependencies));
+    }
+
+    /** The syntax of the .proto file. */
+    public enum Syntax {
+      UNKNOWN("unknown"),
+      PROTO2("proto2"),
+      PROTO3("proto3");
+
+      Syntax(String name) {
+        this.name = name;
+      }
+      private final String name;
+    }
+
+    /** Get the syntax of the .proto file. */
+    public Syntax getSyntax() {
+      if (Syntax.PROTO3.name.equals(proto.getSyntax())) {
+        return Syntax.PROTO3;
+      }
+      return Syntax.PROTO2;
+    }
+
+    /**
+     * Find a message type in the file by name.  Does not find nested types.
+     *
+     * @param name The unqualified type name to look for.
+     * @return The message type's descriptor, or {@code null} if not found.
+     */
+    public Descriptor findMessageTypeByName(String name) {
+      // Don't allow looking up nested types.  This will make optimization
+      // easier later.
+      if (name.indexOf('.') != -1) {
+        return null;
+      }
+      if (getPackage().length() > 0) {
+        name = getPackage() + '.' + name;
+      }
+      final GenericDescriptor result = pool.findSymbol(name);
+      if (result != null && result instanceof Descriptor &&
+          result.getFile() == this) {
+        return (Descriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Find an enum type in the file by name.  Does not find nested types.
+     *
+     * @param name The unqualified type name to look for.
+     * @return The enum type's descriptor, or {@code null} if not found.
+     */
+    public EnumDescriptor findEnumTypeByName(String name) {
+      // Don't allow looking up nested types.  This will make optimization
+      // easier later.
+      if (name.indexOf('.') != -1) {
+        return null;
+      }
+      if (getPackage().length() > 0) {
+        name = getPackage() + '.' + name;
+      }
+      final GenericDescriptor result = pool.findSymbol(name);
+      if (result != null && result instanceof EnumDescriptor &&
+          result.getFile() == this) {
+        return (EnumDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Find a service type in the file by name.
+     *
+     * @param name The unqualified type name to look for.
+     * @return The service type's descriptor, or {@code null} if not found.
+     */
+    public ServiceDescriptor findServiceByName(String name) {
+      // Don't allow looking up nested types.  This will make optimization
+      // easier later.
+      if (name.indexOf('.') != -1) {
+        return null;
+      }
+      if (getPackage().length() > 0) {
+        name = getPackage() + '.' + name;
+      }
+      final GenericDescriptor result = pool.findSymbol(name);
+      if (result != null && result instanceof ServiceDescriptor &&
+          result.getFile() == this) {
+        return (ServiceDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Find an extension in the file by name.  Does not find extensions nested
+     * inside message types.
+     *
+     * @param name The unqualified extension name to look for.
+     * @return The extension's descriptor, or {@code null} if not found.
+     */
+    public FieldDescriptor findExtensionByName(String name) {
+      if (name.indexOf('.') != -1) {
+        return null;
+      }
+      if (getPackage().length() > 0) {
+        name = getPackage() + '.' + name;
+      }
+      final GenericDescriptor result = pool.findSymbol(name);
+      if (result != null && result instanceof FieldDescriptor &&
+          result.getFile() == this) {
+        return (FieldDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Construct a {@code FileDescriptor}.
+     *
+     * @param proto The protocol message form of the FileDescriptor.
+     * @param dependencies {@code FileDescriptor}s corresponding to all of
+     *                     the file's dependencies.
+     * @throws DescriptorValidationException {@code proto} is not a valid
+     *           descriptor.  This can occur for a number of reasons, e.g.
+     *           because a field has an undefined type or because two messages
+     *           were defined with the same name.
+     */
+    public static FileDescriptor buildFrom(final FileDescriptorProto proto,
+                                           final FileDescriptor[] dependencies)
+                                    throws DescriptorValidationException {
+      return buildFrom(proto, dependencies, false);
+    }
+
+
+    /**
+     * Construct a {@code FileDescriptor}.
+     *
+     * @param proto The protocol message form of the FileDescriptor.
+     * @param dependencies {@code FileDescriptor}s corresponding to all of
+     *                     the file's dependencies.
+     * @param allowUnknownDependencies If true, non-exist dependenncies will be
+     *           ignored and undefined message types will be replaced with a
+     *           placeholder type.
+     * @throws DescriptorValidationException {@code proto} is not a valid
+     *           descriptor.  This can occur for a number of reasons, e.g.
+     *           because a field has an undefined type or because two messages
+     *           were defined with the same name.
+     */
+    public static FileDescriptor buildFrom(
+        final FileDescriptorProto proto, final FileDescriptor[] dependencies,
+        final boolean allowUnknownDependencies)
+        throws DescriptorValidationException {
+      // Building descriptors involves two steps:  translating and linking.
+      // In the translation step (implemented by FileDescriptor's
+      // constructor), we build an object tree mirroring the
+      // FileDescriptorProto's tree and put all of the descriptors into the
+      // DescriptorPool's lookup tables.  In the linking step, we look up all
+      // type references in the DescriptorPool, so that, for example, a
+      // FieldDescriptor for an embedded message contains a pointer directly
+      // to the Descriptor for that message's type.  We also detect undefined
+      // types in the linking step.
+      final DescriptorPool pool = new DescriptorPool(
+          dependencies, allowUnknownDependencies);
+      final FileDescriptor result = new FileDescriptor(
+          proto, dependencies, pool, allowUnknownDependencies);
+      result.crossLink();
+      return result;
+    }
+
+    /**
+     * This method is to be called by generated code only.  It is equivalent
+     * to {@code buildFrom} except that the {@code FileDescriptorProto} is
+     * encoded in protocol buffer wire format.
+     */
+    public static void internalBuildGeneratedFileFrom(
+        final String[] descriptorDataParts,
+        final FileDescriptor[] dependencies,
+        final InternalDescriptorAssigner descriptorAssigner) {
+      // Hack:  We can't embed a raw byte array inside generated Java code
+      //   (at least, not efficiently), but we can embed Strings.  So, the
+      //   protocol compiler embeds the FileDescriptorProto as a giant
+      //   string literal which is passed to this function to construct the
+      //   file's FileDescriptor.  The string literal contains only 8-bit
+      //   characters, each one representing a byte of the FileDescriptorProto's
+      //   serialized form.  So, if we convert it to bytes in ISO-8859-1, we
+      //   should get the original bytes that we want.
+
+      // descriptorData may contain multiple strings in order to get around the
+      // Java 64k string literal limit.
+      StringBuilder descriptorData = new StringBuilder();
+      for (String part : descriptorDataParts) {
+        descriptorData.append(part);
+      }
+
+      final byte[] descriptorBytes;
+      descriptorBytes = descriptorData.toString().getBytes(Internal.ISO_8859_1);
+
+      FileDescriptorProto proto;
+      try {
+        proto = FileDescriptorProto.parseFrom(descriptorBytes);
+      } catch (InvalidProtocolBufferException e) {
+        throw new IllegalArgumentException(
+          "Failed to parse protocol buffer descriptor for generated code.", e);
+      }
+
+      final FileDescriptor result;
+      try {
+        // When building descriptors for generated code, we allow unknown
+        // dependencies by default.
+        result = buildFrom(proto, dependencies, true);
+      } catch (DescriptorValidationException e) {
+        throw new IllegalArgumentException(
+          "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
+      }
+
+      final ExtensionRegistry registry =
+          descriptorAssigner.assignDescriptors(result);
+
+      if (registry != null) {
+        // We must re-parse the proto using the registry.
+        try {
+          proto = FileDescriptorProto.parseFrom(descriptorBytes, registry);
+        } catch (InvalidProtocolBufferException e) {
+          throw new IllegalArgumentException(
+            "Failed to parse protocol buffer descriptor for generated code.",
+            e);
+        }
+
+        result.setProto(proto);
+      }
+    }
+
+    /**
+     * This method is to be called by generated code only.  It uses Java
+     * reflection to load the dependencies' descriptors.
+     */
+    public static void internalBuildGeneratedFileFrom(
+        final String[] descriptorDataParts,
+        final Class<?> descriptorOuterClass,
+        final String[] dependencies,
+        final String[] dependencyFileNames,
+        final InternalDescriptorAssigner descriptorAssigner) {
+      List<FileDescriptor> descriptors = new ArrayList<FileDescriptor>();
+      for (int i = 0; i < dependencies.length; i++) {
+        try {
+          Class<?> clazz =
+              descriptorOuterClass.getClassLoader().loadClass(dependencies[i]);
+          descriptors.add(
+              (FileDescriptor) clazz.getField("descriptor").get(null));
+        } catch (Exception e) {
+          // We allow unknown dependencies by default. If a dependency cannot
+          // be found we only generate a warning.
+          logger.warning("Descriptors for \"" + dependencyFileNames[i] +
+              "\" can not be found.");
+        }
+      }
+      FileDescriptor[] descriptorArray = new FileDescriptor[descriptors.size()];
+      descriptors.toArray(descriptorArray);
+      internalBuildGeneratedFileFrom(
+          descriptorDataParts, descriptorArray, descriptorAssigner);
+    }
+
+    /**
+     * This method is to be called by generated code only.  It is used to
+     * update the FileDescriptorProto associated with the descriptor by
+     * parsing it again with the given ExtensionRegistry. This is needed to
+     * recognize custom options.
+     */
+    public static void internalUpdateFileDescriptor(
+        final FileDescriptor descriptor,
+        final ExtensionRegistry registry) {
+      ByteString bytes = descriptor.proto.toByteString();
+      FileDescriptorProto proto;
+      try {
+        proto = FileDescriptorProto.parseFrom(bytes, registry);
+      } catch (InvalidProtocolBufferException e) {
+        throw new IllegalArgumentException(
+          "Failed to parse protocol buffer descriptor for generated code.", e);
+      }
+      descriptor.setProto(proto);
+    }
+
+    /**
+     * This class should be used by generated code only.  When calling
+     * {@link FileDescriptor#internalBuildGeneratedFileFrom}, the caller
+     * provides a callback implementing this interface.  The callback is called
+     * after the FileDescriptor has been constructed, in order to assign all
+     * the global variables defined in the generated code which point at parts
+     * of the FileDescriptor.  The callback returns an ExtensionRegistry which
+     * contains any extensions which might be used in the descriptor -- that
+     * is, extensions of the various "Options" messages defined in
+     * descriptor.proto.  The callback may also return null to indicate that
+     * no extensions are used in the descriptor.
+     */
+    public interface InternalDescriptorAssigner {
+      ExtensionRegistry assignDescriptors(FileDescriptor root);
+    }
+
+    private FileDescriptorProto proto;
+    private final Descriptor[] messageTypes;
+    private final EnumDescriptor[] enumTypes;
+    private final ServiceDescriptor[] services;
+    private final FieldDescriptor[] extensions;
+    private final FileDescriptor[] dependencies;
+    private final FileDescriptor[] publicDependencies;
+    private final DescriptorPool pool;
+
+    private FileDescriptor(final FileDescriptorProto proto,
+                           final FileDescriptor[] dependencies,
+                           final DescriptorPool pool,
+                           boolean allowUnknownDependencies)
+                    throws DescriptorValidationException {
+      this.pool = pool;
+      this.proto = proto;
+      this.dependencies = dependencies.clone();
+      HashMap<String, FileDescriptor> nameToFileMap =
+          new HashMap<String, FileDescriptor>();
+      for (FileDescriptor file : dependencies) {
+        nameToFileMap.put(file.getName(), file);
+      }
+      List<FileDescriptor> publicDependencies = new ArrayList<FileDescriptor>();
+      for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
+        int index = proto.getPublicDependency(i);
+        if (index < 0 || index >= proto.getDependencyCount()) {
+          throw new DescriptorValidationException(this,
+              "Invalid public dependency index.");
+        }
+        String name = proto.getDependency(index);
+        FileDescriptor file = nameToFileMap.get(name);
+        if (file == null) {
+          if (!allowUnknownDependencies) {
+            throw new DescriptorValidationException(this,
+                "Invalid public dependency: " + name);
+          }
+          // Ignore unknown dependencies.
+        } else {
+          publicDependencies.add(file);
+        }
+      }
+      this.publicDependencies = new FileDescriptor[publicDependencies.size()];
+      publicDependencies.toArray(this.publicDependencies);
+
+      pool.addPackage(getPackage(), this);
+
+      messageTypes = new Descriptor[proto.getMessageTypeCount()];
+      for (int i = 0; i < proto.getMessageTypeCount(); i++) {
+        messageTypes[i] =
+          new Descriptor(proto.getMessageType(i), this, null, i);
+      }
+
+      enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
+      for (int i = 0; i < proto.getEnumTypeCount(); i++) {
+        enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i);
+      }
+
+      services = new ServiceDescriptor[proto.getServiceCount()];
+      for (int i = 0; i < proto.getServiceCount(); i++) {
+        services[i] = new ServiceDescriptor(proto.getService(i), this, i);
+      }
+
+      extensions = new FieldDescriptor[proto.getExtensionCount()];
+      for (int i = 0; i < proto.getExtensionCount(); i++) {
+        extensions[i] = new FieldDescriptor(
+          proto.getExtension(i), this, null, i, true);
+      }
+    }
+
+    /**
+     * Create a placeholder FileDescriptor for a message Descriptor.
+     */
+    FileDescriptor(String packageName, Descriptor message)
+        throws DescriptorValidationException {
+      this.pool = new DescriptorPool(new FileDescriptor[0], true);
+      this.proto = FileDescriptorProto.newBuilder()
+          .setName(message.getFullName() + ".placeholder.proto")
+          .setPackage(packageName).addMessageType(message.toProto()).build();
+      this.dependencies = new FileDescriptor[0];
+      this.publicDependencies = new FileDescriptor[0];
+
+      messageTypes = new Descriptor[] {message};
+      enumTypes = new EnumDescriptor[0];
+      services = new ServiceDescriptor[0];
+      extensions = new FieldDescriptor[0];
+
+      pool.addPackage(packageName, this);
+      pool.addSymbol(message);
+    }
+
+    /** Look up and cross-link all field types, etc. */
+    private void crossLink() throws DescriptorValidationException {
+      for (final Descriptor messageType : messageTypes) {
+        messageType.crossLink();
+      }
+
+      for (final ServiceDescriptor service : services) {
+        service.crossLink();
+      }
+
+      for (final FieldDescriptor extension : extensions) {
+        extension.crossLink();
+      }
+    }
+
+    /**
+     * Replace our {@link FileDescriptorProto} with the given one, which is
+     * identical except that it might contain extensions that weren't present
+     * in the original.  This method is needed for bootstrapping when a file
+     * defines custom options.  The options may be defined in the file itself,
+     * so we can't actually parse them until we've constructed the descriptors,
+     * but to construct the descriptors we have to have parsed the descriptor
+     * protos.  So, we have to parse the descriptor protos a second time after
+     * constructing the descriptors.
+     */
+    private void setProto(final FileDescriptorProto proto) {
+      this.proto = proto;
+
+      for (int i = 0; i < messageTypes.length; i++) {
+        messageTypes[i].setProto(proto.getMessageType(i));
+      }
+
+      for (int i = 0; i < enumTypes.length; i++) {
+        enumTypes[i].setProto(proto.getEnumType(i));
+      }
+
+      for (int i = 0; i < services.length; i++) {
+        services[i].setProto(proto.getService(i));
+      }
+
+      for (int i = 0; i < extensions.length; i++) {
+        extensions[i].setProto(proto.getExtension(i));
+      }
+    }
+
+    boolean supportsUnknownEnumValue() {
+      return getSyntax() == Syntax.PROTO3;
+    }
+  }
+
+  // =================================================================
+
+  /** Describes a message type. */
+  public static final class Descriptor extends GenericDescriptor {
+    /**
+     * Get the index of this descriptor within its parent.  In other words,
+     * given a {@link FileDescriptor} {@code file}, the following is true:
+     * <pre>
+     *   for all i in [0, file.getMessageTypeCount()):
+     *     file.getMessageType(i).getIndex() == i
+     * </pre>
+     * Similarly, for a {@link Descriptor} {@code messageType}:
+     * <pre>
+     *   for all i in [0, messageType.getNestedTypeCount()):
+     *     messageType.getNestedType(i).getIndex() == i
+     * </pre>
+     */
+    public int getIndex() { return index; }
+
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public DescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the type's unqualified name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /**
+     * Get the type's fully-qualified name, within the proto language's
+     * namespace.  This differs from the Java name.  For example, given this
+     * {@code .proto}:
+     * <pre>
+     *   package foo.bar;
+     *   option java_package = "com.example.protos"
+     *   message Baz {}
+     * </pre>
+     * {@code Baz}'s full name is "foo.bar.Baz".
+     */
+    @Override
+    public String getFullName() {
+      return fullName;
+    }
+
+    /** Get the {@link FileDescriptor} containing this descriptor. */
+    @Override
+    public FileDescriptor getFile() {
+      return file;
+    }
+
+    /** If this is a nested type, get the outer descriptor, otherwise null. */
+    public Descriptor getContainingType() { return containingType; }
+
+    /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
+    public MessageOptions getOptions() { return proto.getOptions(); }
+
+    /** Get a list of this message type's fields. */
+    public List<FieldDescriptor> getFields() {
+      return Collections.unmodifiableList(Arrays.asList(fields));
+    }
+
+    /** Get a list of this message type's oneofs. */
+    public List<OneofDescriptor> getOneofs() {
+      return Collections.unmodifiableList(Arrays.asList(oneofs));
+    }
+
+    /** Get a list of this message type's extensions. */
+    public List<FieldDescriptor> getExtensions() {
+      return Collections.unmodifiableList(Arrays.asList(extensions));
+    }
+
+    /** Get a list of message types nested within this one. */
+    public List<Descriptor> getNestedTypes() {
+      return Collections.unmodifiableList(Arrays.asList(nestedTypes));
+    }
+
+    /** Get a list of enum types nested within this one. */
+    public List<EnumDescriptor> getEnumTypes() {
+      return Collections.unmodifiableList(Arrays.asList(enumTypes));
+    }
+
+    /** Determines if the given field number is an extension. */
+    public boolean isExtensionNumber(final int number) {
+      for (final DescriptorProto.ExtensionRange range :
+          proto.getExtensionRangeList()) {
+        if (range.getStart() <= number && number < range.getEnd()) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /** Determines if the given field number is reserved. */
+    public boolean isReservedNumber(final int number) {
+      for (final DescriptorProto.ReservedRange range :
+          proto.getReservedRangeList()) {
+        if (range.getStart() <= number && number < range.getEnd()) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /** Determines if the given field name is reserved. */
+    public boolean isReservedName(final String name) {
+      if (name == null) {
+        throw new NullPointerException();
+      }
+      for (final String reservedName : proto.getReservedNameList()) {
+        if (reservedName.equals(name)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /**
+     * Indicates whether the message can be extended.  That is, whether it has
+     * any "extensions x to y" ranges declared on it.
+     */
+    public boolean isExtendable() {
+      return proto.getExtensionRangeList().size() != 0;
+    }
+
+    /**
+     * Finds a field by name.
+     * @param name The unqualified name of the field (e.g. "foo").
+     * @return The field's descriptor, or {@code null} if not found.
+     */
+    public FieldDescriptor findFieldByName(final String name) {
+      final GenericDescriptor result =
+          file.pool.findSymbol(fullName + '.' + name);
+      if (result != null && result instanceof FieldDescriptor) {
+        return (FieldDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Finds a field by field number.
+     * @param number The field number within this message type.
+     * @return The field's descriptor, or {@code null} if not found.
+     */
+    public FieldDescriptor findFieldByNumber(final int number) {
+      return file.pool.fieldsByNumber.get(
+        new DescriptorPool.DescriptorIntPair(this, number));
+    }
+
+    /**
+     * Finds a nested message type by name.
+     * @param name The unqualified name of the nested type (e.g. "Foo").
+     * @return The types's descriptor, or {@code null} if not found.
+     */
+    public Descriptor findNestedTypeByName(final String name) {
+      final GenericDescriptor result =
+          file.pool.findSymbol(fullName + '.' + name);
+      if (result != null && result instanceof Descriptor) {
+        return (Descriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Finds a nested enum type by name.
+     * @param name The unqualified name of the nested type (e.g. "Foo").
+     * @return The types's descriptor, or {@code null} if not found.
+     */
+    public EnumDescriptor findEnumTypeByName(final String name) {
+      final GenericDescriptor result =
+          file.pool.findSymbol(fullName + '.' + name);
+      if (result != null && result instanceof EnumDescriptor) {
+        return (EnumDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    private final int index;
+    private DescriptorProto proto;
+    private final String fullName;
+    private final FileDescriptor file;
+    private final Descriptor containingType;
+    private final Descriptor[] nestedTypes;
+    private final EnumDescriptor[] enumTypes;
+    private final FieldDescriptor[] fields;
+    private final FieldDescriptor[] extensions;
+    private final OneofDescriptor[] oneofs;
+
+    // Used to create a placeholder when the type cannot be found.
+    Descriptor(final String fullname) throws DescriptorValidationException {
+      String name = fullname;
+      String packageName = "";
+      int pos = fullname.lastIndexOf('.');
+      if (pos != -1) {
+        name = fullname.substring(pos + 1);
+        packageName = fullname.substring(0, pos);
+      }
+      this.index = 0;
+      this.proto = DescriptorProto.newBuilder().setName(name).addExtensionRange(
+          DescriptorProto.ExtensionRange.newBuilder().setStart(1)
+          .setEnd(536870912).build()).build();
+      this.fullName = fullname;
+      this.containingType = null;
+
+      this.nestedTypes = new Descriptor[0];
+      this.enumTypes = new EnumDescriptor[0];
+      this.fields = new FieldDescriptor[0];
+      this.extensions = new FieldDescriptor[0];
+      this.oneofs = new OneofDescriptor[0];
+
+      // Create a placeholder FileDescriptor to hold this message.
+      this.file = new FileDescriptor(packageName, this);
+    }
+
+    private Descriptor(final DescriptorProto proto,
+                       final FileDescriptor file,
+                       final Descriptor parent,
+                       final int index)
+                throws DescriptorValidationException {
+      this.index = index;
+      this.proto = proto;
+      fullName = computeFullName(file, parent, proto.getName());
+      this.file = file;
+      containingType = parent;
+
+      oneofs = new OneofDescriptor[proto.getOneofDeclCount()];
+      for (int i = 0; i < proto.getOneofDeclCount(); i++) {
+        oneofs[i] = new OneofDescriptor(
+          proto.getOneofDecl(i), file, this, i);
+      }
+
+      nestedTypes = new Descriptor[proto.getNestedTypeCount()];
+      for (int i = 0; i < proto.getNestedTypeCount(); i++) {
+        nestedTypes[i] = new Descriptor(
+          proto.getNestedType(i), file, this, i);
+      }
+
+      enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
+      for (int i = 0; i < proto.getEnumTypeCount(); i++) {
+        enumTypes[i] = new EnumDescriptor(
+          proto.getEnumType(i), file, this, i);
+      }
+
+      fields = new FieldDescriptor[proto.getFieldCount()];
+      for (int i = 0; i < proto.getFieldCount(); i++) {
+        fields[i] = new FieldDescriptor(
+          proto.getField(i), file, this, i, false);
+      }
+
+      extensions = new FieldDescriptor[proto.getExtensionCount()];
+      for (int i = 0; i < proto.getExtensionCount(); i++) {
+        extensions[i] = new FieldDescriptor(
+          proto.getExtension(i), file, this, i, true);
+      }
+
+      for (int i = 0; i < proto.getOneofDeclCount(); i++) {
+        oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()];
+        oneofs[i].fieldCount = 0;
+      }
+      for (int i = 0; i < proto.getFieldCount(); i++) {
+        OneofDescriptor oneofDescriptor = fields[i].getContainingOneof();
+        if (oneofDescriptor != null) {
+          oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
+        }
+      }
+
+      file.pool.addSymbol(this);
+    }
+
+    /** Look up and cross-link all field types, etc. */
+    private void crossLink() throws DescriptorValidationException {
+      for (final Descriptor nestedType : nestedTypes) {
+        nestedType.crossLink();
+      }
+
+      for (final FieldDescriptor field : fields) {
+        field.crossLink();
+      }
+
+      for (final FieldDescriptor extension : extensions) {
+        extension.crossLink();
+      }
+    }
+
+    /** See {@link FileDescriptor#setProto}. */
+    private void setProto(final DescriptorProto proto) {
+      this.proto = proto;
+
+      for (int i = 0; i < nestedTypes.length; i++) {
+        nestedTypes[i].setProto(proto.getNestedType(i));
+      }
+
+      for (int i = 0; i < oneofs.length; i++) {
+        oneofs[i].setProto(proto.getOneofDecl(i));
+      }
+
+      for (int i = 0; i < enumTypes.length; i++) {
+        enumTypes[i].setProto(proto.getEnumType(i));
+      }
+
+      for (int i = 0; i < fields.length; i++) {
+        fields[i].setProto(proto.getField(i));
+      }
+
+      for (int i = 0; i < extensions.length; i++) {
+        extensions[i].setProto(proto.getExtension(i));
+      }
+    }
+  }
+
+  // =================================================================
+
+  /** Describes a field of a message type. */
+  public static final class FieldDescriptor
+        extends GenericDescriptor
+        implements Comparable<FieldDescriptor>,
+                 FieldSet.FieldDescriptorLite<FieldDescriptor> {
+    /**
+     * Get the index of this descriptor within its parent.
+     * @see Descriptors.Descriptor#getIndex()
+     */
+    public int getIndex() { return index; }
+
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public FieldDescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the field's unqualified name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /** Get the field's number. */
+    @Override
+    public int getNumber() {
+      return proto.getNumber();
+    }
+
+    /**
+     * Get the field's fully-qualified name.
+     * @see Descriptors.Descriptor#getFullName()
+     */
+    @Override
+    public String getFullName() {
+      return fullName;
+    }
+
+    /** Get the JSON name of this field. */
+    public String getJsonName() {
+      return jsonName;
+    }
+
+    /**
+     * Get the field's java type.  This is just for convenience.  Every
+     * {@code FieldDescriptorProto.Type} maps to exactly one Java type.
+     */
+    public JavaType getJavaType() { return type.getJavaType(); }
+
+    /** For internal use only. */
+    @Override
+    public WireFormat.JavaType getLiteJavaType() {
+      return getLiteType().getJavaType();
+    }
+
+    /** Get the {@code FileDescriptor} containing this descriptor. */
+    @Override
+    public FileDescriptor getFile() {
+      return file;
+    }
+
+    /** Get the field's declared type. */
+    public Type getType() { return type; }
+
+    /** For internal use only. */
+    @Override
+    public WireFormat.FieldType getLiteType() {
+      return table[type.ordinal()];
+    }
+
+    /** For internal use only. */
+    public boolean needsUtf8Check() {
+      if (type != Type.STRING) {
+        return false;
+      }
+      if (getContainingType().getOptions().getMapEntry()) {
+        // Always enforce strict UTF-8 checking for map fields.
+        return true;
+      }
+      if (getFile().getSyntax() == Syntax.PROTO3) {
+        return true;
+      }
+      return getFile().getOptions().getJavaStringCheckUtf8();
+    }
+
+    public boolean isMapField() {
+      return getType() == Type.MESSAGE && isRepeated()
+          && getMessageType().getOptions().getMapEntry();
+    }
+
+    // I'm pretty sure values() constructs a new array every time, since there
+    // is nothing stopping the caller from mutating the array.  Therefore we
+    // make a static copy here.
+    private static final WireFormat.FieldType[] table =
+        WireFormat.FieldType.values();
+
+    /** Is this field declared required? */
+    public boolean isRequired() {
+      return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED;
+    }
+
+    /** Is this field declared optional? */
+    public boolean isOptional() {
+      return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL;
+    }
+
+    /** Is this field declared repeated? */
+    @Override
+    public boolean isRepeated() {
+      return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
+    }
+
+    /** Does this field have the {@code [packed = true]} option or is this field
+     *  packable in proto3 and not explicitly setted to unpacked?
+     */
+    @Override
+    public boolean isPacked() {
+      if (!isPackable()) {
+        return false;
+      }
+      if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) {
+        return getOptions().getPacked();
+      } else {
+        return !getOptions().hasPacked() || getOptions().getPacked();
+      }
+    }
+
+    /** Can this field be packed? i.e. is it a repeated primitive field? */
+    public boolean isPackable() {
+      return isRepeated() && getLiteType().isPackable();
+    }
+
+    /** Returns true if the field had an explicitly-defined default value. */
+    public boolean hasDefaultValue() { return proto.hasDefaultValue(); }
+
+    /**
+     * Returns the field's default value.  Valid for all types except for
+     * messages and groups.  For all other types, the object returned is of
+     * the same class that would returned by Message.getField(this).
+     */
+    public Object getDefaultValue() {
+      if (getJavaType() == JavaType.MESSAGE) {
+        throw new UnsupportedOperationException(
+          "FieldDescriptor.getDefaultValue() called on an embedded message " +
+          "field.");
+      }
+      return defaultValue;
+    }
+
+    /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
+    public FieldOptions getOptions() { return proto.getOptions(); }
+
+    /** Is this field an extension? */
+    public boolean isExtension() { return proto.hasExtendee(); }
+
+    /**
+     * Get the field's containing type. For extensions, this is the type being
+     * extended, not the location where the extension was defined.  See
+     * {@link #getExtensionScope()}.
+     */
+    public Descriptor getContainingType() { return containingType; }
+
+    /** Get the field's containing oneof. */
+    public OneofDescriptor getContainingOneof() { return containingOneof; }
+
+    /**
+     * For extensions defined nested within message types, gets the outer
+     * type.  Not valid for non-extension fields.  For example, consider
+     * this {@code .proto} file:
+     * <pre>
+     *   message Foo {
+     *     extensions 1000 to max;
+     *   }
+     *   extend Foo {
+     *     optional int32 baz = 1234;
+     *   }
+     *   message Bar {
+     *     extend Foo {
+     *       optional int32 qux = 4321;
+     *     }
+     *   }
+     * </pre>
+     * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}.
+     * However, {@code baz}'s extension scope is {@code null} while
+     * {@code qux}'s extension scope is {@code Bar}.
+     */
+    public Descriptor getExtensionScope() {
+      if (!isExtension()) {
+        throw new UnsupportedOperationException(
+          "This field is not an extension.");
+      }
+      return extensionScope;
+    }
+
+    /** For embedded message and group fields, gets the field's type. */
+    public Descriptor getMessageType() {
+      if (getJavaType() != JavaType.MESSAGE) {
+        throw new UnsupportedOperationException(
+          "This field is not of message type.");
+      }
+      return messageType;
+    }
+
+    /** For enum fields, gets the field's type. */
+    @Override
+    public EnumDescriptor getEnumType() {
+      if (getJavaType() != JavaType.ENUM) {
+        throw new UnsupportedOperationException(
+          "This field is not of enum type.");
+      }
+      return enumType;
+    }
+
+    /**
+     * Compare with another {@code FieldDescriptor}.  This orders fields in
+     * "canonical" order, which simply means ascending order by field number.
+     * {@code other} must be a field of the same type -- i.e.
+     * {@code getContainingType()} must return the same {@code Descriptor} for
+     * both fields.
+     *
+     * @return negative, zero, or positive if {@code this} is less than,
+     *         equal to, or greater than {@code other}, respectively.
+     */
+    @Override
+    public int compareTo(final FieldDescriptor other) {
+      if (other.containingType != containingType) {
+        throw new IllegalArgumentException(
+          "FieldDescriptors can only be compared to other FieldDescriptors " +
+          "for fields of the same message type.");
+      }
+      return getNumber() - other.getNumber();
+    }
+
+    @Override
+    public String toString() {
+      return getFullName();
+    }
+
+    private final int index;
+
+    private FieldDescriptorProto proto;
+    private final String fullName;
+    private final String jsonName;
+    private final FileDescriptor file;
+    private final Descriptor extensionScope;
+
+    // Possibly initialized during cross-linking.
+    private Type type;
+    private Descriptor containingType;
+    private Descriptor messageType;
+    private OneofDescriptor containingOneof;
+    private EnumDescriptor enumType;
+    private Object defaultValue;
+
+    public enum Type {
+      DOUBLE  (JavaType.DOUBLE     ),
+      FLOAT   (JavaType.FLOAT      ),
+      INT64   (JavaType.LONG       ),
+      UINT64  (JavaType.LONG       ),
+      INT32   (JavaType.INT        ),
+      FIXED64 (JavaType.LONG       ),
+      FIXED32 (JavaType.INT        ),
+      BOOL    (JavaType.BOOLEAN    ),
+      STRING  (JavaType.STRING     ),
+      GROUP   (JavaType.MESSAGE    ),
+      MESSAGE (JavaType.MESSAGE    ),
+      BYTES   (JavaType.BYTE_STRING),
+      UINT32  (JavaType.INT        ),
+      ENUM    (JavaType.ENUM       ),
+      SFIXED32(JavaType.INT        ),
+      SFIXED64(JavaType.LONG       ),
+      SINT32  (JavaType.INT        ),
+      SINT64  (JavaType.LONG       );
+
+      Type(final JavaType javaType) {
+        this.javaType = javaType;
+      }
+
+      private JavaType javaType;
+
+      public FieldDescriptorProto.Type toProto() {
+        return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
+      }
+      public JavaType getJavaType() { return javaType; }
+
+      public static Type valueOf(final FieldDescriptorProto.Type type) {
+        return values()[type.getNumber() - 1];
+      }
+    }
+
+    static {
+      // Refuse to init if someone added a new declared type.
+      if (Type.values().length != FieldDescriptorProto.Type.values().length) {
+        throw new RuntimeException(""
+            + "descriptor.proto has a new declared type but Descriptors.java "
+            + "wasn't updated.");
+      }
+    }
+
+    public enum JavaType {
+      INT(0),
+      LONG(0L),
+      FLOAT(0F),
+      DOUBLE(0D),
+      BOOLEAN(false),
+      STRING(""),
+      BYTE_STRING(ByteString.EMPTY),
+      ENUM(null),
+      MESSAGE(null);
+
+      JavaType(final Object defaultDefault) {
+        this.defaultDefault = defaultDefault;
+      }
+
+      /**
+       * The default default value for fields of this type, if it's a primitive
+       * type.  This is meant for use inside this file only, hence is private.
+       */
+      private final Object defaultDefault;
+    }
+
+    // This method should match exactly with the ToJsonName() function in C++
+    // descriptor.cc.
+    private static String fieldNameToJsonName(String name) {
+      StringBuilder result = new StringBuilder(name.length());
+      boolean isNextUpperCase = false;
+      for (int i = 0; i < name.length(); i++) {
+        Character ch = name.charAt(i);
+        if (ch == '_') {
+          isNextUpperCase = true;
+        } else if (isNextUpperCase) {
+          result.append(Character.toUpperCase(ch));
+          isNextUpperCase = false;
+        } else {
+          result.append(ch);
+        }
+      }
+      return result.toString();
+    }
+
+    private FieldDescriptor(final FieldDescriptorProto proto,
+                            final FileDescriptor file,
+                            final Descriptor parent,
+                            final int index,
+                            final boolean isExtension)
+                     throws DescriptorValidationException {
+      this.index = index;
+      this.proto = proto;
+      fullName = computeFullName(file, parent, proto.getName());
+      this.file = file;
+      if (proto.hasJsonName()) {
+        jsonName = proto.getJsonName();
+      } else {
+        jsonName = fieldNameToJsonName(proto.getName());
+      }
+
+      if (proto.hasType()) {
+        type = Type.valueOf(proto.getType());
+      }
+
+      if (getNumber() <= 0) {
+        throw new DescriptorValidationException(this,
+          "Field numbers must be positive integers.");
+      }
+
+      if (isExtension) {
+        if (!proto.hasExtendee()) {
+          throw new DescriptorValidationException(this,
+            "FieldDescriptorProto.extendee not set for extension field.");
+        }
+        containingType = null;  // Will be filled in when cross-linking
+        if (parent != null) {
+          extensionScope = parent;
+        } else {
+          extensionScope = null;
+        }
+
+        if (proto.hasOneofIndex()) {
+          throw new DescriptorValidationException(this,
+            "FieldDescriptorProto.oneof_index set for extension field.");
+        }
+        containingOneof = null;
+      } else {
+        if (proto.hasExtendee()) {
+          throw new DescriptorValidationException(this,
+            "FieldDescriptorProto.extendee set for non-extension field.");
+        }
+        containingType = parent;
+
+        if (proto.hasOneofIndex()) {
+          if (proto.getOneofIndex() < 0 ||
+              proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) {
+            throw new DescriptorValidationException(this,
+              "FieldDescriptorProto.oneof_index is out of range for type "
+              + parent.getName());
+          }
+          containingOneof = parent.getOneofs().get(proto.getOneofIndex());
+          containingOneof.fieldCount++;
+        } else {
+          containingOneof = null;
+        }
+        extensionScope = null;
+      }
+
+      file.pool.addSymbol(this);
+    }
+
+    /** Look up and cross-link all field types, etc. */
+    private void crossLink() throws DescriptorValidationException {
+      if (proto.hasExtendee()) {
+        final GenericDescriptor extendee =
+          file.pool.lookupSymbol(proto.getExtendee(), this,
+              DescriptorPool.SearchFilter.TYPES_ONLY);
+        if (!(extendee instanceof Descriptor)) {
+          throw new DescriptorValidationException(this,
+              '\"' + proto.getExtendee() + "\" is not a message type.");
+        }
+        containingType = (Descriptor)extendee;
+
+        if (!getContainingType().isExtensionNumber(getNumber())) {
+          throw new DescriptorValidationException(this,
+              '\"' + getContainingType().getFullName() +
+              "\" does not declare " + getNumber() +
+              " as an extension number.");
+        }
+      }
+
+      if (proto.hasTypeName()) {
+        final GenericDescriptor typeDescriptor =
+          file.pool.lookupSymbol(proto.getTypeName(), this,
+              DescriptorPool.SearchFilter.TYPES_ONLY);
+
+        if (!proto.hasType()) {
+          // Choose field type based on symbol.
+          if (typeDescriptor instanceof Descriptor) {
+            type = Type.MESSAGE;
+          } else if (typeDescriptor instanceof EnumDescriptor) {
+            type = Type.ENUM;
+          } else {
+            throw new DescriptorValidationException(this,
+                '\"' + proto.getTypeName() + "\" is not a type.");
+          }
+        }
+
+        if (getJavaType() == JavaType.MESSAGE) {
+          if (!(typeDescriptor instanceof Descriptor)) {
+            throw new DescriptorValidationException(this,
+                '\"' + proto.getTypeName() + "\" is not a message type.");
+          }
+          messageType = (Descriptor)typeDescriptor;
+
+          if (proto.hasDefaultValue()) {
+            throw new DescriptorValidationException(this,
+              "Messages can't have default values.");
+          }
+        } else if (getJavaType() == JavaType.ENUM) {
+          if (!(typeDescriptor instanceof EnumDescriptor)) {
+            throw new DescriptorValidationException(this,
+                '\"' + proto.getTypeName() + "\" is not an enum type.");
+          }
+          enumType = (EnumDescriptor)typeDescriptor;
+        } else {
+          throw new DescriptorValidationException(this,
+            "Field with primitive type has type_name.");
+        }
+      } else {
+        if (getJavaType() == JavaType.MESSAGE ||
+            getJavaType() == JavaType.ENUM) {
+          throw new DescriptorValidationException(this,
+            "Field with message or enum type missing type_name.");
+        }
+      }
+
+      // Only repeated primitive fields may be packed.
+      if (proto.getOptions().getPacked() && !isPackable()) {
+        throw new DescriptorValidationException(this,
+          "[packed = true] can only be specified for repeated primitive " +
+          "fields.");
+      }
+
+      // We don't attempt to parse the default value until here because for
+      // enums we need the enum type's descriptor.
+      if (proto.hasDefaultValue()) {
+        if (isRepeated()) {
+          throw new DescriptorValidationException(this,
+            "Repeated fields cannot have default values.");
+        }
+
+        try {
+          switch (getType()) {
+            case INT32:
+            case SINT32:
+            case SFIXED32:
+              defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
+              break;
+            case UINT32:
+            case FIXED32:
+              defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
+              break;
+            case INT64:
+            case SINT64:
+            case SFIXED64:
+              defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
+              break;
+            case UINT64:
+            case FIXED64:
+              defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
+              break;
+            case FLOAT:
+              if (proto.getDefaultValue().equals("inf")) {
+                defaultValue = Float.POSITIVE_INFINITY;
+              } else if (proto.getDefaultValue().equals("-inf")) {
+                defaultValue = Float.NEGATIVE_INFINITY;
+              } else if (proto.getDefaultValue().equals("nan")) {
+                defaultValue = Float.NaN;
+              } else {
+                defaultValue = Float.valueOf(proto.getDefaultValue());
+              }
+              break;
+            case DOUBLE:
+              if (proto.getDefaultValue().equals("inf")) {
+                defaultValue = Double.POSITIVE_INFINITY;
+              } else if (proto.getDefaultValue().equals("-inf")) {
+                defaultValue = Double.NEGATIVE_INFINITY;
+              } else if (proto.getDefaultValue().equals("nan")) {
+                defaultValue = Double.NaN;
+              } else {
+                defaultValue = Double.valueOf(proto.getDefaultValue());
+              }
+              break;
+            case BOOL:
+              defaultValue = Boolean.valueOf(proto.getDefaultValue());
+              break;
+            case STRING:
+              defaultValue = proto.getDefaultValue();
+              break;
+            case BYTES:
+              try {
+                defaultValue =
+                  TextFormat.unescapeBytes(proto.getDefaultValue());
+              } catch (TextFormat.InvalidEscapeSequenceException e) {
+                throw new DescriptorValidationException(this,
+                  "Couldn't parse default value: " + e.getMessage(), e);
+              }
+              break;
+            case ENUM:
+              defaultValue = enumType.findValueByName(proto.getDefaultValue());
+              if (defaultValue == null) {
+                throw new DescriptorValidationException(this,
+                  "Unknown enum default value: \"" +
+                  proto.getDefaultValue() + '\"');
+              }
+              break;
+            case MESSAGE:
+            case GROUP:
+              throw new DescriptorValidationException(this,
+                "Message type had default value.");
+          }
+        } catch (NumberFormatException e) {
+          throw new DescriptorValidationException(this,
+              "Could not parse default value: \"" +
+              proto.getDefaultValue() + '\"', e);
+        }
+      } else {
+        // Determine the default default for this field.
+        if (isRepeated()) {
+          defaultValue = Collections.emptyList();
+        } else {
+          switch (getJavaType()) {
+            case ENUM:
+              // We guarantee elsewhere that an enum type always has at least
+              // one possible value.
+              defaultValue = enumType.getValues().get(0);
+              break;
+            case MESSAGE:
+              defaultValue = null;
+              break;
+            default:
+              defaultValue = getJavaType().defaultDefault;
+              break;
+          }
+        }
+      }
+
+      if (!isExtension()) {
+        file.pool.addFieldByNumber(this);
+      }
+
+      if (containingType != null &&
+          containingType.getOptions().getMessageSetWireFormat()) {
+        if (isExtension()) {
+          if (!isOptional() || getType() != Type.MESSAGE) {
+            throw new DescriptorValidationException(this,
+              "Extensions of MessageSets must be optional messages.");
+          }
+        } else {
+          throw new DescriptorValidationException(this,
+            "MessageSets cannot have fields, only extensions.");
+        }
+      }
+    }
+
+    /** See {@link FileDescriptor#setProto}. */
+    private void setProto(final FieldDescriptorProto proto) {
+      this.proto = proto;
+    }
+
+    /**
+     * For internal use only.  This is to satisfy the FieldDescriptorLite
+     * interface.
+     */
+    @Override
+    public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
+      // FieldDescriptors are only used with non-lite messages so we can just
+      // down-cast and call mergeFrom directly.
+      return ((Message.Builder) to).mergeFrom((Message) from);
+    }
+
+  }
+
+  // =================================================================
+
+  /** Describes an enum type. */
+  public static final class EnumDescriptor extends GenericDescriptor
+      implements Internal.EnumLiteMap<EnumValueDescriptor> {
+    /**
+     * Get the index of this descriptor within its parent.
+     * @see Descriptors.Descriptor#getIndex()
+     */
+    public int getIndex() { return index; }
+
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public EnumDescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the type's unqualified name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /**
+     * Get the type's fully-qualified name.
+     * @see Descriptors.Descriptor#getFullName()
+     */
+    @Override
+    public String getFullName() {
+      return fullName;
+    }
+
+    /** Get the {@link FileDescriptor} containing this descriptor. */
+    @Override
+    public FileDescriptor getFile() {
+      return file;
+    }
+
+    /** If this is a nested type, get the outer descriptor, otherwise null. */
+    public Descriptor getContainingType() { return containingType; }
+
+    /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
+    public EnumOptions getOptions() { return proto.getOptions(); }
+
+    /** Get a list of defined values for this enum. */
+    public List<EnumValueDescriptor> getValues() {
+      return Collections.unmodifiableList(Arrays.asList(values));
+    }
+
+    /**
+     * Find an enum value by name.
+     * @param name The unqualified name of the value (e.g. "FOO").
+     * @return the value's descriptor, or {@code null} if not found.
+     */
+    public EnumValueDescriptor findValueByName(final String name) {
+      final GenericDescriptor result =
+          file.pool.findSymbol(fullName + '.' + name);
+      if (result != null && result instanceof EnumValueDescriptor) {
+        return (EnumValueDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * Find an enum value by number.  If multiple enum values have the same
+     * number, this returns the first defined value with that number.
+     * @param number The value's number.
+     * @return the value's descriptor, or {@code null} if not found.
+     */
+    @Override
+    public EnumValueDescriptor findValueByNumber(final int number) {
+      return file.pool.enumValuesByNumber.get(
+        new DescriptorPool.DescriptorIntPair(this, number));
+    }
+
+    /**
+     * Get the enum value for a number. If no enum value has this number,
+     * construct an EnumValueDescriptor for it.
+     */
+    public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) {
+      EnumValueDescriptor result = findValueByNumber(number);
+      if (result != null) {
+        return result;
+      }
+      // The number represents an unknown enum value.
+      synchronized (this) {
+        // Descriptors are compared by object identity so for the same number
+        // we need to return the same EnumValueDescriptor object. This means
+        // we have to store created EnumValueDescriptors. However, as there
+        // are potentially 2G unknown enum values, storing all of these
+        // objects persistently will consume lots of memory for long-running
+        // services and it's also unnecessary as not many EnumValueDescriptors
+        // will be used at the same time.
+        //
+        // To solve the problem we take advantage of Java's weak references and
+        // rely on gc to release unused descriptors.
+        //
+        // Here is how it works:
+        //   * We store unknown EnumValueDescriptors in a WeakHashMap with the
+        //     value being a weak reference to the descriptor.
+        //   * The descriptor holds a strong reference to the key so as long
+        //     as the EnumValueDescriptor is in use, the key will be there
+        //     and the corresponding map entry will be there. Following-up
+        //     queries with the same number will return the same descriptor.
+        //   * If the user no longer uses an unknown EnumValueDescriptor,
+        //     it will be gc-ed since we only hold a weak reference to it in
+        //     the map. The key in the corresponding map entry will also be
+        //     gc-ed as the only strong reference to it is in the descriptor
+        //     which is just gc-ed. With the key being gone WeakHashMap will
+        //     then remove the whole entry. This way unknown descriptors will
+        //     be freed automatically and we don't need to do anything to
+        //     clean-up unused map entries.
+
+        // Note: We must use "new Integer(number)" here because we don't want
+        // these Integer objects to be cached.
+        Integer key = new Integer(number);
+        WeakReference<EnumValueDescriptor> reference = unknownValues.get(key);
+        if (reference != null) {
+          result = reference.get();
+        }
+        if (result == null) {
+          result = new EnumValueDescriptor(file, this, key);
+          unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result));
+        }
+      }
+      return result;
+    }
+
+    // Used in tests only.
+    int getUnknownEnumValueDescriptorCount() {
+      return unknownValues.size();
+    }
+
+    private final int index;
+    private EnumDescriptorProto proto;
+    private final String fullName;
+    private final FileDescriptor file;
+    private final Descriptor containingType;
+    private EnumValueDescriptor[] values;
+    private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues =
+        new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>();
+
+    private EnumDescriptor(final EnumDescriptorProto proto,
+                           final FileDescriptor file,
+                           final Descriptor parent,
+                           final int index)
+                    throws DescriptorValidationException {
+      this.index = index;
+      this.proto = proto;
+      fullName = computeFullName(file, parent, proto.getName());
+      this.file = file;
+      containingType = parent;
+
+      if (proto.getValueCount() == 0) {
+        // We cannot allow enums with no values because this would mean there
+        // would be no valid default value for fields of this type.
+        throw new DescriptorValidationException(this,
+          "Enums must contain at least one value.");
+      }
+
+      values = new EnumValueDescriptor[proto.getValueCount()];
+      for (int i = 0; i < proto.getValueCount(); i++) {
+        values[i] = new EnumValueDescriptor(
+          proto.getValue(i), file, this, i);
+      }
+
+      file.pool.addSymbol(this);
+    }
+
+    /** See {@link FileDescriptor#setProto}. */
+    private void setProto(final EnumDescriptorProto proto) {
+      this.proto = proto;
+
+      for (int i = 0; i < values.length; i++) {
+        values[i].setProto(proto.getValue(i));
+      }
+    }
+  }
+
+  // =================================================================
+
+  /**
+   * Describes one value within an enum type.  Note that multiple defined
+   * values may have the same number.  In generated Java code, all values
+   * with the same number after the first become aliases of the first.
+   * However, they still have independent EnumValueDescriptors.
+   */
+  public static final class EnumValueDescriptor extends GenericDescriptor
+      implements Internal.EnumLite {
+    /**
+     * Get the index of this descriptor within its parent.
+     * @see Descriptors.Descriptor#getIndex()
+     */
+    public int getIndex() { return index; }
+
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public EnumValueDescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the value's unqualified name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /** Get the value's number. */
+    @Override
+    public int getNumber() {
+      return proto.getNumber();
+    }
+
+    @Override
+    public String toString() { return proto.getName(); }
+
+    /**
+     * Get the value's fully-qualified name.
+     * @see Descriptors.Descriptor#getFullName()
+     */
+    @Override
+    public String getFullName() {
+      return fullName;
+    }
+
+    /** Get the {@link FileDescriptor} containing this descriptor. */
+    @Override
+    public FileDescriptor getFile() {
+      return file;
+    }
+
+    /** Get the value's enum type. */
+    public EnumDescriptor getType() { return type; }
+
+    /**
+     * Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}.
+     */
+    public EnumValueOptions getOptions() { return proto.getOptions(); }
+
+    private final int index;
+    private EnumValueDescriptorProto proto;
+    private final String fullName;
+    private final FileDescriptor file;
+    private final EnumDescriptor type;
+
+    private EnumValueDescriptor(final EnumValueDescriptorProto proto,
+                                final FileDescriptor file,
+                                final EnumDescriptor parent,
+                                final int index)
+                         throws DescriptorValidationException {
+      this.index = index;
+      this.proto = proto;
+      this.file = file;
+      type = parent;
+
+      fullName = parent.getFullName() + '.' + proto.getName();
+
+      file.pool.addSymbol(this);
+      file.pool.addEnumValueByNumber(this);
+    }
+
+    private Integer number;
+    // Create an unknown enum value.
+    private EnumValueDescriptor(
+        final FileDescriptor file,
+        final EnumDescriptor parent,
+        final Integer number) {
+      String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number;
+      EnumValueDescriptorProto proto = EnumValueDescriptorProto
+          .newBuilder().setName(name).setNumber(number).build();
+      this.index = -1;
+      this.proto = proto;
+      this.file = file;
+      this.type = parent;
+      this.fullName = parent.getFullName() + '.' + proto.getName();
+      this.number = number;
+
+      // Don't add this descriptor into pool.
+    }
+
+    /** See {@link FileDescriptor#setProto}. */
+    private void setProto(final EnumValueDescriptorProto proto) {
+      this.proto = proto;
+    }
+  }
+
+  // =================================================================
+
+  /** Describes a service type. */
+  public static final class ServiceDescriptor extends GenericDescriptor {
+    /**
+     * Get the index of this descriptor within its parent.
+     * * @see Descriptors.Descriptor#getIndex()
+     */
+    public int getIndex() { return index; }
+
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public ServiceDescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the type's unqualified name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /**
+     * Get the type's fully-qualified name.
+     * @see Descriptors.Descriptor#getFullName()
+     */
+    @Override
+    public String getFullName() {
+      return fullName;
+    }
+
+    /** Get the {@link FileDescriptor} containing this descriptor. */
+    @Override
+    public FileDescriptor getFile() {
+      return file;
+    }
+
+    /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
+    public ServiceOptions getOptions() { return proto.getOptions(); }
+
+    /** Get a list of methods for this service. */
+    public List<MethodDescriptor> getMethods() {
+      return Collections.unmodifiableList(Arrays.asList(methods));
+    }
+
+    /**
+     * Find a method by name.
+     * @param name The unqualified name of the method (e.g. "Foo").
+     * @return the method's descriptor, or {@code null} if not found.
+     */
+    public MethodDescriptor findMethodByName(final String name) {
+      final GenericDescriptor result =
+          file.pool.findSymbol(fullName + '.' + name);
+      if (result != null && result instanceof MethodDescriptor) {
+        return (MethodDescriptor)result;
+      } else {
+        return null;
+      }
+    }
+
+    private final int index;
+    private ServiceDescriptorProto proto;
+    private final String fullName;
+    private final FileDescriptor file;
+    private MethodDescriptor[] methods;
+
+    private ServiceDescriptor(final ServiceDescriptorProto proto,
+                              final FileDescriptor file,
+                              final int index)
+                       throws DescriptorValidationException {
+      this.index = index;
+      this.proto = proto;
+      fullName = computeFullName(file, null, proto.getName());
+      this.file = file;
+
+      methods = new MethodDescriptor[proto.getMethodCount()];
+      for (int i = 0; i < proto.getMethodCount(); i++) {
+        methods[i] = new MethodDescriptor(
+          proto.getMethod(i), file, this, i);
+      }
+
+      file.pool.addSymbol(this);
+    }
+
+    private void crossLink() throws DescriptorValidationException {
+      for (final MethodDescriptor method : methods) {
+        method.crossLink();
+      }
+    }
+
+    /** See {@link FileDescriptor#setProto}. */
+    private void setProto(final ServiceDescriptorProto proto) {
+      this.proto = proto;
+
+      for (int i = 0; i < methods.length; i++) {
+        methods[i].setProto(proto.getMethod(i));
+      }
+    }
+  }
+
+  // =================================================================
+
+  /**
+   * Describes one method within a service type.
+   */
+  public static final class MethodDescriptor extends GenericDescriptor {
+    /**
+     * Get the index of this descriptor within its parent.
+     * * @see Descriptors.Descriptor#getIndex()
+     */
+    public int getIndex() { return index; }
+
+    /** Convert the descriptor to its protocol message representation. */
+    @Override
+    public MethodDescriptorProto toProto() {
+      return proto;
+    }
+
+    /** Get the method's unqualified name. */
+    @Override
+    public String getName() {
+      return proto.getName();
+    }
+
+    /**
+     * Get the method's fully-qualified name.
+     * @see Descriptors.Descriptor#getFullName()
+     */
+    @Override
+    public String getFullName() {
+      return fullName;
+    }
+
+    /** Get the {@link FileDescriptor} containing this descriptor. */
+    @Override
+    public FileDescriptor getFile() {
+      return file;
+    }
+
+    /** Get the method's service type. */
+    public ServiceDescriptor getService() { return service; }
+
+    /** Get the method's input type. */
+    public Descriptor getInputType() { return inputType; }
+
+    /** Get the method's output type. */
+    public Descriptor getOutputType() { return outputType; }
+
+    /**
+     * Get the {@code MethodOptions}, defined in {@code descriptor.proto}.
+     */
+    public MethodOptions getOptions() { return proto.getOptions(); }
+
+    private final int index;
+    private MethodDescriptorProto proto;
+    private final String fullName;
+    private final FileDescriptor file;
+    private final ServiceDescriptor service;
+
+    // Initialized during cross-linking.
+    private Descriptor inputType;
+    private Descriptor outputType;
+
+    private MethodDescriptor(final MethodDescriptorProto proto,
+                             final FileDescriptor file,
+                             final ServiceDescriptor parent,
+                             final int index)
+                      throws DescriptorValidationException {
+      this.index = index;
+      this.proto = proto;
+      this.file = file;
+      service = parent;
+
+      fullName = parent.getFullName() + '.' + proto.getName();
+
+      file.pool.addSymbol(this);
+    }
+
+    private void crossLink() throws DescriptorValidationException {
+      final GenericDescriptor input =
+        file.pool.lookupSymbol(proto.getInputType(), this,
+            DescriptorPool.SearchFilter.TYPES_ONLY);
+      if (!(input instanceof Descriptor)) {
+        throw new DescriptorValidationException(this,
+            '\"' + proto.getInputType() + "\" is not a message type.");
+      }
+      inputType = (Descriptor)input;
+
+      final GenericDescriptor output =
+        file.pool.lookupSymbol(proto.getOutputType(), this,
+            DescriptorPool.SearchFilter.TYPES_ONLY);
+      if (!(output instanceof Descriptor)) {
+        throw new DescriptorValidationException(this,
+            '\"' + proto.getOutputType() + "\" is not a message type.");
+      }
+      outputType = (Descriptor)output;
+    }
+
+    /** See {@link FileDescriptor#setProto}. */
+    private void setProto(final MethodDescriptorProto proto) {
+      this.proto = proto;
+    }
+  }
+
+  // =================================================================
+
+  private static String computeFullName(final FileDescriptor file,
+                                        final Descriptor parent,
+                                        final String name) {
+    if (parent != null) {
+      return parent.getFullName() + '.' + name;
+    } else if (file.getPackage().length() > 0) {
+      return file.getPackage() + '.' + name;
+    } else {
+      return name;
+    }
+  }
+
+  // =================================================================
+
+  /**
+   * All descriptors implement this to make it easier to implement tools like
+   * {@code DescriptorPool}.<p>
+   *
+   * This class is public so that the methods it exposes can be called from
+   * outside of this package. However, it should only be subclassed from
+   * nested classes of Descriptors.
+   */
+  public abstract static class GenericDescriptor {
+    public abstract Message toProto();
+    public abstract String getName();
+    public abstract String getFullName();
+    public abstract FileDescriptor getFile();
+  }
+
+  /**
+   * Thrown when building descriptors fails because the source DescriptorProtos
+   * are not valid.
+   */
+  public static class DescriptorValidationException extends Exception {
+    private static final long serialVersionUID = 5750205775490483148L;
+
+    /** Gets the full name of the descriptor where the error occurred. */
+    public String getProblemSymbolName() { return name; }
+
+    /**
+     * Gets the protocol message representation of the invalid descriptor.
+     */
+    public Message getProblemProto() { return proto; }
+
+    /**
+     * Gets a human-readable description of the error.
+     */
+    public String getDescription() { return description; }
+
+    private final String name;
+    private final Message proto;
+    private final String description;
+
+    private DescriptorValidationException(
+        final GenericDescriptor problemDescriptor,
+        final String description) {
+      super(problemDescriptor.getFullName() + ": " + description);
+
+      // Note that problemDescriptor may be partially uninitialized, so we
+      // don't want to expose it directly to the user.  So, we only provide
+      // the name and the original proto.
+      name = problemDescriptor.getFullName();
+      proto = problemDescriptor.toProto();
+      this.description = description;
+    }
+
+    private DescriptorValidationException(
+        final GenericDescriptor problemDescriptor,
+        final String description,
+        final Throwable cause) {
+      this(problemDescriptor, description);
+      initCause(cause);
+    }
+
+    private DescriptorValidationException(
+        final FileDescriptor problemDescriptor,
+        final String description) {
+      super(problemDescriptor.getName() + ": " + description);
+
+      // Note that problemDescriptor may be partially uninitialized, so we
+      // don't want to expose it directly to the user.  So, we only provide
+      // the name and the original proto.
+      name = problemDescriptor.getName();
+      proto = problemDescriptor.toProto();
+      this.description = description;
+    }
+  }
+
+  // =================================================================
+
+  /**
+   * A private helper class which contains lookup tables containing all the
+   * descriptors defined in a particular file.
+   */
+  private static final class DescriptorPool {
+
+    /** Defines what subclass of descriptors to search in the descriptor pool.
+     */
+    enum SearchFilter {
+      TYPES_ONLY, AGGREGATES_ONLY, ALL_SYMBOLS
+    }
+
+    DescriptorPool(final FileDescriptor[] dependencies,
+        boolean allowUnknownDependencies) {
+      this.dependencies = new HashSet<FileDescriptor>();
+      this.allowUnknownDependencies = allowUnknownDependencies;
+
+      for (int i = 0; i < dependencies.length; i++) {
+        this.dependencies.add(dependencies[i]);
+        importPublicDependencies(dependencies[i]);
+      }
+
+      for (final FileDescriptor dependency : this.dependencies) {
+        try {
+          addPackage(dependency.getPackage(), dependency);
+        } catch (DescriptorValidationException e) {
+          // Can't happen, because addPackage() only fails when the name
+          // conflicts with a non-package, but we have not yet added any
+          // non-packages at this point.
+          throw new AssertionError(e);
+        }
+      }
+    }
+
+    /** Find and put public dependencies of the file into dependencies set.*/
+    private void importPublicDependencies(final FileDescriptor file) {
+      for (FileDescriptor dependency : file.getPublicDependencies()) {
+        if (dependencies.add(dependency)) {
+          importPublicDependencies(dependency);
+        }
+      }
+    }
+
+    private final Set<FileDescriptor> dependencies;
+    private boolean allowUnknownDependencies;
+
+    private final Map<String, GenericDescriptor> descriptorsByName =
+      new HashMap<String, GenericDescriptor>();
+    private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
+      new HashMap<DescriptorIntPair, FieldDescriptor>();
+    private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber
+        = new HashMap<DescriptorIntPair, EnumValueDescriptor>();
+
+    /** Find a generic descriptor by fully-qualified name. */
+    GenericDescriptor findSymbol(final String fullName) {
+      return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
+    }
+
+    /** Find a descriptor by fully-qualified name and given option to only
+     * search valid field type descriptors.
+     */
+    GenericDescriptor findSymbol(final String fullName,
+                                 final SearchFilter filter) {
+      GenericDescriptor result = descriptorsByName.get(fullName);
+      if (result != null) {
+        if ((filter==SearchFilter.ALL_SYMBOLS) ||
+            ((filter==SearchFilter.TYPES_ONLY) && isType(result)) ||
+            ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
+          return result;
+        }
+      }
+
+      for (final FileDescriptor dependency : dependencies) {
+        result = dependency.pool.descriptorsByName.get(fullName);
+        if (result != null) {
+          if ((filter==SearchFilter.ALL_SYMBOLS) ||
+              ((filter==SearchFilter.TYPES_ONLY) && isType(result)) ||
+              ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
+            return result;
+          }
+        }
+      }
+
+      return null;
+    }
+
+    /** Checks if the descriptor is a valid type for a message field. */
+    boolean isType(GenericDescriptor descriptor) {
+      return (descriptor instanceof Descriptor) ||
+        (descriptor instanceof EnumDescriptor);
+    }
+
+    /** Checks if the descriptor is a valid namespace type. */
+    boolean isAggregate(GenericDescriptor descriptor) {
+      return (descriptor instanceof Descriptor) ||
+        (descriptor instanceof EnumDescriptor) ||
+        (descriptor instanceof PackageDescriptor) ||
+        (descriptor instanceof ServiceDescriptor);
+    }
+
+    /**
+     * Look up a type descriptor by name, relative to some other descriptor.
+     * The name may be fully-qualified (with a leading '.'),
+     * partially-qualified, or unqualified.  C++-like name lookup semantics
+     * are used to search for the matching descriptor.
+     */
+    GenericDescriptor lookupSymbol(final String name,
+                                   final GenericDescriptor relativeTo,
+                                   final DescriptorPool.SearchFilter filter)
+                            throws DescriptorValidationException {
+      // TODO(kenton):  This could be optimized in a number of ways.
+
+      GenericDescriptor result;
+      String fullname;
+      if (name.startsWith(".")) {
+        // Fully-qualified name.
+        fullname = name.substring(1);
+        result = findSymbol(fullname, filter);
+      } else {
+        // If "name" is a compound identifier, we want to search for the
+        // first component of it, then search within it for the rest.
+        // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
+        // defined in multiple parent scopes, we only want to find "Bar.baz" in
+        // the innermost one.  E.g., the following should produce an error:
+        //   message Bar { message Baz {} }
+        //   message Foo {
+        //     message Bar {
+        //     }
+        //     optional Bar.Baz baz = 1;
+        //   }
+        // So, we look for just "Foo" first, then look for "Bar.baz" within it
+        // if found.
+        final int firstPartLength = name.indexOf('.');
+        final String firstPart;
+        if (firstPartLength == -1) {
+          firstPart = name;
+        } else {
+          firstPart = name.substring(0, firstPartLength);
+        }
+
+        // We will search each parent scope of "relativeTo" looking for the
+        // symbol.
+        final StringBuilder scopeToTry =
+            new StringBuilder(relativeTo.getFullName());
+
+        while (true) {
+          // Chop off the last component of the scope.
+          final int dotpos = scopeToTry.lastIndexOf(".");
+          if (dotpos == -1) {
+            fullname = name;
+            result = findSymbol(name, filter);
+            break;
+          } else {
+            scopeToTry.setLength(dotpos + 1);
+
+            // Append firstPart and try to find
+            scopeToTry.append(firstPart);
+            result = findSymbol(scopeToTry.toString(),
+                DescriptorPool.SearchFilter.AGGREGATES_ONLY);
+
+            if (result != null) {
+              if (firstPartLength != -1) {
+                // We only found the first part of the symbol.  Now look for
+                // the whole thing.  If this fails, we *don't* want to keep
+                // searching parent scopes.
+                scopeToTry.setLength(dotpos + 1);
+                scopeToTry.append(name);
+                result = findSymbol(scopeToTry.toString(), filter);
+              }
+              fullname = scopeToTry.toString();
+              break;
+            }
+
+            // Not found.  Remove the name so we can try again.
+            scopeToTry.setLength(dotpos);
+          }
+        }
+      }
+
+      if (result == null) {
+        if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) {
+          logger.warning("The descriptor for message type \"" + name +
+              "\" can not be found and a placeholder is created for it");
+          // We create a dummy message descriptor here regardless of the
+          // expected type. If the type should be message, this dummy
+          // descriptor will work well and if the type should be enum, a
+          // DescriptorValidationException will be thrown latter. In either
+          // case, the code works as expected: we allow unknown message types
+          // but not unknwon enum types.
+          result = new Descriptor(fullname);
+          // Add the placeholder file as a dependency so we can find the
+          // placeholder symbol when resolving other references.
+          this.dependencies.add(result.getFile());
+          return result;
+        } else {
+          throw new DescriptorValidationException(relativeTo,
+              '\"' + name + "\" is not defined.");
+        }
+      } else {
+        return result;
+      }
+    }
+
+    /**
+     * Adds a symbol to the symbol table.  If a symbol with the same name
+     * already exists, throws an error.
+     */
+    void addSymbol(final GenericDescriptor descriptor)
+            throws DescriptorValidationException {
+      validateSymbolName(descriptor);
+
+      final String fullName = descriptor.getFullName();
+      final int dotpos = fullName.lastIndexOf('.');
+
+      final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
+      if (old != null) {
+        descriptorsByName.put(fullName, old);
+
+        if (descriptor.getFile() == old.getFile()) {
+          if (dotpos == -1) {
+            throw new DescriptorValidationException(descriptor,
+                '\"' + fullName + "\" is already defined.");
+          } else {
+            throw new DescriptorValidationException(descriptor,
+                '\"' + fullName.substring(dotpos + 1) +
+              "\" is already defined in \"" +
+              fullName.substring(0, dotpos) + "\".");
+          }
+        } else {
+          throw new DescriptorValidationException(descriptor,
+              '\"' + fullName + "\" is already defined in file \"" +
+            old.getFile().getName() + "\".");
+        }
+      }
+    }
+
+    /**
+     * Represents a package in the symbol table.  We use PackageDescriptors
+     * just as placeholders so that someone cannot define, say, a message type
+     * that has the same name as an existing package.
+     */
+    private static final class PackageDescriptor extends GenericDescriptor {
+      @Override
+      public Message toProto() {
+        return file.toProto();
+      }
+      @Override
+      public String getName() {
+        return name;
+      }
+      @Override
+      public String getFullName() {
+        return fullName;
+      }
+      @Override
+      public FileDescriptor getFile() {
+        return file;
+      }
+
+      PackageDescriptor(final String name, final String fullName,
+                        final FileDescriptor file) {
+        this.file = file;
+        this.fullName = fullName;
+        this.name = name;
+      }
+
+      private final String name;
+      private final String fullName;
+      private final FileDescriptor file;
+    }
+
+    /**
+     * Adds a package to the symbol tables.  If a package by the same name
+     * already exists, that is fine, but if some other kind of symbol exists
+     * under the same name, an exception is thrown.  If the package has
+     * multiple components, this also adds the parent package(s).
+     */
+    void addPackage(final String fullName, final FileDescriptor file)
+             throws DescriptorValidationException {
+      final int dotpos = fullName.lastIndexOf('.');
+      final String name;
+      if (dotpos == -1) {
+        name = fullName;
+      } else {
+        addPackage(fullName.substring(0, dotpos), file);
+        name = fullName.substring(dotpos + 1);
+      }
+
+      final GenericDescriptor old =
+        descriptorsByName.put(fullName,
+          new PackageDescriptor(name, fullName, file));
+      if (old != null) {
+        descriptorsByName.put(fullName, old);
+        if (!(old instanceof PackageDescriptor)) {
+          throw new DescriptorValidationException(file,
+              '\"' + name + "\" is already defined (as something other than a "
+              + "package) in file \"" + old.getFile().getName() + "\".");
+        }
+      }
+    }
+
+    /** A (GenericDescriptor, int) pair, used as a map key. */
+    private static final class DescriptorIntPair {
+      private final GenericDescriptor descriptor;
+      private final int number;
+
+      DescriptorIntPair(final GenericDescriptor descriptor, final int number) {
+        this.descriptor = descriptor;
+        this.number = number;
+      }
+
+      @Override
+      public int hashCode() {
+        return descriptor.hashCode() * ((1 << 16) - 1) + number;
+      }
+      @Override
+      public boolean equals(final Object obj) {
+        if (!(obj instanceof DescriptorIntPair)) {
+          return false;
+        }
+        final DescriptorIntPair other = (DescriptorIntPair)obj;
+        return descriptor == other.descriptor && number == other.number;
+      }
+    }
+
+    /**
+     * Adds a field to the fieldsByNumber table.  Throws an exception if a
+     * field with the same containing type and number already exists.
+     */
+    void addFieldByNumber(final FieldDescriptor

<TRUNCATED>

Mime
View raw message