12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Protocol Buffers - Google's data interchange format
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2008 Google Inc.  All rights reserved.
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// http://code.google.com/p/protobuf/
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Redistribution and use in source and binary forms, with or without
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// modification, are permitted provided that the following conditions are
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// met:
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//     * Redistributions of source code must retain the above copyright
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// notice, this list of conditions and the following disclaimer.
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//     * Redistributions in binary form must reproduce the above
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// copyright notice, this list of conditions and the following disclaimer
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// in the documentation and/or other materials provided with the
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// distribution.
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//     * Neither the name of Google Inc. nor the names of its
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// contributors may be used to endorse or promote products derived from
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// this software without specific prior written permission.
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)package com.google.protobuf;
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import com.google.protobuf.DescriptorProtos.*;
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.Arrays;
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.Collections;
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.HashMap;
383d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdochimport java.util.HashSet;
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.List;
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.Map;
413d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdochimport java.util.Set;
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.io.UnsupportedEncodingException;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Contains a collection of classes which describe protocol message types.
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Every message type has a {@link Descriptor}, which lists all
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * its fields and other information about a type.  You can get a message
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * type's descriptor by calling {@code MessageType.getDescriptor()}, or
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * (given a message object of the type) {@code message.getDescriptorForType()}.
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Furthermore, each message is associated with a {@link FileDescriptor} for
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * a relevant {@code .proto} file. You can obtain it by calling
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * for all the messages defined in that file, and file descriptors for all the
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * imported {@code .proto} files.
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Descriptors are built from DescriptorProtos, as defined in
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * {@code google/protobuf/descriptor.proto}.
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @author kenton@google.com Kenton Varda
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public final class Descriptors {
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /**
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * Describes a {@code .proto} file, including everything defined within.
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * That includes, in particular, descriptors for all the messages and
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * file descriptors for all other imported {@code .proto} files
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * (dependencies).
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   */
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class FileDescriptor {
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptorProto toProto() { return proto; }
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the file name. */
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the proto package name.  This is the package name given by the
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code package} statement in the {@code .proto} file, which differs
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * from the Java package.
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getPackage() { return proto.getPackage(); }
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileOptions getOptions() { return proto.getOptions(); }
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of top-level message types declared in this file. */
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<Descriptor> getMessageTypes() {
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(messageTypes));
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of top-level enum types declared in this file. */
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<EnumDescriptor> getEnumTypes() {
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(enumTypes));
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of top-level services declared in this file. */
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<ServiceDescriptor> getServices() {
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(services));
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of top-level extensions declared in this file. */
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<FieldDescriptor> getExtensions() {
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(extensions));
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of this file's dependencies (imports). */
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<FileDescriptor> getDependencies() {
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(dependencies));
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1113d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    /** Get a list of this file's public dependencies (public imports). */
1123d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    public List<FileDescriptor> getPublicDependencies() {
1133d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      return Collections.unmodifiableList(Arrays.asList(publicDependencies));
1143d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    }
1153d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find a message type in the file by name.  Does not find nested types.
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified type name to look for.
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The message type's descriptor, or {@code null} if not found.
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor findMessageTypeByName(String name) {
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Don't allow looking up nested types.  This will make optimization
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // easier later.
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (name.indexOf('.') != -1) {
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getPackage().length() > 0) {
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        name = getPackage() + '.' + name;
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result = pool.findSymbol(name);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof Descriptor &&
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          result.getFile() == this) {
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (Descriptor)result;
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find an enum type in the file by name.  Does not find nested types.
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified type name to look for.
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The enum type's descriptor, or {@code null} if not found.
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumDescriptor findEnumTypeByName(String name) {
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Don't allow looking up nested types.  This will make optimization
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // easier later.
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (name.indexOf('.') != -1) {
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getPackage().length() > 0) {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        name = getPackage() + '.' + name;
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result = pool.findSymbol(name);
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof EnumDescriptor &&
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          result.getFile() == this) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (EnumDescriptor)result;
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find a service type in the file by name.
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified type name to look for.
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The service type's descriptor, or {@code null} if not found.
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public ServiceDescriptor findServiceByName(String name) {
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Don't allow looking up nested types.  This will make optimization
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // easier later.
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (name.indexOf('.') != -1) {
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getPackage().length() > 0) {
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        name = getPackage() + '.' + name;
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result = pool.findSymbol(name);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof ServiceDescriptor &&
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          result.getFile() == this) {
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (ServiceDescriptor)result;
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find an extension in the file by name.  Does not find extensions nested
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * inside message types.
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified extension name to look for.
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The extension's descriptor, or {@code null} if not found.
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FieldDescriptor findExtensionByName(String name) {
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (name.indexOf('.') != -1) {
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getPackage().length() > 0) {
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        name = getPackage() + '.' + name;
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result = pool.findSymbol(name);
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof FieldDescriptor &&
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          result.getFile() == this) {
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (FieldDescriptor)result;
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Construct a {@code FileDescriptor}.
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param proto The protocol message form of the FileDescriptor.
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param dependencies {@code FileDescriptor}s corresponding to all of
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *                     the file's dependencies, in the exact order listed
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *                     in {@code proto}.
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @throws DescriptorValidationException {@code proto} is not a valid
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *           descriptor.  This can occur for a number of reasons, e.g.
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *           because a field has an undefined type or because two messages
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *           were defined with the same name.
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public static FileDescriptor buildFrom(final FileDescriptorProto proto,
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                           final FileDescriptor[] dependencies)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    throws DescriptorValidationException {
2263d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      // Building descriptors involves two steps:  translating and linking.
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // In the translation step (implemented by FileDescriptor's
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // constructor), we build an object tree mirroring the
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // FileDescriptorProto's tree and put all of the descriptors into the
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // DescriptorPool's lookup tables.  In the linking step, we look up all
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // type references in the DescriptorPool, so that, for example, a
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // FieldDescriptor for an embedded message contains a pointer directly
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // to the Descriptor for that message's type.  We also detect undefined
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // types in the linking step.
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final DescriptorPool pool = new DescriptorPool(dependencies);
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final FileDescriptor result =
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          new FileDescriptor(proto, dependencies, pool);
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (dependencies.length != proto.getDependencyCount()) {
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(result,
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Dependencies passed to FileDescriptor.buildFrom() don't match " +
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "those listed in the FileDescriptorProto.");
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getDependencyCount(); i++) {
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!dependencies[i].getName().equals(proto.getDependency(i))) {
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(result,
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "Dependencies passed to FileDescriptor.buildFrom() don't match " +
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "those listed in the FileDescriptorProto.");
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      result.crossLink();
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return result;
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * This method is to be called by generated code only.  It is equivalent
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * to {@code buildFrom} except that the {@code FileDescriptorProto} is
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * encoded in protocol buffer wire format.
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public static void internalBuildGeneratedFileFrom(
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String[] descriptorDataParts,
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final FileDescriptor[] dependencies,
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final InternalDescriptorAssigner descriptorAssigner) {
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Hack:  We can't embed a raw byte array inside generated Java code
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   (at least, not efficiently), but we can embed Strings.  So, the
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   protocol compiler embeds the FileDescriptorProto as a giant
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   string literal which is passed to this function to construct the
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   file's FileDescriptor.  The string literal contains only 8-bit
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   characters, each one representing a byte of the FileDescriptorProto's
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   serialized form.  So, if we convert it to bytes in ISO-8859-1, we
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      //   should get the original bytes that we want.
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // descriptorData may contain multiple strings in order to get around the
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Java 64k string literal limit.
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      StringBuilder descriptorData = new StringBuilder();
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (String part : descriptorDataParts) {
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        descriptorData.append(part);
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final byte[] descriptorBytes;
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      try {
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        descriptorBytes = descriptorData.toString().getBytes("ISO-8859-1");
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } catch (UnsupportedEncodingException e) {
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new RuntimeException(
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Standard encoding ISO-8859-1 not supported by JVM.", e);
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FileDescriptorProto proto;
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      try {
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        proto = FileDescriptorProto.parseFrom(descriptorBytes);
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } catch (InvalidProtocolBufferException e) {
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new IllegalArgumentException(
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Failed to parse protocol buffer descriptor for generated code.", e);
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final FileDescriptor result;
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      try {
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result = buildFrom(proto, dependencies);
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } catch (DescriptorValidationException e) {
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new IllegalArgumentException(
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final ExtensionRegistry registry =
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          descriptorAssigner.assignDescriptors(result);
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (registry != null) {
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // We must re-parse the proto using the registry.
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        try {
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto = FileDescriptorProto.parseFrom(descriptorBytes, registry);
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } catch (InvalidProtocolBufferException e) {
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new IllegalArgumentException(
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "Failed to parse protocol buffer descriptor for generated code.",
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            e);
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result.setProto(proto);
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * This class should be used by generated code only.  When calling
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@link FileDescriptor#internalBuildGeneratedFileFrom}, the caller
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * provides a callback implementing this interface.  The callback is called
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * after the FileDescriptor has been constructed, in order to assign all
3273d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * the global variables defined in the generated code which point at parts
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * of the FileDescriptor.  The callback returns an ExtensionRegistry which
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * contains any extensions which might be used in the descriptor -- that
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * is, extensions of the various "Options" messages defined in
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * descriptor.proto.  The callback may also return null to indicate that
3323d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * no extensions are used in the descriptor.
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public interface InternalDescriptorAssigner {
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ExtensionRegistry assignDescriptors(FileDescriptor root);
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private FileDescriptorProto proto;
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Descriptor[] messageTypes;
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final EnumDescriptor[] enumTypes;
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final ServiceDescriptor[] services;
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FieldDescriptor[] extensions;
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor[] dependencies;
3443d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    private final FileDescriptor[] publicDependencies;
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final DescriptorPool pool;
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private FileDescriptor(final FileDescriptorProto proto,
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           final FileDescriptor[] dependencies,
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           final DescriptorPool pool)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    throws DescriptorValidationException {
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.pool = pool;
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.dependencies = dependencies.clone();
3543d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      this.publicDependencies =
3553d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          new FileDescriptor[proto.getPublicDependencyCount()];
3563d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
3573d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        int index = proto.getPublicDependency(i);
3583d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        if (index < 0 || index >= this.dependencies.length) {
3593d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          throw new DescriptorValidationException(this,
3603d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch              "Invalid public dependency index.");
3613d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        }
3623d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        this.publicDependencies[i] =
3633d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            this.dependencies[proto.getPublicDependency(i)];
3643d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      }
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      pool.addPackage(getPackage(), this);
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      messageTypes = new Descriptor[proto.getMessageTypeCount()];
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getMessageTypeCount(); i++) {
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        messageTypes[i] =
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          new Descriptor(proto.getMessageType(i), this, null, i);
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getEnumTypeCount(); i++) {
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i);
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      services = new ServiceDescriptor[proto.getServiceCount()];
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getServiceCount(); i++) {
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        services[i] = new ServiceDescriptor(proto.getService(i), this, i);
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions = new FieldDescriptor[proto.getExtensionCount()];
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getExtensionCount(); i++) {
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extensions[i] = new FieldDescriptor(
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getExtension(i), this, null, i, true);
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Look up and cross-link all field types, etc. */
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void crossLink() throws DescriptorValidationException {
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final Descriptor messageType : messageTypes) {
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        messageType.crossLink();
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final ServiceDescriptor service : services) {
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        service.crossLink();
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final FieldDescriptor extension : extensions) {
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extension.crossLink();
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Replace our {@link FileDescriptorProto} with the given one, which is
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * identical except that it might contain extensions that weren't present
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * in the original.  This method is needed for bootstrapping when a file
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * defines custom options.  The options may be defined in the file itself,
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * so we can't actually parse them until we've constructed the descriptors,
4123d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * but to construct the descriptors we have to have parsed the descriptor
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * protos.  So, we have to parse the descriptor protos a second time after
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * constructing the descriptors.
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final FileDescriptorProto proto) {
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < messageTypes.length; i++) {
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        messageTypes[i].setProto(proto.getMessageType(i));
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < enumTypes.length; i++) {
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        enumTypes[i].setProto(proto.getEnumType(i));
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < services.length; i++) {
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        services[i].setProto(proto.getService(i));
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < extensions.length; i++) {
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extensions[i].setProto(proto.getExtension(i));
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /** Describes a message type. */
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class Descriptor implements GenericDescriptor {
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the index of this descriptor within its parent.  In other words,
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * given a {@link FileDescriptor} {@code file}, the following is true:
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * <pre>
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   for all i in [0, file.getMessageTypeCount()):
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *     file.getMessageType(i).getIndex() == i
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * </pre>
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Similarly, for a {@link Descriptor} {@code messageType}:
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * <pre>
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   for all i in [0, messageType.getNestedTypeCount()):
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *     messageType.getNestedType(i).getIndex() == i
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * </pre>
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getIndex() { return index; }
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public DescriptorProto toProto() { return proto; }
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the type's unqualified name. */
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the type's fully-qualified name, within the proto language's
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * namespace.  This differs from the Java name.  For example, given this
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code .proto}:
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * <pre>
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   package foo.bar;
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   option java_package = "com.example.protos"
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   message Baz {}
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * </pre>
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code Baz}'s full name is "foo.bar.Baz".
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getFullName() { return fullName; }
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@link FileDescriptor} containing this descriptor. */
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptor getFile() { return file; }
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** If this is a nested type, get the outer descriptor, otherwise null. */
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getContainingType() { return containingType; }
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public MessageOptions getOptions() { return proto.getOptions(); }
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of this message type's fields. */
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<FieldDescriptor> getFields() {
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(fields));
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of this message type's extensions. */
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<FieldDescriptor> getExtensions() {
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(extensions));
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of message types nested within this one. */
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<Descriptor> getNestedTypes() {
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(nestedTypes));
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of enum types nested within this one. */
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<EnumDescriptor> getEnumTypes() {
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(enumTypes));
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Determines if the given field number is an extension. */
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isExtensionNumber(final int number) {
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final DescriptorProto.ExtensionRange range :
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getExtensionRangeList()) {
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (range.getStart() <= number && number < range.getEnd()) {
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return true;
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Finds a field by name.
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified name of the field (e.g. "foo").
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The field's descriptor, or {@code null} if not found.
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FieldDescriptor findFieldByName(final String name) {
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result =
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          file.pool.findSymbol(fullName + '.' + name);
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof FieldDescriptor) {
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (FieldDescriptor)result;
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Finds a field by field number.
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param number The field number within this message type.
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The field's descriptor, or {@code null} if not found.
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FieldDescriptor findFieldByNumber(final int number) {
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return file.pool.fieldsByNumber.get(
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new DescriptorPool.DescriptorIntPair(this, number));
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Finds a nested message type by name.
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified name of the nested type (e.g. "Foo").
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The types's descriptor, or {@code null} if not found.
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor findNestedTypeByName(final String name) {
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result =
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          file.pool.findSymbol(fullName + '.' + name);
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof Descriptor) {
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (Descriptor)result;
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Finds a nested enum type by name.
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified name of the nested type (e.g. "Foo").
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return The types's descriptor, or {@code null} if not found.
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumDescriptor findEnumTypeByName(final String name) {
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result =
5622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          file.pool.findSymbol(fullName + '.' + name);
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof EnumDescriptor) {
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (EnumDescriptor)result;
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final int index;
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private DescriptorProto proto;
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String fullName;
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor file;
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Descriptor containingType;
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Descriptor[] nestedTypes;
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final EnumDescriptor[] enumTypes;
5772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FieldDescriptor[] fields;
5782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FieldDescriptor[] extensions;
5792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Descriptor(final DescriptorProto proto,
5812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       final FileDescriptor file,
5822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       final Descriptor parent,
5832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       final int index)
5842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                throws DescriptorValidationException {
5852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.index = index;
5862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
5872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fullName = computeFullName(file, parent, proto.getName());
5882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.file = file;
5892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      containingType = parent;
5902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      nestedTypes = new Descriptor[proto.getNestedTypeCount()];
5922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getNestedTypeCount(); i++) {
5932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        nestedTypes[i] = new Descriptor(
5942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getNestedType(i), file, this, i);
5952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getEnumTypeCount(); i++) {
5992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        enumTypes[i] = new EnumDescriptor(
6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getEnumType(i), file, this, i);
6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fields = new FieldDescriptor[proto.getFieldCount()];
6042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getFieldCount(); i++) {
6052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        fields[i] = new FieldDescriptor(
6062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getField(i), file, this, i, false);
6072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions = new FieldDescriptor[proto.getExtensionCount()];
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getExtensionCount(); i++) {
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extensions[i] = new FieldDescriptor(
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getExtension(i), file, this, i, true);
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addSymbol(this);
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Look up and cross-link all field types, etc. */
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void crossLink() throws DescriptorValidationException {
6202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final Descriptor nestedType : nestedTypes) {
6212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        nestedType.crossLink();
6222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final FieldDescriptor field : fields) {
6252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        field.crossLink();
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final FieldDescriptor extension : extensions) {
6292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extension.crossLink();
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** See {@link FileDescriptor#setProto}. */
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final DescriptorProto proto) {
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
6362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < nestedTypes.length; i++) {
6382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        nestedTypes[i].setProto(proto.getNestedType(i));
6392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < enumTypes.length; i++) {
6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        enumTypes[i].setProto(proto.getEnumType(i));
6432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < fields.length; i++) {
6462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        fields[i].setProto(proto.getField(i));
6472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < extensions.length; i++) {
6502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extensions[i].setProto(proto.getExtension(i));
6512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
6562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /** Describes a field of a message type. */
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class FieldDescriptor
6592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      implements GenericDescriptor, Comparable<FieldDescriptor>,
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 FieldSet.FieldDescriptorLite<FieldDescriptor> {
6612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
6622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the index of this descriptor within its parent.
6633d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getIndex()
6642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
6652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getIndex() { return index; }
6662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
6682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FieldDescriptorProto toProto() { return proto; }
6692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the field's unqualified name. */
6712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
6722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the field's number. */
6742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getNumber() { return proto.getNumber(); }
6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
6772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the field's fully-qualified name.
6783d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getFullName()
6792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
6802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getFullName() { return fullName; }
6812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
6832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the field's java type.  This is just for convenience.  Every
6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code FieldDescriptorProto.Type} maps to exactly one Java type.
6852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public JavaType getJavaType() { return type.getJavaType(); }
6872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** For internal use only. */
6892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public WireFormat.JavaType getLiteJavaType() {
6902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return getLiteType().getJavaType();
6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@code FileDescriptor} containing this descriptor. */
6942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptor getFile() { return file; }
6952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the field's declared type. */
6972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Type getType() { return type; }
6982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** For internal use only. */
7002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public WireFormat.FieldType getLiteType() {
7012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return table[type.ordinal()];
7022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // I'm pretty sure values() constructs a new array every time, since there
7042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // is nothing stopping the caller from mutating the array.  Therefore we
7052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // make a static copy here.
7062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static final WireFormat.FieldType[] table =
7072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        WireFormat.FieldType.values();
7082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Is this field declared required? */
7102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isRequired() {
7112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED;
7122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Is this field declared optional? */
7152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isOptional() {
7162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL;
7172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Is this field declared repeated? */
7202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isRepeated() {
7212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
7222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Does this field have the {@code [packed = true]} option? */
7252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isPacked() {
7262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return getOptions().getPacked();
7272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Can this field be packed? i.e. is it a repeated primitive field? */
7302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isPackable() {
7312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return isRepeated() && getLiteType().isPackable();
7322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Returns true if the field had an explicitly-defined default value. */
7352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean hasDefaultValue() { return proto.hasDefaultValue(); }
7362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
7382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Returns the field's default value.  Valid for all types except for
7392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * messages and groups.  For all other types, the object returned is of
7402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * the same class that would returned by Message.getField(this).
7412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
7422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Object getDefaultValue() {
7432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getJavaType() == JavaType.MESSAGE) {
7442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new UnsupportedOperationException(
7452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "FieldDescriptor.getDefaultValue() called on an embedded message " +
7462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "field.");
7472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
7482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return defaultValue;
7492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
7522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FieldOptions getOptions() { return proto.getOptions(); }
7532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Is this field an extension? */
7552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public boolean isExtension() { return proto.hasExtendee(); }
7562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
7582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the field's containing type. For extensions, this is the type being
7592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * extended, not the location where the extension was defined.  See
7602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@link #getExtensionScope()}.
7612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
7622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getContainingType() { return containingType; }
7632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
7652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * For extensions defined nested within message types, gets the outer
7662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * type.  Not valid for non-extension fields.  For example, consider
7672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * this {@code .proto} file:
7682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * <pre>
7692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   message Foo {
7702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *     extensions 1000 to max;
7712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   }
7722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   extend Foo {
7732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *     optional int32 baz = 1234;
7742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   }
7752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   message Bar {
7762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *     extend Foo {
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *       optional int32 qux = 4321;
7782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *     }
7792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *   }
7802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * </pre>
7812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}.
7822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * However, {@code baz}'s extension scope is {@code null} while
7832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code qux}'s extension scope is {@code Bar}.
7842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
7852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getExtensionScope() {
7862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!isExtension()) {
7872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new UnsupportedOperationException(
7882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "This field is not an extension.");
7892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
7902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return extensionScope;
7912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** For embedded message and group fields, gets the field's type. */
7942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getMessageType() {
7952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getJavaType() != JavaType.MESSAGE) {
7962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new UnsupportedOperationException(
7972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "This field is not of message type.");
7982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
7992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return messageType;
8002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** For enum fields, gets the field's type. */
8032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumDescriptor getEnumType() {
8042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getJavaType() != JavaType.ENUM) {
8052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new UnsupportedOperationException(
8062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "This field is not of enum type.");
8072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
8082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return enumType;
8092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
8122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Compare with another {@code FieldDescriptor}.  This orders fields in
8132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * "canonical" order, which simply means ascending order by field number.
8142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code other} must be a field of the same type -- i.e.
8152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * {@code getContainingType()} must return the same {@code Descriptor} for
8162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * both fields.
8172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *
8182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return negative, zero, or positive if {@code this} is less than,
8192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     *         equal to, or greater than {@code other}, respectively.
8202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
8212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int compareTo(final FieldDescriptor other) {
8222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (other.containingType != containingType) {
8232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new IllegalArgumentException(
8242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "FieldDescriptors can only be compared to other FieldDescriptors " +
8252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "for fields of the same message type.");
8262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
8272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return getNumber() - other.getNumber();
8282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final int index;
8312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private FieldDescriptorProto proto;
8332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String fullName;
8342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor file;
8352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Descriptor extensionScope;
8362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Possibly initialized during cross-linking.
8382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Type type;
8392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Descriptor containingType;
8402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Descriptor messageType;
8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private EnumDescriptor enumType;
8422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Object defaultValue;
8432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public enum Type {
8452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DOUBLE  (JavaType.DOUBLE     ),
8462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FLOAT   (JavaType.FLOAT      ),
8472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      INT64   (JavaType.LONG       ),
8482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      UINT64  (JavaType.LONG       ),
8492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      INT32   (JavaType.INT        ),
8502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FIXED64 (JavaType.LONG       ),
8512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FIXED32 (JavaType.INT        ),
8522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BOOL    (JavaType.BOOLEAN    ),
8532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      STRING  (JavaType.STRING     ),
8542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GROUP   (JavaType.MESSAGE    ),
8552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MESSAGE (JavaType.MESSAGE    ),
8562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BYTES   (JavaType.BYTE_STRING),
8572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      UINT32  (JavaType.INT        ),
8582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ENUM    (JavaType.ENUM       ),
8592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SFIXED32(JavaType.INT        ),
8602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SFIXED64(JavaType.LONG       ),
8612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SINT32  (JavaType.INT        ),
8622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SINT64  (JavaType.LONG       );
8632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Type(final JavaType javaType) {
8652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.javaType = javaType;
8662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
8672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private JavaType javaType;
8692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public FieldDescriptorProto.Type toProto() {
8712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return FieldDescriptorProto.Type.valueOf(ordinal() + 1);
8722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
8732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public JavaType getJavaType() { return javaType; }
8742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public static Type valueOf(final FieldDescriptorProto.Type type) {
8762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return values()[type.getNumber() - 1];
8772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
8782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    static {
8812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Refuse to init if someone added a new declared type.
8822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (Type.values().length != FieldDescriptorProto.Type.values().length) {
8832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new RuntimeException(
8842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "descriptor.proto has a new declared type but Desrciptors.java " +
8852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "wasn't updated.");
8862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
8872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
8882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public enum JavaType {
8902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      INT(0),
8912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LONG(0L),
8922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FLOAT(0F),
8932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DOUBLE(0D),
8942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BOOLEAN(false),
8952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      STRING(""),
8962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BYTE_STRING(ByteString.EMPTY),
8972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ENUM(null),
8982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MESSAGE(null);
8992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      JavaType(final Object defaultDefault) {
9012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.defaultDefault = defaultDefault;
9022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
9032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      /**
9052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       * The default default value for fields of this type, if it's a primitive
9062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       * type.  This is meant for use inside this file only, hence is private.
9072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       */
9082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private final Object defaultDefault;
9092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
9102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private FieldDescriptor(final FieldDescriptorProto proto,
9122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            final FileDescriptor file,
9132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            final Descriptor parent,
9142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            final int index,
9152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            final boolean isExtension)
9162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     throws DescriptorValidationException {
9172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.index = index;
9182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
9192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fullName = computeFullName(file, parent, proto.getName());
9202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.file = file;
9212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (proto.hasType()) {
9232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        type = Type.valueOf(proto.getType());
9242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
9252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (getNumber() <= 0) {
9272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(this,
9282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Field numbers must be positive integers.");
9292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
9302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Only repeated primitive fields may be packed.
9322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (proto.getOptions().getPacked() && !isPackable()) {
9332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(this,
9342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "[packed = true] can only be specified for repeated primitive " +
9352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "fields.");
9362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
9372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (isExtension) {
9392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!proto.hasExtendee()) {
9402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
9412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "FieldDescriptorProto.extendee not set for extension field.");
9422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
9432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        containingType = null;  // Will be filled in when cross-linking
9442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (parent != null) {
9452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extensionScope = parent;
9462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
9472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extensionScope = null;
9482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
9492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
9502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (proto.hasExtendee()) {
9512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
9522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "FieldDescriptorProto.extendee set for non-extension field.");
9532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
9542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        containingType = parent;
9552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        extensionScope = null;
9562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
9572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addSymbol(this);
9592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
9602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Look up and cross-link all field types, etc. */
9622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void crossLink() throws DescriptorValidationException {
9632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (proto.hasExtendee()) {
9642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final GenericDescriptor extendee =
9653d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          file.pool.lookupSymbol(proto.getExtendee(), this,
9663d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch              DescriptorPool.SearchFilter.TYPES_ONLY);
9672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!(extendee instanceof Descriptor)) {
9682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
9692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '\"' + proto.getExtendee() + "\" is not a message type.");
9702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
9712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        containingType = (Descriptor)extendee;
9722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!getContainingType().isExtensionNumber(getNumber())) {
9742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
9752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '\"' + getContainingType().getFullName() +
9762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "\" does not declare " + getNumber() +
9772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              " as an extension number.");
9782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
9792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
9802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (proto.hasTypeName()) {
9822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final GenericDescriptor typeDescriptor =
9833d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          file.pool.lookupSymbol(proto.getTypeName(), this,
9843d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch              DescriptorPool.SearchFilter.TYPES_ONLY);
9852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!proto.hasType()) {
9872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Choose field type based on symbol.
9882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (typeDescriptor instanceof Descriptor) {
9892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            type = Type.MESSAGE;
9902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (typeDescriptor instanceof EnumDescriptor) {
9912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            type = Type.ENUM;
9922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
9932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(this,
9942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                '\"' + proto.getTypeName() + "\" is not a type.");
9952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
9962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
9972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (getJavaType() == JavaType.MESSAGE) {
9992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (!(typeDescriptor instanceof Descriptor)) {
10002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(this,
10012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                '\"' + proto.getTypeName() + "\" is not a message type.");
10022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
10032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          messageType = (Descriptor)typeDescriptor;
10042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (proto.hasDefaultValue()) {
10062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(this,
10072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "Messages can't have default values.");
10082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
10092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else if (getJavaType() == JavaType.ENUM) {
10102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (!(typeDescriptor instanceof EnumDescriptor)) {
10112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(this,
10122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                '\"' + proto.getTypeName() + "\" is not an enum type.");
10132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
10142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          enumType = (EnumDescriptor)typeDescriptor;
10152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
10162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
10172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "Field with primitive type has type_name.");
10182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
10192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
10202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (getJavaType() == JavaType.MESSAGE ||
10212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            getJavaType() == JavaType.ENUM) {
10222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
10232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "Field with message or enum type missing type_name.");
10242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
10252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
10262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // We don't attempt to parse the default value until here because for
10282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // enums we need the enum type's descriptor.
10292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (proto.hasDefaultValue()) {
10302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (isRepeated()) {
10312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
10322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "Repeated fields cannot have default values.");
10332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
10342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        try {
10362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          switch (getType()) {
10372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case INT32:
10382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case SINT32:
10392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case SFIXED32:
10402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
10412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case UINT32:
10432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case FIXED32:
10442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
10452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case INT64:
10472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case SINT64:
10482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case SFIXED64:
10492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
10502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case UINT64:
10522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case FIXED64:
10532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
10542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case FLOAT:
10562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              if (proto.getDefaultValue().equals("inf")) {
10572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Float.POSITIVE_INFINITY;
10582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } else if (proto.getDefaultValue().equals("-inf")) {
10592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Float.NEGATIVE_INFINITY;
10602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } else if (proto.getDefaultValue().equals("nan")) {
10612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Float.NaN;
10622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } else {
10632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Float.valueOf(proto.getDefaultValue());
10642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              }
10652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case DOUBLE:
10672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              if (proto.getDefaultValue().equals("inf")) {
10682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Double.POSITIVE_INFINITY;
10692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } else if (proto.getDefaultValue().equals("-inf")) {
10702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Double.NEGATIVE_INFINITY;
10712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } else if (proto.getDefaultValue().equals("nan")) {
10722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Double.NaN;
10732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } else {
10742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue = Double.valueOf(proto.getDefaultValue());
10752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              }
10762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case BOOL:
10782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = Boolean.valueOf(proto.getDefaultValue());
10792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case STRING:
10812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = proto.getDefaultValue();
10822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case BYTES:
10842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              try {
10852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                defaultValue =
10862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  TextFormat.unescapeBytes(proto.getDefaultValue());
10872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              } catch (TextFormat.InvalidEscapeSequenceException e) {
10882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                throw new DescriptorValidationException(this,
10892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  "Couldn't parse default value: " + e.getMessage(), e);
10902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              }
10912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
10922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case ENUM:
10932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = enumType.findValueByName(proto.getDefaultValue());
10942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              if (defaultValue == null) {
10952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                throw new DescriptorValidationException(this,
10962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  "Unknown enum default value: \"" +
10972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  proto.getDefaultValue() + '\"');
10982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              }
10992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
11002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case MESSAGE:
11012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case GROUP:
11022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              throw new DescriptorValidationException(this,
11032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                "Message type had default value.");
11042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
11052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } catch (NumberFormatException e) {
11062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
11072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "Could not parse default value: \"" +
11082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              proto.getDefaultValue() + '\"', e);
11092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
11102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
11112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Determine the default default for this field.
11122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (isRepeated()) {
11132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          defaultValue = Collections.emptyList();
11142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
11152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          switch (getJavaType()) {
11162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case ENUM:
11172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              // We guarantee elsewhere that an enum type always has at least
11182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              // one possible value.
11192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = enumType.getValues().get(0);
11202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
11212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            case MESSAGE:
11222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = null;
11232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
11242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            default:
11252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              defaultValue = getJavaType().defaultDefault;
11262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
11272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
11282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
11292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
11302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!isExtension()) {
11322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        file.pool.addFieldByNumber(this);
11332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
11342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (containingType != null &&
11362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          containingType.getOptions().getMessageSetWireFormat()) {
11372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (isExtension()) {
11382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (!isOptional() || getType() != Type.MESSAGE) {
11392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(this,
11402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "Extensions of MessageSets must be optional messages.");
11412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
11422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
11432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(this,
11442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "MessageSets cannot have fields, only extensions.");
11452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
11462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
11472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
11482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** See {@link FileDescriptor#setProto}. */
11502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final FieldDescriptorProto proto) {
11512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
11522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
11532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
11552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * For internal use only.  This is to satisfy the FieldDescriptorLite
11562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * interface.
11572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
11582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public MessageLite.Builder internalMergeFrom(
11592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        MessageLite.Builder to, MessageLite from) {
11602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // FieldDescriptors are only used with non-lite messages so we can just
11612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // down-cast and call mergeFrom directly.
11622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ((Message.Builder) to).mergeFrom((Message) from);
11632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
11642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
11652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
11672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /** Describes an enum type. */
11692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class EnumDescriptor
11702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      implements GenericDescriptor, Internal.EnumLiteMap<EnumValueDescriptor> {
11712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
11722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the index of this descriptor within its parent.
11733d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getIndex()
11742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
11752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getIndex() { return index; }
11762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
11782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumDescriptorProto toProto() { return proto; }
11792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the type's unqualified name. */
11812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
11822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
11842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the type's fully-qualified name.
11853d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getFullName()
11862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
11872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getFullName() { return fullName; }
11882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@link FileDescriptor} containing this descriptor. */
11902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptor getFile() { return file; }
11912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** If this is a nested type, get the outer descriptor, otherwise null. */
11932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getContainingType() { return containingType; }
11942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
11962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumOptions getOptions() { return proto.getOptions(); }
11972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of defined values for this enum. */
11992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<EnumValueDescriptor> getValues() {
12002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(values));
12012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
12022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
12042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find an enum value by name.
12052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified name of the value (e.g. "FOO").
12063d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @return the value's descriptor, or {@code null} if not found.
12072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
12082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumValueDescriptor findValueByName(final String name) {
12092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result =
12102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          file.pool.findSymbol(fullName + '.' + name);
12112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof EnumValueDescriptor) {
12122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (EnumValueDescriptor)result;
12132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
12142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
12152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
12162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
12172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
12192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find an enum value by number.  If multiple enum values have the same
12202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * number, this returns the first defined value with that number.
12212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param number The value's number.
12223d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @return the value's descriptor, or {@code null} if not found.
12232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
12242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumValueDescriptor findValueByNumber(final int number) {
12252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return file.pool.enumValuesByNumber.get(
12262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new DescriptorPool.DescriptorIntPair(this, number));
12272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
12282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final int index;
12302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private EnumDescriptorProto proto;
12312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String fullName;
12322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor file;
12332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Descriptor containingType;
12342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private EnumValueDescriptor[] values;
12352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private EnumDescriptor(final EnumDescriptorProto proto,
12372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           final FileDescriptor file,
12382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           final Descriptor parent,
12392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           final int index)
12402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    throws DescriptorValidationException {
12412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.index = index;
12422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
12432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fullName = computeFullName(file, parent, proto.getName());
12442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.file = file;
12452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      containingType = parent;
12462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (proto.getValueCount() == 0) {
12482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // We cannot allow enums with no values because this would mean there
12492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // would be no valid default value for fields of this type.
12502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(this,
12512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Enums must contain at least one value.");
12522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
12532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      values = new EnumValueDescriptor[proto.getValueCount()];
12552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getValueCount(); i++) {
12562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        values[i] = new EnumValueDescriptor(
12572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getValue(i), file, this, i);
12582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
12592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addSymbol(this);
12612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
12622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** See {@link FileDescriptor#setProto}. */
12642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final EnumDescriptorProto proto) {
12652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
12662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < values.length; i++) {
12682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        values[i].setProto(proto.getValue(i));
12692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
12702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
12712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
12722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
12742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /**
12762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * Describes one value within an enum type.  Note that multiple defined
12772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * values may have the same number.  In generated Java code, all values
12782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * with the same number after the first become aliases of the first.
12792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * However, they still have independent EnumValueDescriptors.
12802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   */
12812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class EnumValueDescriptor
12822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      implements GenericDescriptor, Internal.EnumLite {
12832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
12842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the index of this descriptor within its parent.
12853d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getIndex()
12862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
12872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getIndex() { return index; }
12882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
12902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumValueDescriptorProto toProto() { return proto; }
12912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the value's unqualified name. */
12932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
12942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the value's number. */
12962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getNumber() { return proto.getNumber(); }
12972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
12992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the value's fully-qualified name.
13003d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getFullName()
13012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
13022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getFullName() { return fullName; }
13032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@link FileDescriptor} containing this descriptor. */
13052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptor getFile() { return file; }
13062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the value's enum type. */
13082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumDescriptor getType() { return type; }
13092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
13112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}.
13122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
13132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public EnumValueOptions getOptions() { return proto.getOptions(); }
13142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final int index;
13162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private EnumValueDescriptorProto proto;
13172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String fullName;
13182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor file;
13192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final EnumDescriptor type;
13202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private EnumValueDescriptor(final EnumValueDescriptorProto proto,
13222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                final FileDescriptor file,
13232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                final EnumDescriptor parent,
13242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                final int index)
13252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         throws DescriptorValidationException {
13262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.index = index;
13272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
13282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.file = file;
13292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      type = parent;
13302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fullName = parent.getFullName() + '.' + proto.getName();
13322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addSymbol(this);
13342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addEnumValueByNumber(this);
13352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
13362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** See {@link FileDescriptor#setProto}. */
13382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final EnumValueDescriptorProto proto) {
13392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
13402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
13412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
13422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
13442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /** Describes a service type. */
13462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class ServiceDescriptor implements GenericDescriptor {
13472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
13482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the index of this descriptor within its parent.
13492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * * @see Descriptors.Descriptor#getIndex()
13502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
13512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getIndex() { return index; }
13522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
13542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public ServiceDescriptorProto toProto() { return proto; }
13552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the type's unqualified name. */
13572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
13582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
13602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the type's fully-qualified name.
13613d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getFullName()
13622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
13632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getFullName() { return fullName; }
13642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@link FileDescriptor} containing this descriptor. */
13662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptor getFile() { return file; }
13672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
13692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public ServiceOptions getOptions() { return proto.getOptions(); }
13702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get a list of methods for this service. */
13722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public List<MethodDescriptor> getMethods() {
13732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Collections.unmodifiableList(Arrays.asList(methods));
13742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
13752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
13772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Find a method by name.
13782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param name The unqualified name of the method (e.g. "Foo").
13793d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @return the method's descriptor, or {@code null} if not found.
13802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
13812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public MethodDescriptor findMethodByName(final String name) {
13822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor result =
13832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          file.pool.findSymbol(fullName + '.' + name);
13842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null && result instanceof MethodDescriptor) {
13852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return (MethodDescriptor)result;
13862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
13872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return null;
13882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
13892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
13902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final int index;
13922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private ServiceDescriptorProto proto;
13932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String fullName;
13942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor file;
13952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private MethodDescriptor[] methods;
13962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private ServiceDescriptor(final ServiceDescriptorProto proto,
13982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              final FileDescriptor file,
13992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              final int index)
14002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       throws DescriptorValidationException {
14012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.index = index;
14022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
14032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fullName = computeFullName(file, null, proto.getName());
14042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.file = file;
14052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      methods = new MethodDescriptor[proto.getMethodCount()];
14072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < proto.getMethodCount(); i++) {
14082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        methods[i] = new MethodDescriptor(
14092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          proto.getMethod(i), file, this, i);
14102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
14112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addSymbol(this);
14132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
14142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void crossLink() throws DescriptorValidationException {
14162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (final MethodDescriptor method : methods) {
14172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        method.crossLink();
14182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
14192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
14202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** See {@link FileDescriptor#setProto}. */
14222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final ServiceDescriptorProto proto) {
14232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
14242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (int i = 0; i < methods.length; i++) {
14262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        methods[i].setProto(proto.getMethod(i));
14272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
14282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
14292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
14302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
14322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /**
14342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * Describes one method within a service type.
14352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   */
14362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static final class MethodDescriptor implements GenericDescriptor {
14372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
14382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the index of this descriptor within its parent.
14392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * * @see Descriptors.Descriptor#getIndex()
14402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
14412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public int getIndex() { return index; }
14422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Convert the descriptor to its protocol message representation. */
14442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public MethodDescriptorProto toProto() { return proto; }
14452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the method's unqualified name. */
14472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getName() { return proto.getName(); }
14482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
14502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the method's fully-qualified name.
14513d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * @see Descriptors.Descriptor#getFullName()
14522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
14532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getFullName() { return fullName; }
14542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the {@link FileDescriptor} containing this descriptor. */
14562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public FileDescriptor getFile() { return file; }
14572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the method's service type. */
14592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public ServiceDescriptor getService() { return service; }
14602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the method's input type. */
14622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getInputType() { return inputType; }
14632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Get the method's output type. */
14652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Descriptor getOutputType() { return outputType; }
14662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
14682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Get the {@code MethodOptions}, defined in {@code descriptor.proto}.
14692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
14702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public MethodOptions getOptions() { return proto.getOptions(); }
14712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final int index;
14732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private MethodDescriptorProto proto;
14742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String fullName;
14752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final FileDescriptor file;
14762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final ServiceDescriptor service;
14772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Initialized during cross-linking.
14792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Descriptor inputType;
14802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private Descriptor outputType;
14812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private MethodDescriptor(final MethodDescriptorProto proto,
14832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             final FileDescriptor file,
14842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             final ServiceDescriptor parent,
14852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             final int index)
14862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      throws DescriptorValidationException {
14872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.index = index;
14882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
14892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.file = file;
14902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      service = parent;
14912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fullName = parent.getFullName() + '.' + proto.getName();
14932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file.pool.addSymbol(this);
14952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
14962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void crossLink() throws DescriptorValidationException {
14982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor input =
14993d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        file.pool.lookupSymbol(proto.getInputType(), this,
15003d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            DescriptorPool.SearchFilter.TYPES_ONLY);
15012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!(input instanceof Descriptor)) {
15022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(this,
15032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '\"' + proto.getInputType() + "\" is not a message type.");
15042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
15052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      inputType = (Descriptor)input;
15062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor output =
15083d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        file.pool.lookupSymbol(proto.getOutputType(), this,
15093d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            DescriptorPool.SearchFilter.TYPES_ONLY);
15102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!(output instanceof Descriptor)) {
15112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(this,
15122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '\"' + proto.getOutputType() + "\" is not a message type.");
15132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
15142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      outputType = (Descriptor)output;
15152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
15162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** See {@link FileDescriptor#setProto}. */
15182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private void setProto(final MethodDescriptorProto proto) {
15192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.proto = proto;
15202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
15212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
15222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
15242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  private static String computeFullName(final FileDescriptor file,
15262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        final Descriptor parent,
15272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        final String name) {
15282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (parent != null) {
15292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return parent.getFullName() + '.' + name;
15302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (file.getPackage().length() > 0) {
15312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return file.getPackage() + '.' + name;
15322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
15332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return name;
15342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
15352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
15362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
15382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /**
15402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * All descriptors except {@code FileDescriptor} implement this to make
15412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * {@code DescriptorPool}'s life easier.
15422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   */
15432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  private interface GenericDescriptor {
15442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Message toProto();
15452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    String getName();
15462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    String getFullName();
15472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FileDescriptor getFile();
15482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
15492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /**
15512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * Thrown when building descriptors fails because the source DescriptorProtos
15522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * are not valid.
15532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   */
15542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  public static class DescriptorValidationException extends Exception {
15552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static final long serialVersionUID = 5750205775490483148L;
15562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Gets the full name of the descriptor where the error occurred. */
15582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getProblemSymbolName() { return name; }
15592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
15613d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * Gets the protocol message representation of the invalid descriptor.
15622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
15632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public Message getProblemProto() { return proto; }
15642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
15662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Gets a human-readable description of the error.
15672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
15682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public String getDescription() { return description; }
15692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String name;
15712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Message proto;
15722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final String description;
15732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private DescriptorValidationException(
15752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final GenericDescriptor problemDescriptor,
15762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String description) {
15772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      super(problemDescriptor.getFullName() + ": " + description);
15782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Note that problemDescriptor may be partially uninitialized, so we
15802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // don't want to expose it directly to the user.  So, we only provide
15812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // the name and the original proto.
15822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      name = problemDescriptor.getFullName();
15832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      proto = problemDescriptor.toProto();
15842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.description = description;
15852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
15862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private DescriptorValidationException(
15882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final GenericDescriptor problemDescriptor,
15892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String description,
15902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final Throwable cause) {
15912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this(problemDescriptor, description);
15922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      initCause(cause);
15932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
15942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private DescriptorValidationException(
15962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final FileDescriptor problemDescriptor,
15972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String description) {
15982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      super(problemDescriptor.getName() + ": " + description);
15992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Note that problemDescriptor may be partially uninitialized, so we
16012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // don't want to expose it directly to the user.  So, we only provide
16022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // the name and the original proto.
16032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      name = problemDescriptor.getName();
16042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      proto = problemDescriptor.toProto();
16052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.description = description;
16062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
16072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
16082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // =================================================================
16102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  /**
16122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * A private helper class which contains lookup tables containing all the
16132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * descriptors defined in a particular file.
16142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   */
16152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  private static final class DescriptorPool {
16163d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
16173d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    /** Defines what subclass of descriptors to search in the descriptor pool.
16183d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     */
16193d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    enum SearchFilter {
16203d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      TYPES_ONLY, AGGREGATES_ONLY, ALL_SYMBOLS
16213d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    }
16223d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
16232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DescriptorPool(final FileDescriptor[] dependencies) {
16243d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      this.dependencies = new HashSet<FileDescriptor>();
16252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16263d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      for (int i = 0; i < dependencies.length; i++) {
16273d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        this.dependencies.add(dependencies[i]);
16283d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        importPublicDependencies(dependencies[i]);
16292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
16302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16313d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      for (final FileDescriptor dependency : this.dependencies) {
16322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        try {
16332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          addPackage(dependency.getPackage(), dependency);
16342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } catch (DescriptorValidationException e) {
16352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Can't happen, because addPackage() only fails when the name
16362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // conflicts with a non-package, but we have not yet added any
16372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // non-packages at this point.
16382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          assert false;
16392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
16402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
16412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
16422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16433d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    /** Find and put public dependencies of the file into dependencies set.*/
16443d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    private void importPublicDependencies(final FileDescriptor file) {
16453d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      for (FileDescriptor dependency : file.getPublicDependencies()) {
16463d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        if (dependencies.add(dependency)) {
16473d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          importPublicDependencies(dependency);
16483d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        }
16493d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      }
16503d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    }
16513d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
16523d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    private final Set<FileDescriptor> dependencies;
16532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Map<String, GenericDescriptor> descriptorsByName =
16552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new HashMap<String, GenericDescriptor>();
16562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
16572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new HashMap<DescriptorIntPair, FieldDescriptor>();
16582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber
16592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        = new HashMap<DescriptorIntPair, EnumValueDescriptor>();
16602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** Find a generic descriptor by fully-qualified name. */
16622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GenericDescriptor findSymbol(final String fullName) {
16633d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
16643d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    }
16653d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
16663d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    /** Find a descriptor by fully-qualified name and given option to only
16673d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * search valid field type descriptors.
16683d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     */
16693d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    GenericDescriptor findSymbol(final String fullName,
16703d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch                                 final SearchFilter filter) {
16712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GenericDescriptor result = descriptorsByName.get(fullName);
16722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result != null) {
16733d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        if ((filter==SearchFilter.ALL_SYMBOLS) ||
16743d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            ((filter==SearchFilter.TYPES_ONLY) && isType(result)) ||
16753d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
16763d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          return result;
16773d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        }
16782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
16792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16803d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      for (final FileDescriptor dependency : dependencies) {
16813d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        result = dependency.pool.descriptorsByName.get(fullName);
16822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (result != null) {
16833d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          if ((filter==SearchFilter.ALL_SYMBOLS) ||
16843d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch              ((filter==SearchFilter.TYPES_ONLY) && isType(result)) ||
16853d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch              ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
16863d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            return result;
16873d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch          }
16882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
16892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
16902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return null;
16922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
16932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16943d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    /** Checks if the descriptor is a valid type for a message field. */
16953d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    boolean isType(GenericDescriptor descriptor) {
16963d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      return (descriptor instanceof Descriptor) ||
16973d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        (descriptor instanceof EnumDescriptor);
16983d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    }
16993d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
17003d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    /** Checks if the descriptor is a valid namespace type. */
17013d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    boolean isAggregate(GenericDescriptor descriptor) {
17023d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch      return (descriptor instanceof Descriptor) ||
17033d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        (descriptor instanceof EnumDescriptor) ||
17043d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        (descriptor instanceof PackageDescriptor) ||
17053d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        (descriptor instanceof ServiceDescriptor);
17063d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch    }
17073d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch
17082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
17093d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * Look up a type descriptor by name, relative to some other descriptor.
17102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * The name may be fully-qualified (with a leading '.'),
17112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * partially-qualified, or unqualified.  C++-like name lookup semantics
17122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * are used to search for the matching descriptor.
17132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
17142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GenericDescriptor lookupSymbol(final String name,
17153d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch                                   final GenericDescriptor relativeTo,
17163d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch                                   final DescriptorPool.SearchFilter filter)
17172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            throws DescriptorValidationException {
17182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // TODO(kenton):  This could be optimized in a number of ways.
17192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GenericDescriptor result;
17212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (name.startsWith(".")) {
17222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Fully-qualified name.
17233d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        result = findSymbol(name.substring(1), filter);
17242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
17252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // If "name" is a compound identifier, we want to search for the
17262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // first component of it, then search within it for the rest.
17273d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
17283d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        // defined in multiple parent scopes, we only want to find "Bar.baz" in
17293d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        // the innermost one.  E.g., the following should produce an error:
17303d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        //   message Bar { message Baz {} }
17313d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        //   message Foo {
17323d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        //     message Bar {
17333d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        //     }
17343d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        //     optional Bar.Baz baz = 1;
17353d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        //   }
17363d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        // So, we look for just "Foo" first, then look for "Bar.baz" within it
17373d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch        // if found.
17382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final int firstPartLength = name.indexOf('.');
17392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String firstPart;
17402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (firstPartLength == -1) {
17412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          firstPart = name;
17422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
17432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          firstPart = name.substring(0, firstPartLength);
17442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
17452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // We will search each parent scope of "relativeTo" looking for the
17472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // symbol.
17482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final StringBuilder scopeToTry =
17492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            new StringBuilder(relativeTo.getFullName());
17502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        while (true) {
17522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Chop off the last component of the scope.
17532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          final int dotpos = scopeToTry.lastIndexOf(".");
17542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (dotpos == -1) {
17553d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            result = findSymbol(name, filter);
17562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
17572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
17582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            scopeToTry.setLength(dotpos + 1);
17592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17603d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            // Append firstPart and try to find
17612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            scopeToTry.append(firstPart);
17623d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch            result = findSymbol(scopeToTry.toString(),
17633d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch                DescriptorPool.SearchFilter.AGGREGATES_ONLY);
17642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if (result != null) {
17662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              if (firstPartLength != -1) {
17672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                // We only found the first part of the symbol.  Now look for
17682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                // the whole thing.  If this fails, we *don't* want to keep
17692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                // searching parent scopes.
17702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                scopeToTry.setLength(dotpos + 1);
17712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                scopeToTry.append(name);
17723d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch                result = findSymbol(scopeToTry.toString(), filter);
17732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              }
17742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              break;
17752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            }
17762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // Not found.  Remove the name so we can try again.
17782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            scopeToTry.setLength(dotpos);
17792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
17802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
17812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
17822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (result == null) {
17842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(relativeTo,
17852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '\"' + name + "\" is not defined.");
17862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
17872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return result;
17882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
17892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
17902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
17922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Adds a symbol to the symbol table.  If a symbol with the same name
17932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * already exists, throws an error.
17942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
17952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    void addSymbol(final GenericDescriptor descriptor)
17962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throws DescriptorValidationException {
17972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      validateSymbolName(descriptor);
17982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final String fullName = descriptor.getFullName();
18002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final int dotpos = fullName.lastIndexOf('.');
18012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
18032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (old != null) {
18042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        descriptorsByName.put(fullName, old);
18052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (descriptor.getFile() == old.getFile()) {
18072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (dotpos == -1) {
18082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(descriptor,
18092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                '\"' + fullName + "\" is already defined.");
18102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
18112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new DescriptorValidationException(descriptor,
18122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                '\"' + fullName.substring(dotpos + 1) +
18132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "\" is already defined in \"" +
18142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              fullName.substring(0, dotpos) + "\".");
18152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
18162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
18172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(descriptor,
18182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '\"' + fullName + "\" is already defined in file \"" +
18192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            old.getFile().getName() + "\".");
18202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
18212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
18232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
18252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Represents a package in the symbol table.  We use PackageDescriptors
18262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * just as placeholders so that someone cannot define, say, a message type
18272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * that has the same name as an existing package.
18282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
18292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static final class PackageDescriptor implements GenericDescriptor {
18302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public Message toProto()        { return file.toProto(); }
18312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public String getName()         { return name;           }
18322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public String getFullName()     { return fullName;       }
18332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public FileDescriptor getFile() { return file;           }
18342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      PackageDescriptor(final String name, final String fullName,
18362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        final FileDescriptor file) {
18372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.file = file;
18382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.fullName = fullName;
18392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.name = name;
18402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private final String name;
18432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private final String fullName;
18442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private final FileDescriptor file;
18452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
18462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
18482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Adds a package to the symbol tables.  If a package by the same name
18492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * already exists, that is fine, but if some other kind of symbol exists
18502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * under the same name, an exception is thrown.  If the package has
18512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * multiple components, this also adds the parent package(s).
18522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
18532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    void addPackage(final String fullName, final FileDescriptor file)
18542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             throws DescriptorValidationException {
18552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final int dotpos = fullName.lastIndexOf('.');
18562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final String name;
18572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (dotpos == -1) {
18582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        name = fullName;
18592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
18602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        addPackage(fullName.substring(0, dotpos), file);
18612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        name = fullName.substring(dotpos + 1);
18622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final GenericDescriptor old =
18652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        descriptorsByName.put(fullName,
18662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          new PackageDescriptor(name, fullName, file));
18672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (old != null) {
18682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        descriptorsByName.put(fullName, old);
18692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!(old instanceof PackageDescriptor)) {
18702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(file,
18712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '\"' + name + "\" is already defined (as something other than a "
18722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              + "package) in file \"" + old.getFile().getName() + "\".");
18732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
18742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
18762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /** A (GenericDescriptor, int) pair, used as a map key. */
18782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static final class DescriptorIntPair {
18792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private final GenericDescriptor descriptor;
18802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      private final int number;
18812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DescriptorIntPair(final GenericDescriptor descriptor, final int number) {
18832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.descriptor = descriptor;
18842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.number = number;
18852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      @Override
18882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public int hashCode() {
18892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return descriptor.hashCode() * ((1 << 16) - 1) + number;
18902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      @Override
18922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public boolean equals(final Object obj) {
18932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!(obj instanceof DescriptorIntPair)) {
18942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return false;
18952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
18962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final DescriptorIntPair other = (DescriptorIntPair)obj;
18972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return descriptor == other.descriptor && number == other.number;
18982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
18992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
19002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
19022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Adds a field to the fieldsByNumber table.  Throws an exception if a
19033d4dfb6f11fb4e934d658743a8efc26d5490fdb0Ben Murdoch     * field with the same containing type and number already exists.
19042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
19052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    void addFieldByNumber(final FieldDescriptor field)
19062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   throws DescriptorValidationException {
19072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final DescriptorIntPair key =
19082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new DescriptorIntPair(field.getContainingType(), field.getNumber());
19092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final FieldDescriptor old = fieldsByNumber.put(key, field);
19102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (old != null) {
19112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        fieldsByNumber.put(key, old);
19122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(field,
19132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "Field number " + field.getNumber() +
19142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "has already been used in \"" +
19152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          field.getContainingType().getFullName() +
19162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "\" by field \"" + old.getName() + "\".");
19172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
19182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
19192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
19212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Adds an enum value to the enumValuesByNumber table.  If an enum value
19222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * with the same type and number already exists, does nothing.  (This is
19232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * allowed; the first value define with the number takes precedence.)
19242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
19252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    void addEnumValueByNumber(final EnumValueDescriptor value) {
19262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final DescriptorIntPair key =
19272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new DescriptorIntPair(value.getType(), value.getNumber());
19282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final EnumValueDescriptor old = enumValuesByNumber.put(key, value);
19292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (old != null) {
19302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        enumValuesByNumber.put(key, old);
19312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Not an error:  Multiple enum values may have the same number, but
19322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // we only want the first one in the map.
19332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
19342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
19352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
19372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Verifies that the descriptor's name is valid (i.e. it contains only
19382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * letters, digits, and underscores, and does not start with a digit).
19392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
19402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    static void validateSymbolName(final GenericDescriptor descriptor)
19412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   throws DescriptorValidationException {
19422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      final String name = descriptor.getName();
19432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (name.length() == 0) {
19442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new DescriptorValidationException(descriptor, "Missing name.");
19452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
19462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        boolean valid = true;
19472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        for (int i = 0; i < name.length(); i++) {
19482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          final char c = name.charAt(i);
19492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Non-ASCII characters are not valid in protobuf identifiers, even
19502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // if they are letters or digits.
19512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (c >= 128) {
19522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            valid = false;
19532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
19542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // First character must be letter or _.  Subsequent characters may
19552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // be letters, numbers, or digits.
19562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (Character.isLetter(c) || c == '_' ||
19572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              (Character.isDigit(c) && i > 0)) {
19582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // Valid
19592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
19602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            valid = false;
19612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
19622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
19632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!valid) {
19642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new DescriptorValidationException(descriptor,
19652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '\"' + name + "\" is not a valid identifier.");
19662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
19672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
19682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
19692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
19702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1971