1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 package com.google.protobuf;
33 import com.google.protobuf.DescriptorProtos.*;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.List;
42 import java.io.UnsupportedEncodingException;
45 * Contains a collection of classes which describe protocol message types.
47 * Every message type has a {@link Descriptor}, which lists all
48 * its fields and other information about a type. You can get a message
49 * type's descriptor by calling {@code MessageType.getDescriptor()}, or
50 * (given a message object of the type) {@code message.getDescriptorForType()}.
51 * Furthermore, each message is associated with a {@link FileDescriptor} for
52 * a relevant {@code .proto} file. You can obtain it by calling
53 * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors
54 * for all the messages defined in that file, and file descriptors for all the
55 * imported {@code .proto} files.
57 * Descriptors are built from DescriptorProtos, as defined in
58 * {@code google/protobuf/descriptor.proto}.
60 * @author kenton@google.com Kenton Varda
62 public final class Descriptors {
64 * Describes a {@code .proto} file, including everything defined within.
65 * That includes, in particular, descriptors for all the messages and
66 * file descriptors for all other imported {@code .proto} files
69 public static final class FileDescriptor {
70 /** Convert the descriptor to its protocol message representation. */
71 public FileDescriptorProto toProto() { return proto; }
73 /** Get the file name. */
74 public String getName() { return proto.getName(); }
77 * Get the proto package name. This is the package name given by the
78 * {@code package} statement in the {@code .proto} file, which differs
79 * from the Java package.
81 public String getPackage() { return proto.getPackage(); }
83 /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
84 public FileOptions getOptions() { return proto.getOptions(); }
86 /** Get a list of top-level message types declared in this file. */
87 public List<Descriptor> getMessageTypes() {
88 return Collections.unmodifiableList(Arrays.asList(messageTypes));
91 /** Get a list of top-level enum types declared in this file. */
92 public List<EnumDescriptor> getEnumTypes() {
93 return Collections.unmodifiableList(Arrays.asList(enumTypes));
96 /** Get a list of top-level services declared in this file. */
97 public List<ServiceDescriptor> getServices() {
98 return Collections.unmodifiableList(Arrays.asList(services));
101 /** Get a list of top-level extensions declared in this file. */
102 public List<FieldDescriptor> getExtensions() {
103 return Collections.unmodifiableList(Arrays.asList(extensions));
106 /** Get a list of this file's dependencies (imports). */
107 public List<FileDescriptor> getDependencies() {
108 return Collections.unmodifiableList(Arrays.asList(dependencies));
111 /** Get a list of this file's public dependencies (public imports). */
112 public List<FileDescriptor> getPublicDependencies() {
113 return Collections.unmodifiableList(Arrays.asList(publicDependencies));
117 * Find a message type in the file by name. Does not find nested types.
119 * @param name The unqualified type name to look for.
120 * @return The message type's descriptor, or {@code null} if not found.
122 public Descriptor findMessageTypeByName(String name) {
123 // Don't allow looking up nested types. This will make optimization
125 if (name.indexOf('.') != -1) {
128 if (getPackage().length() > 0) {
129 name = getPackage() + '.' + name;
131 final GenericDescriptor result = pool.findSymbol(name);
132 if (result != null && result instanceof Descriptor &&
133 result.getFile() == this) {
134 return (Descriptor)result;
141 * Find an enum type in the file by name. Does not find nested types.
143 * @param name The unqualified type name to look for.
144 * @return The enum type's descriptor, or {@code null} if not found.
146 public EnumDescriptor findEnumTypeByName(String name) {
147 // Don't allow looking up nested types. This will make optimization
149 if (name.indexOf('.') != -1) {
152 if (getPackage().length() > 0) {
153 name = getPackage() + '.' + name;
155 final GenericDescriptor result = pool.findSymbol(name);
156 if (result != null && result instanceof EnumDescriptor &&
157 result.getFile() == this) {
158 return (EnumDescriptor)result;
165 * Find a service type in the file by name.
167 * @param name The unqualified type name to look for.
168 * @return The service type's descriptor, or {@code null} if not found.
170 public ServiceDescriptor findServiceByName(String name) {
171 // Don't allow looking up nested types. This will make optimization
173 if (name.indexOf('.') != -1) {
176 if (getPackage().length() > 0) {
177 name = getPackage() + '.' + name;
179 final GenericDescriptor result = pool.findSymbol(name);
180 if (result != null && result instanceof ServiceDescriptor &&
181 result.getFile() == this) {
182 return (ServiceDescriptor)result;
189 * Find an extension in the file by name. Does not find extensions nested
190 * inside message types.
192 * @param name The unqualified extension name to look for.
193 * @return The extension's descriptor, or {@code null} if not found.
195 public FieldDescriptor findExtensionByName(String name) {
196 if (name.indexOf('.') != -1) {
199 if (getPackage().length() > 0) {
200 name = getPackage() + '.' + name;
202 final GenericDescriptor result = pool.findSymbol(name);
203 if (result != null && result instanceof FieldDescriptor &&
204 result.getFile() == this) {
205 return (FieldDescriptor)result;
212 * Construct a {@code FileDescriptor}.
214 * @param proto The protocol message form of the FileDescriptor.
215 * @param dependencies {@code FileDescriptor}s corresponding to all of
216 * the file's dependencies, in the exact order listed
218 * @throws DescriptorValidationException {@code proto} is not a valid
219 * descriptor. This can occur for a number of reasons, e.g.
220 * because a field has an undefined type or because two messages
221 * were defined with the same name.
223 public static FileDescriptor buildFrom(final FileDescriptorProto proto,
224 final FileDescriptor[] dependencies)
225 throws DescriptorValidationException {
226 // Building descriptors involves two steps: translating and linking.
227 // In the translation step (implemented by FileDescriptor's
228 // constructor), we build an object tree mirroring the
229 // FileDescriptorProto's tree and put all of the descriptors into the
230 // DescriptorPool's lookup tables. In the linking step, we look up all
231 // type references in the DescriptorPool, so that, for example, a
232 // FieldDescriptor for an embedded message contains a pointer directly
233 // to the Descriptor for that message's type. We also detect undefined
234 // types in the linking step.
235 final DescriptorPool pool = new DescriptorPool(dependencies);
236 final FileDescriptor result =
237 new FileDescriptor(proto, dependencies, pool);
239 if (dependencies.length != proto.getDependencyCount()) {
240 throw new DescriptorValidationException(result,
241 "Dependencies passed to FileDescriptor.buildFrom() don't match " +
242 "those listed in the FileDescriptorProto.");
244 for (int i = 0; i < proto.getDependencyCount(); i++) {
245 if (!dependencies[i].getName().equals(proto.getDependency(i))) {
246 throw new DescriptorValidationException(result,
247 "Dependencies passed to FileDescriptor.buildFrom() don't match " +
248 "those listed in the FileDescriptorProto.");
257 * This method is to be called by generated code only. It is equivalent
258 * to {@code buildFrom} except that the {@code FileDescriptorProto} is
259 * encoded in protocol buffer wire format.
261 public static void internalBuildGeneratedFileFrom(
262 final String[] descriptorDataParts,
263 final FileDescriptor[] dependencies,
264 final InternalDescriptorAssigner descriptorAssigner) {
265 // Hack: We can't embed a raw byte array inside generated Java code
266 // (at least, not efficiently), but we can embed Strings. So, the
267 // protocol compiler embeds the FileDescriptorProto as a giant
268 // string literal which is passed to this function to construct the
269 // file's FileDescriptor. The string literal contains only 8-bit
270 // characters, each one representing a byte of the FileDescriptorProto's
271 // serialized form. So, if we convert it to bytes in ISO-8859-1, we
272 // should get the original bytes that we want.
274 // descriptorData may contain multiple strings in order to get around the
275 // Java 64k string literal limit.
276 StringBuilder descriptorData = new StringBuilder();
277 for (String part : descriptorDataParts) {
278 descriptorData.append(part);
281 final byte[] descriptorBytes;
283 descriptorBytes = descriptorData.toString().getBytes("ISO-8859-1");
284 } catch (UnsupportedEncodingException e) {
285 throw new RuntimeException(
286 "Standard encoding ISO-8859-1 not supported by JVM.", e);
289 FileDescriptorProto proto;
291 proto = FileDescriptorProto.parseFrom(descriptorBytes);
292 } catch (InvalidProtocolBufferException e) {
293 throw new IllegalArgumentException(
294 "Failed to parse protocol buffer descriptor for generated code.", e);
297 final FileDescriptor result;
299 result = buildFrom(proto, dependencies);
300 } catch (DescriptorValidationException e) {
301 throw new IllegalArgumentException(
302 "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
305 final ExtensionRegistry registry =
306 descriptorAssigner.assignDescriptors(result);
308 if (registry != null) {
309 // We must re-parse the proto using the registry.
311 proto = FileDescriptorProto.parseFrom(descriptorBytes, registry);
312 } catch (InvalidProtocolBufferException e) {
313 throw new IllegalArgumentException(
314 "Failed to parse protocol buffer descriptor for generated code.",
318 result.setProto(proto);
323 * This class should be used by generated code only. When calling
324 * {@link FileDescriptor#internalBuildGeneratedFileFrom}, the caller
325 * provides a callback implementing this interface. The callback is called
326 * after the FileDescriptor has been constructed, in order to assign all
327 * the global variables defined in the generated code which point at parts
328 * of the FileDescriptor. The callback returns an ExtensionRegistry which
329 * contains any extensions which might be used in the descriptor -- that
330 * is, extensions of the various "Options" messages defined in
331 * descriptor.proto. The callback may also return null to indicate that
332 * no extensions are used in the descriptor.
334 public interface InternalDescriptorAssigner {
335 ExtensionRegistry assignDescriptors(FileDescriptor root);
338 private FileDescriptorProto proto;
339 private final Descriptor[] messageTypes;
340 private final EnumDescriptor[] enumTypes;
341 private final ServiceDescriptor[] services;
342 private final FieldDescriptor[] extensions;
343 private final FileDescriptor[] dependencies;
344 private final FileDescriptor[] publicDependencies;
345 private final DescriptorPool pool;
347 private FileDescriptor(final FileDescriptorProto proto,
348 final FileDescriptor[] dependencies,
349 final DescriptorPool pool)
350 throws DescriptorValidationException {
353 this.dependencies = dependencies.clone();
354 this.publicDependencies =
355 new FileDescriptor[proto.getPublicDependencyCount()];
356 for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
357 int index = proto.getPublicDependency(i);
358 if (index < 0 || index >= this.dependencies.length) {
359 throw new DescriptorValidationException(this,
360 "Invalid public dependency index.");
362 this.publicDependencies[i] =
363 this.dependencies[proto.getPublicDependency(i)];
366 pool.addPackage(getPackage(), this);
368 messageTypes = new Descriptor[proto.getMessageTypeCount()];
369 for (int i = 0; i < proto.getMessageTypeCount(); i++) {
371 new Descriptor(proto.getMessageType(i), this, null, i);
374 enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
375 for (int i = 0; i < proto.getEnumTypeCount(); i++) {
376 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i);
379 services = new ServiceDescriptor[proto.getServiceCount()];
380 for (int i = 0; i < proto.getServiceCount(); i++) {
381 services[i] = new ServiceDescriptor(proto.getService(i), this, i);
384 extensions = new FieldDescriptor[proto.getExtensionCount()];
385 for (int i = 0; i < proto.getExtensionCount(); i++) {
386 extensions[i] = new FieldDescriptor(
387 proto.getExtension(i), this, null, i, true);
391 /** Look up and cross-link all field types, etc. */
392 private void crossLink() throws DescriptorValidationException {
393 for (final Descriptor messageType : messageTypes) {
394 messageType.crossLink();
397 for (final ServiceDescriptor service : services) {
401 for (final FieldDescriptor extension : extensions) {
402 extension.crossLink();
407 * Replace our {@link FileDescriptorProto} with the given one, which is
408 * identical except that it might contain extensions that weren't present
409 * in the original. This method is needed for bootstrapping when a file
410 * defines custom options. The options may be defined in the file itself,
411 * so we can't actually parse them until we've constructed the descriptors,
412 * but to construct the descriptors we have to have parsed the descriptor
413 * protos. So, we have to parse the descriptor protos a second time after
414 * constructing the descriptors.
416 private void setProto(final FileDescriptorProto proto) {
419 for (int i = 0; i < messageTypes.length; i++) {
420 messageTypes[i].setProto(proto.getMessageType(i));
423 for (int i = 0; i < enumTypes.length; i++) {
424 enumTypes[i].setProto(proto.getEnumType(i));
427 for (int i = 0; i < services.length; i++) {
428 services[i].setProto(proto.getService(i));
431 for (int i = 0; i < extensions.length; i++) {
432 extensions[i].setProto(proto.getExtension(i));
437 // =================================================================
439 /** Describes a message type. */
440 public static final class Descriptor implements GenericDescriptor {
442 * Get the index of this descriptor within its parent. In other words,
443 * given a {@link FileDescriptor} {@code file}, the following is true:
445 * for all i in [0, file.getMessageTypeCount()):
446 * file.getMessageType(i).getIndex() == i
448 * Similarly, for a {@link Descriptor} {@code messageType}:
450 * for all i in [0, messageType.getNestedTypeCount()):
451 * messageType.getNestedType(i).getIndex() == i
454 public int getIndex() { return index; }
456 /** Convert the descriptor to its protocol message representation. */
457 public DescriptorProto toProto() { return proto; }
459 /** Get the type's unqualified name. */
460 public String getName() { return proto.getName(); }
463 * Get the type's fully-qualified name, within the proto language's
464 * namespace. This differs from the Java name. For example, given this
468 * option java_package = "com.example.protos"
471 * {@code Baz}'s full name is "foo.bar.Baz".
473 public String getFullName() { return fullName; }
475 /** Get the {@link FileDescriptor} containing this descriptor. */
476 public FileDescriptor getFile() { return file; }
478 /** If this is a nested type, get the outer descriptor, otherwise null. */
479 public Descriptor getContainingType() { return containingType; }
481 /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
482 public MessageOptions getOptions() { return proto.getOptions(); }
484 /** Get a list of this message type's fields. */
485 public List<FieldDescriptor> getFields() {
486 return Collections.unmodifiableList(Arrays.asList(fields));
489 /** Get a list of this message type's extensions. */
490 public List<FieldDescriptor> getExtensions() {
491 return Collections.unmodifiableList(Arrays.asList(extensions));
494 /** Get a list of message types nested within this one. */
495 public List<Descriptor> getNestedTypes() {
496 return Collections.unmodifiableList(Arrays.asList(nestedTypes));
499 /** Get a list of enum types nested within this one. */
500 public List<EnumDescriptor> getEnumTypes() {
501 return Collections.unmodifiableList(Arrays.asList(enumTypes));
504 /** Determines if the given field number is an extension. */
505 public boolean isExtensionNumber(final int number) {
506 for (final DescriptorProto.ExtensionRange range :
507 proto.getExtensionRangeList()) {
508 if (range.getStart() <= number && number < range.getEnd()) {
516 * Finds a field by name.
517 * @param name The unqualified name of the field (e.g. "foo").
518 * @return The field's descriptor, or {@code null} if not found.
520 public FieldDescriptor findFieldByName(final String name) {
521 final GenericDescriptor result =
522 file.pool.findSymbol(fullName + '.' + name);
523 if (result != null && result instanceof FieldDescriptor) {
524 return (FieldDescriptor)result;
531 * Finds a field by field number.
532 * @param number The field number within this message type.
533 * @return The field's descriptor, or {@code null} if not found.
535 public FieldDescriptor findFieldByNumber(final int number) {
536 return file.pool.fieldsByNumber.get(
537 new DescriptorPool.DescriptorIntPair(this, number));
541 * Finds a nested message type by name.
542 * @param name The unqualified name of the nested type (e.g. "Foo").
543 * @return The types's descriptor, or {@code null} if not found.
545 public Descriptor findNestedTypeByName(final String name) {
546 final GenericDescriptor result =
547 file.pool.findSymbol(fullName + '.' + name);
548 if (result != null && result instanceof Descriptor) {
549 return (Descriptor)result;
556 * Finds a nested enum type by name.
557 * @param name The unqualified name of the nested type (e.g. "Foo").
558 * @return The types's descriptor, or {@code null} if not found.
560 public EnumDescriptor findEnumTypeByName(final String name) {
561 final GenericDescriptor result =
562 file.pool.findSymbol(fullName + '.' + name);
563 if (result != null && result instanceof EnumDescriptor) {
564 return (EnumDescriptor)result;
570 private final int index;
571 private DescriptorProto proto;
572 private final String fullName;
573 private final FileDescriptor file;
574 private final Descriptor containingType;
575 private final Descriptor[] nestedTypes;
576 private final EnumDescriptor[] enumTypes;
577 private final FieldDescriptor[] fields;
578 private final FieldDescriptor[] extensions;
580 private Descriptor(final DescriptorProto proto,
581 final FileDescriptor file,
582 final Descriptor parent,
584 throws DescriptorValidationException {
587 fullName = computeFullName(file, parent, proto.getName());
589 containingType = parent;
591 nestedTypes = new Descriptor[proto.getNestedTypeCount()];
592 for (int i = 0; i < proto.getNestedTypeCount(); i++) {
593 nestedTypes[i] = new Descriptor(
594 proto.getNestedType(i), file, this, i);
597 enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
598 for (int i = 0; i < proto.getEnumTypeCount(); i++) {
599 enumTypes[i] = new EnumDescriptor(
600 proto.getEnumType(i), file, this, i);
603 fields = new FieldDescriptor[proto.getFieldCount()];
604 for (int i = 0; i < proto.getFieldCount(); i++) {
605 fields[i] = new FieldDescriptor(
606 proto.getField(i), file, this, i, false);
609 extensions = new FieldDescriptor[proto.getExtensionCount()];
610 for (int i = 0; i < proto.getExtensionCount(); i++) {
611 extensions[i] = new FieldDescriptor(
612 proto.getExtension(i), file, this, i, true);
615 file.pool.addSymbol(this);
618 /** Look up and cross-link all field types, etc. */
619 private void crossLink() throws DescriptorValidationException {
620 for (final Descriptor nestedType : nestedTypes) {
621 nestedType.crossLink();
624 for (final FieldDescriptor field : fields) {
628 for (final FieldDescriptor extension : extensions) {
629 extension.crossLink();
633 /** See {@link FileDescriptor#setProto}. */
634 private void setProto(final DescriptorProto proto) {
637 for (int i = 0; i < nestedTypes.length; i++) {
638 nestedTypes[i].setProto(proto.getNestedType(i));
641 for (int i = 0; i < enumTypes.length; i++) {
642 enumTypes[i].setProto(proto.getEnumType(i));
645 for (int i = 0; i < fields.length; i++) {
646 fields[i].setProto(proto.getField(i));
649 for (int i = 0; i < extensions.length; i++) {
650 extensions[i].setProto(proto.getExtension(i));
655 // =================================================================
657 /** Describes a field of a message type. */
658 public static final class FieldDescriptor
659 implements GenericDescriptor, Comparable<FieldDescriptor>,
660 FieldSet.FieldDescriptorLite<FieldDescriptor> {
662 * Get the index of this descriptor within its parent.
663 * @see Descriptors.Descriptor#getIndex()
665 public int getIndex() { return index; }
667 /** Convert the descriptor to its protocol message representation. */
668 public FieldDescriptorProto toProto() { return proto; }
670 /** Get the field's unqualified name. */
671 public String getName() { return proto.getName(); }
673 /** Get the field's number. */
674 public int getNumber() { return proto.getNumber(); }
677 * Get the field's fully-qualified name.
678 * @see Descriptors.Descriptor#getFullName()
680 public String getFullName() { return fullName; }
683 * Get the field's java type. This is just for convenience. Every
684 * {@code FieldDescriptorProto.Type} maps to exactly one Java type.
686 public JavaType getJavaType() { return type.getJavaType(); }
688 /** For internal use only. */
689 public WireFormat.JavaType getLiteJavaType() {
690 return getLiteType().getJavaType();
693 /** Get the {@code FileDescriptor} containing this descriptor. */
694 public FileDescriptor getFile() { return file; }
696 /** Get the field's declared type. */
697 public Type getType() { return type; }
699 /** For internal use only. */
700 public WireFormat.FieldType getLiteType() {
701 return table[type.ordinal()];
703 // I'm pretty sure values() constructs a new array every time, since there
704 // is nothing stopping the caller from mutating the array. Therefore we
705 // make a static copy here.
706 private static final WireFormat.FieldType[] table =
707 WireFormat.FieldType.values();
709 /** Is this field declared required? */
710 public boolean isRequired() {
711 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED;
714 /** Is this field declared optional? */
715 public boolean isOptional() {
716 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL;
719 /** Is this field declared repeated? */
720 public boolean isRepeated() {
721 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
724 /** Does this field have the {@code [packed = true]} option? */
725 public boolean isPacked() {
726 return getOptions().getPacked();
729 /** Can this field be packed? i.e. is it a repeated primitive field? */
730 public boolean isPackable() {
731 return isRepeated() && getLiteType().isPackable();
734 /** Returns true if the field had an explicitly-defined default value. */
735 public boolean hasDefaultValue() { return proto.hasDefaultValue(); }
738 * Returns the field's default value. Valid for all types except for
739 * messages and groups. For all other types, the object returned is of
740 * the same class that would returned by Message.getField(this).
742 public Object getDefaultValue() {
743 if (getJavaType() == JavaType.MESSAGE) {
744 throw new UnsupportedOperationException(
745 "FieldDescriptor.getDefaultValue() called on an embedded message " +
751 /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
752 public FieldOptions getOptions() { return proto.getOptions(); }
754 /** Is this field an extension? */
755 public boolean isExtension() { return proto.hasExtendee(); }
758 * Get the field's containing type. For extensions, this is the type being
759 * extended, not the location where the extension was defined. See
760 * {@link #getExtensionScope()}.
762 public Descriptor getContainingType() { return containingType; }
765 * For extensions defined nested within message types, gets the outer
766 * type. Not valid for non-extension fields. For example, consider
767 * this {@code .proto} file:
770 * extensions 1000 to max;
773 * optional int32 baz = 1234;
777 * optional int32 qux = 4321;
781 * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}.
782 * However, {@code baz}'s extension scope is {@code null} while
783 * {@code qux}'s extension scope is {@code Bar}.
785 public Descriptor getExtensionScope() {
786 if (!isExtension()) {
787 throw new UnsupportedOperationException(
788 "This field is not an extension.");
790 return extensionScope;
793 /** For embedded message and group fields, gets the field's type. */
794 public Descriptor getMessageType() {
795 if (getJavaType() != JavaType.MESSAGE) {
796 throw new UnsupportedOperationException(
797 "This field is not of message type.");
802 /** For enum fields, gets the field's type. */
803 public EnumDescriptor getEnumType() {
804 if (getJavaType() != JavaType.ENUM) {
805 throw new UnsupportedOperationException(
806 "This field is not of enum type.");
812 * Compare with another {@code FieldDescriptor}. This orders fields in
813 * "canonical" order, which simply means ascending order by field number.
814 * {@code other} must be a field of the same type -- i.e.
815 * {@code getContainingType()} must return the same {@code Descriptor} for
818 * @return negative, zero, or positive if {@code this} is less than,
819 * equal to, or greater than {@code other}, respectively.
821 public int compareTo(final FieldDescriptor other) {
822 if (other.containingType != containingType) {
823 throw new IllegalArgumentException(
824 "FieldDescriptors can only be compared to other FieldDescriptors " +
825 "for fields of the same message type.");
827 return getNumber() - other.getNumber();
830 private final int index;
832 private FieldDescriptorProto proto;
833 private final String fullName;
834 private final FileDescriptor file;
835 private final Descriptor extensionScope;
837 // Possibly initialized during cross-linking.
839 private Descriptor containingType;
840 private Descriptor messageType;
841 private EnumDescriptor enumType;
842 private Object defaultValue;
845 DOUBLE (JavaType.DOUBLE ),
846 FLOAT (JavaType.FLOAT ),
847 INT64 (JavaType.LONG ),
848 UINT64 (JavaType.LONG ),
849 INT32 (JavaType.INT ),
850 FIXED64 (JavaType.LONG ),
851 FIXED32 (JavaType.INT ),
852 BOOL (JavaType.BOOLEAN ),
853 STRING (JavaType.STRING ),
854 GROUP (JavaType.MESSAGE ),
855 MESSAGE (JavaType.MESSAGE ),
856 BYTES (JavaType.BYTE_STRING),
857 UINT32 (JavaType.INT ),
858 ENUM (JavaType.ENUM ),
859 SFIXED32(JavaType.INT ),
860 SFIXED64(JavaType.LONG ),
861 SINT32 (JavaType.INT ),
862 SINT64 (JavaType.LONG );
864 Type(final JavaType javaType) {
865 this.javaType = javaType;
868 private JavaType javaType;
870 public FieldDescriptorProto.Type toProto() {
871 return FieldDescriptorProto.Type.valueOf(ordinal() + 1);
873 public JavaType getJavaType() { return javaType; }
875 public static Type valueOf(final FieldDescriptorProto.Type type) {
876 return values()[type.getNumber() - 1];
881 // Refuse to init if someone added a new declared type.
882 if (Type.values().length != FieldDescriptorProto.Type.values().length) {
883 throw new RuntimeException(
884 "descriptor.proto has a new declared type but Desrciptors.java " +
889 public enum JavaType {
896 BYTE_STRING(ByteString.EMPTY),
900 JavaType(final Object defaultDefault) {
901 this.defaultDefault = defaultDefault;
905 * The default default value for fields of this type, if it's a primitive
906 * type. This is meant for use inside this file only, hence is private.
908 private final Object defaultDefault;
911 private FieldDescriptor(final FieldDescriptorProto proto,
912 final FileDescriptor file,
913 final Descriptor parent,
915 final boolean isExtension)
916 throws DescriptorValidationException {
919 fullName = computeFullName(file, parent, proto.getName());
922 if (proto.hasType()) {
923 type = Type.valueOf(proto.getType());
926 if (getNumber() <= 0) {
927 throw new DescriptorValidationException(this,
928 "Field numbers must be positive integers.");
931 // Only repeated primitive fields may be packed.
932 if (proto.getOptions().getPacked() && !isPackable()) {
933 throw new DescriptorValidationException(this,
934 "[packed = true] can only be specified for repeated primitive " +
939 if (!proto.hasExtendee()) {
940 throw new DescriptorValidationException(this,
941 "FieldDescriptorProto.extendee not set for extension field.");
943 containingType = null; // Will be filled in when cross-linking
944 if (parent != null) {
945 extensionScope = parent;
947 extensionScope = null;
950 if (proto.hasExtendee()) {
951 throw new DescriptorValidationException(this,
952 "FieldDescriptorProto.extendee set for non-extension field.");
954 containingType = parent;
955 extensionScope = null;
958 file.pool.addSymbol(this);
961 /** Look up and cross-link all field types, etc. */
962 private void crossLink() throws DescriptorValidationException {
963 if (proto.hasExtendee()) {
964 final GenericDescriptor extendee =
965 file.pool.lookupSymbol(proto.getExtendee(), this,
966 DescriptorPool.SearchFilter.TYPES_ONLY);
967 if (!(extendee instanceof Descriptor)) {
968 throw new DescriptorValidationException(this,
969 '\"' + proto.getExtendee() + "\" is not a message type.");
971 containingType = (Descriptor)extendee;
973 if (!getContainingType().isExtensionNumber(getNumber())) {
974 throw new DescriptorValidationException(this,
975 '\"' + getContainingType().getFullName() +
976 "\" does not declare " + getNumber() +
977 " as an extension number.");
981 if (proto.hasTypeName()) {
982 final GenericDescriptor typeDescriptor =
983 file.pool.lookupSymbol(proto.getTypeName(), this,
984 DescriptorPool.SearchFilter.TYPES_ONLY);
986 if (!proto.hasType()) {
987 // Choose field type based on symbol.
988 if (typeDescriptor instanceof Descriptor) {
990 } else if (typeDescriptor instanceof EnumDescriptor) {
993 throw new DescriptorValidationException(this,
994 '\"' + proto.getTypeName() + "\" is not a type.");
998 if (getJavaType() == JavaType.MESSAGE) {
999 if (!(typeDescriptor instanceof Descriptor)) {
1000 throw new DescriptorValidationException(this,
1001 '\"' + proto.getTypeName() + "\" is not a message type.");
1003 messageType = (Descriptor)typeDescriptor;
1005 if (proto.hasDefaultValue()) {
1006 throw new DescriptorValidationException(this,
1007 "Messages can't have default values.");
1009 } else if (getJavaType() == JavaType.ENUM) {
1010 if (!(typeDescriptor instanceof EnumDescriptor)) {
1011 throw new DescriptorValidationException(this,
1012 '\"' + proto.getTypeName() + "\" is not an enum type.");
1014 enumType = (EnumDescriptor)typeDescriptor;
1016 throw new DescriptorValidationException(this,
1017 "Field with primitive type has type_name.");
1020 if (getJavaType() == JavaType.MESSAGE ||
1021 getJavaType() == JavaType.ENUM) {
1022 throw new DescriptorValidationException(this,
1023 "Field with message or enum type missing type_name.");
1027 // We don't attempt to parse the default value until here because for
1028 // enums we need the enum type's descriptor.
1029 if (proto.hasDefaultValue()) {
1031 throw new DescriptorValidationException(this,
1032 "Repeated fields cannot have default values.");
1036 switch (getType()) {
1040 defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
1044 defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
1049 defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
1053 defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
1056 if (proto.getDefaultValue().equals("inf")) {
1057 defaultValue = Float.POSITIVE_INFINITY;
1058 } else if (proto.getDefaultValue().equals("-inf")) {
1059 defaultValue = Float.NEGATIVE_INFINITY;
1060 } else if (proto.getDefaultValue().equals("nan")) {
1061 defaultValue = Float.NaN;
1063 defaultValue = Float.valueOf(proto.getDefaultValue());
1067 if (proto.getDefaultValue().equals("inf")) {
1068 defaultValue = Double.POSITIVE_INFINITY;
1069 } else if (proto.getDefaultValue().equals("-inf")) {
1070 defaultValue = Double.NEGATIVE_INFINITY;
1071 } else if (proto.getDefaultValue().equals("nan")) {
1072 defaultValue = Double.NaN;
1074 defaultValue = Double.valueOf(proto.getDefaultValue());
1078 defaultValue = Boolean.valueOf(proto.getDefaultValue());
1081 defaultValue = proto.getDefaultValue();
1086 TextFormat.unescapeBytes(proto.getDefaultValue());
1087 } catch (TextFormat.InvalidEscapeSequenceException e) {
1088 throw new DescriptorValidationException(this,
1089 "Couldn't parse default value: " + e.getMessage(), e);
1093 defaultValue = enumType.findValueByName(proto.getDefaultValue());
1094 if (defaultValue == null) {
1095 throw new DescriptorValidationException(this,
1096 "Unknown enum default value: \"" +
1097 proto.getDefaultValue() + '\"');
1102 throw new DescriptorValidationException(this,
1103 "Message type had default value.");
1105 } catch (NumberFormatException e) {
1106 throw new DescriptorValidationException(this,
1107 "Could not parse default value: \"" +
1108 proto.getDefaultValue() + '\"', e);
1111 // Determine the default default for this field.
1113 defaultValue = Collections.emptyList();
1115 switch (getJavaType()) {
1117 // We guarantee elsewhere that an enum type always has at least
1118 // one possible value.
1119 defaultValue = enumType.getValues().get(0);
1122 defaultValue = null;
1125 defaultValue = getJavaType().defaultDefault;
1131 if (!isExtension()) {
1132 file.pool.addFieldByNumber(this);
1135 if (containingType != null &&
1136 containingType.getOptions().getMessageSetWireFormat()) {
1137 if (isExtension()) {
1138 if (!isOptional() || getType() != Type.MESSAGE) {
1139 throw new DescriptorValidationException(this,
1140 "Extensions of MessageSets must be optional messages.");
1143 throw new DescriptorValidationException(this,
1144 "MessageSets cannot have fields, only extensions.");
1149 /** See {@link FileDescriptor#setProto}. */
1150 private void setProto(final FieldDescriptorProto proto) {
1155 * For internal use only. This is to satisfy the FieldDescriptorLite
1158 public MessageLite.Builder internalMergeFrom(
1159 MessageLite.Builder to, MessageLite from) {
1160 // FieldDescriptors are only used with non-lite messages so we can just
1161 // down-cast and call mergeFrom directly.
1162 return ((Message.Builder) to).mergeFrom((Message) from);
1166 // =================================================================
1168 /** Describes an enum type. */
1169 public static final class EnumDescriptor
1170 implements GenericDescriptor, Internal.EnumLiteMap<EnumValueDescriptor> {
1172 * Get the index of this descriptor within its parent.
1173 * @see Descriptors.Descriptor#getIndex()
1175 public int getIndex() { return index; }
1177 /** Convert the descriptor to its protocol message representation. */
1178 public EnumDescriptorProto toProto() { return proto; }
1180 /** Get the type's unqualified name. */
1181 public String getName() { return proto.getName(); }
1184 * Get the type's fully-qualified name.
1185 * @see Descriptors.Descriptor#getFullName()
1187 public String getFullName() { return fullName; }
1189 /** Get the {@link FileDescriptor} containing this descriptor. */
1190 public FileDescriptor getFile() { return file; }
1192 /** If this is a nested type, get the outer descriptor, otherwise null. */
1193 public Descriptor getContainingType() { return containingType; }
1195 /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
1196 public EnumOptions getOptions() { return proto.getOptions(); }
1198 /** Get a list of defined values for this enum. */
1199 public List<EnumValueDescriptor> getValues() {
1200 return Collections.unmodifiableList(Arrays.asList(values));
1204 * Find an enum value by name.
1205 * @param name The unqualified name of the value (e.g. "FOO").
1206 * @return the value's descriptor, or {@code null} if not found.
1208 public EnumValueDescriptor findValueByName(final String name) {
1209 final GenericDescriptor result =
1210 file.pool.findSymbol(fullName + '.' + name);
1211 if (result != null && result instanceof EnumValueDescriptor) {
1212 return (EnumValueDescriptor)result;
1219 * Find an enum value by number. If multiple enum values have the same
1220 * number, this returns the first defined value with that number.
1221 * @param number The value's number.
1222 * @return the value's descriptor, or {@code null} if not found.
1224 public EnumValueDescriptor findValueByNumber(final int number) {
1225 return file.pool.enumValuesByNumber.get(
1226 new DescriptorPool.DescriptorIntPair(this, number));
1229 private final int index;
1230 private EnumDescriptorProto proto;
1231 private final String fullName;
1232 private final FileDescriptor file;
1233 private final Descriptor containingType;
1234 private EnumValueDescriptor[] values;
1236 private EnumDescriptor(final EnumDescriptorProto proto,
1237 final FileDescriptor file,
1238 final Descriptor parent,
1240 throws DescriptorValidationException {
1243 fullName = computeFullName(file, parent, proto.getName());
1245 containingType = parent;
1247 if (proto.getValueCount() == 0) {
1248 // We cannot allow enums with no values because this would mean there
1249 // would be no valid default value for fields of this type.
1250 throw new DescriptorValidationException(this,
1251 "Enums must contain at least one value.");
1254 values = new EnumValueDescriptor[proto.getValueCount()];
1255 for (int i = 0; i < proto.getValueCount(); i++) {
1256 values[i] = new EnumValueDescriptor(
1257 proto.getValue(i), file, this, i);
1260 file.pool.addSymbol(this);
1263 /** See {@link FileDescriptor#setProto}. */
1264 private void setProto(final EnumDescriptorProto proto) {
1267 for (int i = 0; i < values.length; i++) {
1268 values[i].setProto(proto.getValue(i));
1273 // =================================================================
1276 * Describes one value within an enum type. Note that multiple defined
1277 * values may have the same number. In generated Java code, all values
1278 * with the same number after the first become aliases of the first.
1279 * However, they still have independent EnumValueDescriptors.
1281 public static final class EnumValueDescriptor
1282 implements GenericDescriptor, Internal.EnumLite {
1284 * Get the index of this descriptor within its parent.
1285 * @see Descriptors.Descriptor#getIndex()
1287 public int getIndex() { return index; }
1289 /** Convert the descriptor to its protocol message representation. */
1290 public EnumValueDescriptorProto toProto() { return proto; }
1292 /** Get the value's unqualified name. */
1293 public String getName() { return proto.getName(); }
1295 /** Get the value's number. */
1296 public int getNumber() { return proto.getNumber(); }
1299 * Get the value's fully-qualified name.
1300 * @see Descriptors.Descriptor#getFullName()
1302 public String getFullName() { return fullName; }
1304 /** Get the {@link FileDescriptor} containing this descriptor. */
1305 public FileDescriptor getFile() { return file; }
1307 /** Get the value's enum type. */
1308 public EnumDescriptor getType() { return type; }
1311 * Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}.
1313 public EnumValueOptions getOptions() { return proto.getOptions(); }
1315 private final int index;
1316 private EnumValueDescriptorProto proto;
1317 private final String fullName;
1318 private final FileDescriptor file;
1319 private final EnumDescriptor type;
1321 private EnumValueDescriptor(final EnumValueDescriptorProto proto,
1322 final FileDescriptor file,
1323 final EnumDescriptor parent,
1325 throws DescriptorValidationException {
1331 fullName = parent.getFullName() + '.' + proto.getName();
1333 file.pool.addSymbol(this);
1334 file.pool.addEnumValueByNumber(this);
1337 /** See {@link FileDescriptor#setProto}. */
1338 private void setProto(final EnumValueDescriptorProto proto) {
1343 // =================================================================
1345 /** Describes a service type. */
1346 public static final class ServiceDescriptor implements GenericDescriptor {
1348 * Get the index of this descriptor within its parent.
1349 * * @see Descriptors.Descriptor#getIndex()
1351 public int getIndex() { return index; }
1353 /** Convert the descriptor to its protocol message representation. */
1354 public ServiceDescriptorProto toProto() { return proto; }
1356 /** Get the type's unqualified name. */
1357 public String getName() { return proto.getName(); }
1360 * Get the type's fully-qualified name.
1361 * @see Descriptors.Descriptor#getFullName()
1363 public String getFullName() { return fullName; }
1365 /** Get the {@link FileDescriptor} containing this descriptor. */
1366 public FileDescriptor getFile() { return file; }
1368 /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
1369 public ServiceOptions getOptions() { return proto.getOptions(); }
1371 /** Get a list of methods for this service. */
1372 public List<MethodDescriptor> getMethods() {
1373 return Collections.unmodifiableList(Arrays.asList(methods));
1377 * Find a method by name.
1378 * @param name The unqualified name of the method (e.g. "Foo").
1379 * @return the method's descriptor, or {@code null} if not found.
1381 public MethodDescriptor findMethodByName(final String name) {
1382 final GenericDescriptor result =
1383 file.pool.findSymbol(fullName + '.' + name);
1384 if (result != null && result instanceof MethodDescriptor) {
1385 return (MethodDescriptor)result;
1391 private final int index;
1392 private ServiceDescriptorProto proto;
1393 private final String fullName;
1394 private final FileDescriptor file;
1395 private MethodDescriptor[] methods;
1397 private ServiceDescriptor(final ServiceDescriptorProto proto,
1398 final FileDescriptor file,
1400 throws DescriptorValidationException {
1403 fullName = computeFullName(file, null, proto.getName());
1406 methods = new MethodDescriptor[proto.getMethodCount()];
1407 for (int i = 0; i < proto.getMethodCount(); i++) {
1408 methods[i] = new MethodDescriptor(
1409 proto.getMethod(i), file, this, i);
1412 file.pool.addSymbol(this);
1415 private void crossLink() throws DescriptorValidationException {
1416 for (final MethodDescriptor method : methods) {
1421 /** See {@link FileDescriptor#setProto}. */
1422 private void setProto(final ServiceDescriptorProto proto) {
1425 for (int i = 0; i < methods.length; i++) {
1426 methods[i].setProto(proto.getMethod(i));
1431 // =================================================================
1434 * Describes one method within a service type.
1436 public static final class MethodDescriptor implements GenericDescriptor {
1438 * Get the index of this descriptor within its parent.
1439 * * @see Descriptors.Descriptor#getIndex()
1441 public int getIndex() { return index; }
1443 /** Convert the descriptor to its protocol message representation. */
1444 public MethodDescriptorProto toProto() { return proto; }
1446 /** Get the method's unqualified name. */
1447 public String getName() { return proto.getName(); }
1450 * Get the method's fully-qualified name.
1451 * @see Descriptors.Descriptor#getFullName()
1453 public String getFullName() { return fullName; }
1455 /** Get the {@link FileDescriptor} containing this descriptor. */
1456 public FileDescriptor getFile() { return file; }
1458 /** Get the method's service type. */
1459 public ServiceDescriptor getService() { return service; }
1461 /** Get the method's input type. */
1462 public Descriptor getInputType() { return inputType; }
1464 /** Get the method's output type. */
1465 public Descriptor getOutputType() { return outputType; }
1468 * Get the {@code MethodOptions}, defined in {@code descriptor.proto}.
1470 public MethodOptions getOptions() { return proto.getOptions(); }
1472 private final int index;
1473 private MethodDescriptorProto proto;
1474 private final String fullName;
1475 private final FileDescriptor file;
1476 private final ServiceDescriptor service;
1478 // Initialized during cross-linking.
1479 private Descriptor inputType;
1480 private Descriptor outputType;
1482 private MethodDescriptor(final MethodDescriptorProto proto,
1483 final FileDescriptor file,
1484 final ServiceDescriptor parent,
1486 throws DescriptorValidationException {
1492 fullName = parent.getFullName() + '.' + proto.getName();
1494 file.pool.addSymbol(this);
1497 private void crossLink() throws DescriptorValidationException {
1498 final GenericDescriptor input =
1499 file.pool.lookupSymbol(proto.getInputType(), this,
1500 DescriptorPool.SearchFilter.TYPES_ONLY);
1501 if (!(input instanceof Descriptor)) {
1502 throw new DescriptorValidationException(this,
1503 '\"' + proto.getInputType() + "\" is not a message type.");
1505 inputType = (Descriptor)input;
1507 final GenericDescriptor output =
1508 file.pool.lookupSymbol(proto.getOutputType(), this,
1509 DescriptorPool.SearchFilter.TYPES_ONLY);
1510 if (!(output instanceof Descriptor)) {
1511 throw new DescriptorValidationException(this,
1512 '\"' + proto.getOutputType() + "\" is not a message type.");
1514 outputType = (Descriptor)output;
1517 /** See {@link FileDescriptor#setProto}. */
1518 private void setProto(final MethodDescriptorProto proto) {
1523 // =================================================================
1525 private static String computeFullName(final FileDescriptor file,
1526 final Descriptor parent,
1527 final String name) {
1528 if (parent != null) {
1529 return parent.getFullName() + '.' + name;
1530 } else if (file.getPackage().length() > 0) {
1531 return file.getPackage() + '.' + name;
1537 // =================================================================
1540 * All descriptors except {@code FileDescriptor} implement this to make
1541 * {@code DescriptorPool}'s life easier.
1543 private interface GenericDescriptor {
1546 String getFullName();
1547 FileDescriptor getFile();
1551 * Thrown when building descriptors fails because the source DescriptorProtos
1554 public static class DescriptorValidationException extends Exception {
1555 private static final long serialVersionUID = 5750205775490483148L;
1557 /** Gets the full name of the descriptor where the error occurred. */
1558 public String getProblemSymbolName() { return name; }
1561 * Gets the protocol message representation of the invalid descriptor.
1563 public Message getProblemProto() { return proto; }
1566 * Gets a human-readable description of the error.
1568 public String getDescription() { return description; }
1570 private final String name;
1571 private final Message proto;
1572 private final String description;
1574 private DescriptorValidationException(
1575 final GenericDescriptor problemDescriptor,
1576 final String description) {
1577 super(problemDescriptor.getFullName() + ": " + description);
1579 // Note that problemDescriptor may be partially uninitialized, so we
1580 // don't want to expose it directly to the user. So, we only provide
1581 // the name and the original proto.
1582 name = problemDescriptor.getFullName();
1583 proto = problemDescriptor.toProto();
1584 this.description = description;
1587 private DescriptorValidationException(
1588 final GenericDescriptor problemDescriptor,
1589 final String description,
1590 final Throwable cause) {
1591 this(problemDescriptor, description);
1595 private DescriptorValidationException(
1596 final FileDescriptor problemDescriptor,
1597 final String description) {
1598 super(problemDescriptor.getName() + ": " + description);
1600 // Note that problemDescriptor may be partially uninitialized, so we
1601 // don't want to expose it directly to the user. So, we only provide
1602 // the name and the original proto.
1603 name = problemDescriptor.getName();
1604 proto = problemDescriptor.toProto();
1605 this.description = description;
1609 // =================================================================
1612 * A private helper class which contains lookup tables containing all the
1613 * descriptors defined in a particular file.
1615 private static final class DescriptorPool {
1617 /** Defines what subclass of descriptors to search in the descriptor pool.
1620 TYPES_ONLY, AGGREGATES_ONLY, ALL_SYMBOLS
1623 DescriptorPool(final FileDescriptor[] dependencies) {
1624 this.dependencies = new HashSet<FileDescriptor>();
1626 for (int i = 0; i < dependencies.length; i++) {
1627 this.dependencies.add(dependencies[i]);
1628 importPublicDependencies(dependencies[i]);
1631 for (final FileDescriptor dependency : this.dependencies) {
1633 addPackage(dependency.getPackage(), dependency);
1634 } catch (DescriptorValidationException e) {
1635 // Can't happen, because addPackage() only fails when the name
1636 // conflicts with a non-package, but we have not yet added any
1637 // non-packages at this point.
1643 /** Find and put public dependencies of the file into dependencies set.*/
1644 private void importPublicDependencies(final FileDescriptor file) {
1645 for (FileDescriptor dependency : file.getPublicDependencies()) {
1646 if (dependencies.add(dependency)) {
1647 importPublicDependencies(dependency);
1652 private final Set<FileDescriptor> dependencies;
1654 private final Map<String, GenericDescriptor> descriptorsByName =
1655 new HashMap<String, GenericDescriptor>();
1656 private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
1657 new HashMap<DescriptorIntPair, FieldDescriptor>();
1658 private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber
1659 = new HashMap<DescriptorIntPair, EnumValueDescriptor>();
1661 /** Find a generic descriptor by fully-qualified name. */
1662 GenericDescriptor findSymbol(final String fullName) {
1663 return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
1666 /** Find a descriptor by fully-qualified name and given option to only
1667 * search valid field type descriptors.
1669 GenericDescriptor findSymbol(final String fullName,
1670 final SearchFilter filter) {
1671 GenericDescriptor result = descriptorsByName.get(fullName);
1672 if (result != null) {
1673 if ((filter==SearchFilter.ALL_SYMBOLS) ||
1674 ((filter==SearchFilter.TYPES_ONLY) && isType(result)) ||
1675 ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
1680 for (final FileDescriptor dependency : dependencies) {
1681 result = dependency.pool.descriptorsByName.get(fullName);
1682 if (result != null) {
1683 if ((filter==SearchFilter.ALL_SYMBOLS) ||
1684 ((filter==SearchFilter.TYPES_ONLY) && isType(result)) ||
1685 ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
1694 /** Checks if the descriptor is a valid type for a message field. */
1695 boolean isType(GenericDescriptor descriptor) {
1696 return (descriptor instanceof Descriptor) ||
1697 (descriptor instanceof EnumDescriptor);
1700 /** Checks if the descriptor is a valid namespace type. */
1701 boolean isAggregate(GenericDescriptor descriptor) {
1702 return (descriptor instanceof Descriptor) ||
1703 (descriptor instanceof EnumDescriptor) ||
1704 (descriptor instanceof PackageDescriptor) ||
1705 (descriptor instanceof ServiceDescriptor);
1709 * Look up a type descriptor by name, relative to some other descriptor.
1710 * The name may be fully-qualified (with a leading '.'),
1711 * partially-qualified, or unqualified. C++-like name lookup semantics
1712 * are used to search for the matching descriptor.
1714 GenericDescriptor lookupSymbol(final String name,
1715 final GenericDescriptor relativeTo,
1716 final DescriptorPool.SearchFilter filter)
1717 throws DescriptorValidationException {
1718 // TODO(kenton): This could be optimized in a number of ways.
1720 GenericDescriptor result;
1721 if (name.startsWith(".")) {
1722 // Fully-qualified name.
1723 result = findSymbol(name.substring(1), filter);
1725 // If "name" is a compound identifier, we want to search for the
1726 // first component of it, then search within it for the rest.
1727 // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
1728 // defined in multiple parent scopes, we only want to find "Bar.baz" in
1729 // the innermost one. E.g., the following should produce an error:
1730 // message Bar { message Baz {} }
1734 // optional Bar.Baz baz = 1;
1736 // So, we look for just "Foo" first, then look for "Bar.baz" within it
1738 final int firstPartLength = name.indexOf('.');
1739 final String firstPart;
1740 if (firstPartLength == -1) {
1743 firstPart = name.substring(0, firstPartLength);
1746 // We will search each parent scope of "relativeTo" looking for the
1748 final StringBuilder scopeToTry =
1749 new StringBuilder(relativeTo.getFullName());
1752 // Chop off the last component of the scope.
1753 final int dotpos = scopeToTry.lastIndexOf(".");
1755 result = findSymbol(name, filter);
1758 scopeToTry.setLength(dotpos + 1);
1760 // Append firstPart and try to find
1761 scopeToTry.append(firstPart);
1762 result = findSymbol(scopeToTry.toString(),
1763 DescriptorPool.SearchFilter.AGGREGATES_ONLY);
1765 if (result != null) {
1766 if (firstPartLength != -1) {
1767 // We only found the first part of the symbol. Now look for
1768 // the whole thing. If this fails, we *don't* want to keep
1769 // searching parent scopes.
1770 scopeToTry.setLength(dotpos + 1);
1771 scopeToTry.append(name);
1772 result = findSymbol(scopeToTry.toString(), filter);
1777 // Not found. Remove the name so we can try again.
1778 scopeToTry.setLength(dotpos);
1783 if (result == null) {
1784 throw new DescriptorValidationException(relativeTo,
1785 '\"' + name + "\" is not defined.");
1792 * Adds a symbol to the symbol table. If a symbol with the same name
1793 * already exists, throws an error.
1795 void addSymbol(final GenericDescriptor descriptor)
1796 throws DescriptorValidationException {
1797 validateSymbolName(descriptor);
1799 final String fullName = descriptor.getFullName();
1800 final int dotpos = fullName.lastIndexOf('.');
1802 final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
1804 descriptorsByName.put(fullName, old);
1806 if (descriptor.getFile() == old.getFile()) {
1808 throw new DescriptorValidationException(descriptor,
1809 '\"' + fullName + "\" is already defined.");
1811 throw new DescriptorValidationException(descriptor,
1812 '\"' + fullName.substring(dotpos + 1) +
1813 "\" is already defined in \"" +
1814 fullName.substring(0, dotpos) + "\".");
1817 throw new DescriptorValidationException(descriptor,
1818 '\"' + fullName + "\" is already defined in file \"" +
1819 old.getFile().getName() + "\".");
1825 * Represents a package in the symbol table. We use PackageDescriptors
1826 * just as placeholders so that someone cannot define, say, a message type
1827 * that has the same name as an existing package.
1829 private static final class PackageDescriptor implements GenericDescriptor {
1830 public Message toProto() { return file.toProto(); }
1831 public String getName() { return name; }
1832 public String getFullName() { return fullName; }
1833 public FileDescriptor getFile() { return file; }
1835 PackageDescriptor(final String name, final String fullName,
1836 final FileDescriptor file) {
1838 this.fullName = fullName;
1842 private final String name;
1843 private final String fullName;
1844 private final FileDescriptor file;
1848 * Adds a package to the symbol tables. If a package by the same name
1849 * already exists, that is fine, but if some other kind of symbol exists
1850 * under the same name, an exception is thrown. If the package has
1851 * multiple components, this also adds the parent package(s).
1853 void addPackage(final String fullName, final FileDescriptor file)
1854 throws DescriptorValidationException {
1855 final int dotpos = fullName.lastIndexOf('.');
1860 addPackage(fullName.substring(0, dotpos), file);
1861 name = fullName.substring(dotpos + 1);
1864 final GenericDescriptor old =
1865 descriptorsByName.put(fullName,
1866 new PackageDescriptor(name, fullName, file));
1868 descriptorsByName.put(fullName, old);
1869 if (!(old instanceof PackageDescriptor)) {
1870 throw new DescriptorValidationException(file,
1871 '\"' + name + "\" is already defined (as something other than a "
1872 + "package) in file \"" + old.getFile().getName() + "\".");
1877 /** A (GenericDescriptor, int) pair, used as a map key. */
1878 private static final class DescriptorIntPair {
1879 private final GenericDescriptor descriptor;
1880 private final int number;
1882 DescriptorIntPair(final GenericDescriptor descriptor, final int number) {
1883 this.descriptor = descriptor;
1884 this.number = number;
1888 public int hashCode() {
1889 return descriptor.hashCode() * ((1 << 16) - 1) + number;
1892 public boolean equals(final Object obj) {
1893 if (!(obj instanceof DescriptorIntPair)) {
1896 final DescriptorIntPair other = (DescriptorIntPair)obj;
1897 return descriptor == other.descriptor && number == other.number;
1902 * Adds a field to the fieldsByNumber table. Throws an exception if a
1903 * field with the same containing type and number already exists.
1905 void addFieldByNumber(final FieldDescriptor field)
1906 throws DescriptorValidationException {
1907 final DescriptorIntPair key =
1908 new DescriptorIntPair(field.getContainingType(), field.getNumber());
1909 final FieldDescriptor old = fieldsByNumber.put(key, field);
1911 fieldsByNumber.put(key, old);
1912 throw new DescriptorValidationException(field,
1913 "Field number " + field.getNumber() +
1914 "has already been used in \"" +
1915 field.getContainingType().getFullName() +
1916 "\" by field \"" + old.getName() + "\".");
1921 * Adds an enum value to the enumValuesByNumber table. If an enum value
1922 * with the same type and number already exists, does nothing. (This is
1923 * allowed; the first value define with the number takes precedence.)
1925 void addEnumValueByNumber(final EnumValueDescriptor value) {
1926 final DescriptorIntPair key =
1927 new DescriptorIntPair(value.getType(), value.getNumber());
1928 final EnumValueDescriptor old = enumValuesByNumber.put(key, value);
1930 enumValuesByNumber.put(key, old);
1931 // Not an error: Multiple enum values may have the same number, but
1932 // we only want the first one in the map.
1937 * Verifies that the descriptor's name is valid (i.e. it contains only
1938 * letters, digits, and underscores, and does not start with a digit).
1940 static void validateSymbolName(final GenericDescriptor descriptor)
1941 throws DescriptorValidationException {
1942 final String name = descriptor.getName();
1943 if (name.length() == 0) {
1944 throw new DescriptorValidationException(descriptor, "Missing name.");
1946 boolean valid = true;
1947 for (int i = 0; i < name.length(); i++) {
1948 final char c = name.charAt(i);
1949 // Non-ASCII characters are not valid in protobuf identifiers, even
1950 // if they are letters or digits.
1954 // First character must be letter or _. Subsequent characters may
1955 // be letters, numbers, or digits.
1956 if (Character.isLetter(c) || c == '_' ||
1957 (Character.isDigit(c) && i > 0)) {
1964 throw new DescriptorValidationException(descriptor,
1965 '\"' + name + "\" is not a valid identifier.");