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