1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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 static com.google.protobuf.Internal.checkNotNull;
35 import com.google.protobuf.DescriptorProtos.DescriptorProto;
36 import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
37 import com.google.protobuf.DescriptorProtos.EnumOptions;
38 import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto;
39 import com.google.protobuf.DescriptorProtos.EnumValueOptions;
40 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
41 import com.google.protobuf.DescriptorProtos.FieldOptions;
42 import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
43 import com.google.protobuf.DescriptorProtos.FileOptions;
44 import com.google.protobuf.DescriptorProtos.MessageOptions;
45 import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
46 import com.google.protobuf.DescriptorProtos.MethodOptions;
47 import com.google.protobuf.DescriptorProtos.OneofDescriptorProto;
48 import com.google.protobuf.DescriptorProtos.OneofOptions;
49 import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
50 import com.google.protobuf.DescriptorProtos.ServiceOptions;
51 import com.google.protobuf.Descriptors.FileDescriptor.Syntax;
52 import java.lang.ref.WeakReference;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
61 import java.util.WeakHashMap;
62 import java.util.logging.Logger;
65 * Contains a collection of classes which describe protocol message types.
67 * <p>Every message type has a {@link Descriptor}, which lists all its fields and other information
68 * about a type. You can get a message type's descriptor by calling {@code
69 * MessageType.getDescriptor()}, or (given a message object of the type) {@code
70 * message.getDescriptorForType()}. Furthermore, each message is associated with a {@link
71 * FileDescriptor} for a relevant {@code .proto} file. You can obtain it by calling {@code
72 * Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors for all the messages defined
73 * in that file, and file descriptors for all the imported {@code .proto} files.
75 * <p>Descriptors are built from DescriptorProtos, as defined in {@code
76 * google/protobuf/descriptor.proto}.
78 * @author kenton@google.com Kenton Varda
80 public final class Descriptors {
81 private static final Logger logger = Logger.getLogger(Descriptors.class.getName());
83 * Describes a {@code .proto} file, including everything defined within. That includes, in
84 * particular, descriptors for all the messages and file descriptors for all other imported {@code
85 * .proto} files (dependencies).
87 public static final class FileDescriptor extends GenericDescriptor {
88 /** Convert the descriptor to its protocol message representation. */
90 public FileDescriptorProto toProto() {
94 /** Get the file name. */
96 public String getName() {
97 return proto.getName();
100 /** Returns this object. */
102 public FileDescriptor getFile() {
106 /** Returns the same as getName(). */
108 public String getFullName() {
109 return proto.getName();
113 * Get the proto package name. This is the package name given by the {@code package} statement
114 * in the {@code .proto} file, which differs from the Java package.
116 public String getPackage() {
117 return proto.getPackage();
120 /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */
121 public FileOptions getOptions() {
122 return proto.getOptions();
125 /** Get a list of top-level message types declared in this file. */
126 public List<Descriptor> getMessageTypes() {
127 return Collections.unmodifiableList(Arrays.asList(messageTypes));
130 /** Get a list of top-level enum types declared in this file. */
131 public List<EnumDescriptor> getEnumTypes() {
132 return Collections.unmodifiableList(Arrays.asList(enumTypes));
135 /** Get a list of top-level services declared in this file. */
136 public List<ServiceDescriptor> getServices() {
137 return Collections.unmodifiableList(Arrays.asList(services));
140 /** Get a list of top-level extensions declared in this file. */
141 public List<FieldDescriptor> getExtensions() {
142 return Collections.unmodifiableList(Arrays.asList(extensions));
145 /** Get a list of this file's dependencies (imports). */
146 public List<FileDescriptor> getDependencies() {
147 return Collections.unmodifiableList(Arrays.asList(dependencies));
150 /** Get a list of this file's public dependencies (public imports). */
151 public List<FileDescriptor> getPublicDependencies() {
152 return Collections.unmodifiableList(Arrays.asList(publicDependencies));
155 /** The syntax of the .proto file. */
161 Syntax(String name) {
165 private final String name;
168 /** Get the syntax of the .proto file. */
169 public Syntax getSyntax() {
170 if (Syntax.PROTO3.name.equals(proto.getSyntax())) {
171 return Syntax.PROTO3;
173 return Syntax.PROTO2;
177 * Find a message type in the file by name. Does not find nested types.
179 * @param name The unqualified type name to look for.
180 * @return The message type's descriptor, or {@code null} if not found.
182 public Descriptor findMessageTypeByName(String name) {
183 // Don't allow looking up nested types. This will make optimization
185 if (name.indexOf('.') != -1) {
188 final String packageName = getPackage();
189 if (!packageName.isEmpty()) {
190 name = packageName + '.' + name;
192 final GenericDescriptor result = pool.findSymbol(name);
193 if (result != null && result instanceof Descriptor && result.getFile() == this) {
194 return (Descriptor) result;
201 * Find an enum type in the file by name. Does not find nested types.
203 * @param name The unqualified type name to look for.
204 * @return The enum type's descriptor, or {@code null} if not found.
206 public EnumDescriptor findEnumTypeByName(String name) {
207 // Don't allow looking up nested types. This will make optimization
209 if (name.indexOf('.') != -1) {
212 final String packageName = getPackage();
213 if (!packageName.isEmpty()) {
214 name = packageName + '.' + name;
216 final GenericDescriptor result = pool.findSymbol(name);
217 if (result != null && result instanceof EnumDescriptor && result.getFile() == this) {
218 return (EnumDescriptor) result;
225 * Find a service type in the file by name.
227 * @param name The unqualified type name to look for.
228 * @return The service type's descriptor, or {@code null} if not found.
230 public ServiceDescriptor findServiceByName(String name) {
231 // Don't allow looking up nested types. This will make optimization
233 if (name.indexOf('.') != -1) {
236 final String packageName = getPackage();
237 if (!packageName.isEmpty()) {
238 name = packageName + '.' + name;
240 final GenericDescriptor result = pool.findSymbol(name);
241 if (result != null && result instanceof ServiceDescriptor && result.getFile() == this) {
242 return (ServiceDescriptor) result;
249 * Find an extension in the file by name. Does not find extensions nested inside message types.
251 * @param name The unqualified extension name to look for.
252 * @return The extension's descriptor, or {@code null} if not found.
254 public FieldDescriptor findExtensionByName(String name) {
255 if (name.indexOf('.') != -1) {
258 final String packageName = getPackage();
259 if (!packageName.isEmpty()) {
260 name = packageName + '.' + name;
262 final GenericDescriptor result = pool.findSymbol(name);
263 if (result != null && result instanceof FieldDescriptor && result.getFile() == this) {
264 return (FieldDescriptor) result;
271 * Construct a {@code FileDescriptor}.
273 * @param proto The protocol message form of the FileDescriptor.
274 * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies.
275 * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur
276 * for a number of reasons, e.g. because a field has an undefined type or because two
277 * messages were defined with the same name.
279 public static FileDescriptor buildFrom(
280 final FileDescriptorProto proto, final FileDescriptor[] dependencies)
281 throws DescriptorValidationException {
282 return buildFrom(proto, dependencies, false);
286 * Construct a {@code FileDescriptor}.
288 * @param proto The protocol message form of the FileDescriptor.
289 * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies.
290 * @param allowUnknownDependencies If true, non-exist dependenncies will be ignored and
291 * undefined message types will be replaced with a placeholder type.
292 * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur
293 * for a number of reasons, e.g. because a field has an undefined type or because two
294 * messages were defined with the same name.
296 public static FileDescriptor buildFrom(
297 final FileDescriptorProto proto,
298 final FileDescriptor[] dependencies,
299 final boolean allowUnknownDependencies)
300 throws DescriptorValidationException {
301 // Building descriptors involves two steps: translating and linking.
302 // In the translation step (implemented by FileDescriptor's
303 // constructor), we build an object tree mirroring the
304 // FileDescriptorProto's tree and put all of the descriptors into the
305 // DescriptorPool's lookup tables. In the linking step, we look up all
306 // type references in the DescriptorPool, so that, for example, a
307 // FieldDescriptor for an embedded message contains a pointer directly
308 // to the Descriptor for that message's type. We also detect undefined
309 // types in the linking step.
310 final DescriptorPool pool = new DescriptorPool(dependencies, allowUnknownDependencies);
311 final FileDescriptor result =
312 new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
317 private static byte[] latin1Cat(final String[] strings) {
318 // Hack: We can't embed a raw byte array inside generated Java code
319 // (at least, not efficiently), but we can embed Strings. So, the
320 // protocol compiler embeds the FileDescriptorProto as a giant
321 // string literal which is passed to this function to construct the
322 // file's FileDescriptor. The string literal contains only 8-bit
323 // characters, each one representing a byte of the FileDescriptorProto's
324 // serialized form. So, if we convert it to bytes in ISO-8859-1, we
325 // should get the original bytes that we want.
326 // Literal strings are limited to 64k, so it may be split into multiple strings.
327 if (strings.length == 1) {
328 return strings[0].getBytes(Internal.ISO_8859_1);
330 StringBuilder descriptorData = new StringBuilder();
331 for (String part : strings) {
332 descriptorData.append(part);
334 return descriptorData.toString().getBytes(Internal.ISO_8859_1);
337 private static FileDescriptor[] findDescriptors(
338 final Class<?> descriptorOuterClass,
339 final String[] dependencyClassNames,
340 final String[] dependencyFileNames) {
341 List<FileDescriptor> descriptors = new ArrayList<FileDescriptor>();
342 for (int i = 0; i < dependencyClassNames.length; i++) {
344 Class<?> clazz = descriptorOuterClass.getClassLoader().loadClass(dependencyClassNames[i]);
345 descriptors.add((FileDescriptor) clazz.getField("descriptor").get(null));
346 } catch (Exception e) {
347 // We allow unknown dependencies by default. If a dependency cannot
348 // be found we only generate a warning.
349 logger.warning("Descriptors for \"" + dependencyFileNames[i] + "\" can not be found.");
352 return descriptors.toArray(new FileDescriptor[0]);
356 * This method is for backward compatibility with generated code which passed an
357 * InternalDescriptorAssigner.
360 public static void internalBuildGeneratedFileFrom(
361 final String[] descriptorDataParts,
362 final FileDescriptor[] dependencies,
363 final InternalDescriptorAssigner descriptorAssigner) {
364 final byte[] descriptorBytes = latin1Cat(descriptorDataParts);
366 FileDescriptorProto proto;
368 proto = FileDescriptorProto.parseFrom(descriptorBytes);
369 } catch (InvalidProtocolBufferException e) {
370 throw new IllegalArgumentException(
371 "Failed to parse protocol buffer descriptor for generated code.", e);
374 final FileDescriptor result;
376 // When building descriptors for generated code, we allow unknown
377 // dependencies by default.
378 result = buildFrom(proto, dependencies, true);
379 } catch (DescriptorValidationException e) {
380 throw new IllegalArgumentException(
381 "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
384 final ExtensionRegistry registry = descriptorAssigner.assignDescriptors(result);
386 if (registry != null) {
387 // We must re-parse the proto using the registry.
389 proto = FileDescriptorProto.parseFrom(descriptorBytes, registry);
390 } catch (InvalidProtocolBufferException e) {
391 throw new IllegalArgumentException(
392 "Failed to parse protocol buffer descriptor for generated code.", e);
395 result.setProto(proto);
400 * This method is to be called by generated code only. It is equivalent to {@code buildFrom}
401 * except that the {@code FileDescriptorProto} is encoded in protocol buffer wire format.
403 public static FileDescriptor internalBuildGeneratedFileFrom(
404 final String[] descriptorDataParts,
405 final FileDescriptor[] dependencies) {
406 final byte[] descriptorBytes = latin1Cat(descriptorDataParts);
408 FileDescriptorProto proto;
410 proto = FileDescriptorProto.parseFrom(descriptorBytes);
411 } catch (InvalidProtocolBufferException e) {
412 throw new IllegalArgumentException(
413 "Failed to parse protocol buffer descriptor for generated code.", e);
417 // When building descriptors for generated code, we allow unknown
418 // dependencies by default.
419 return buildFrom(proto, dependencies, true);
420 } catch (DescriptorValidationException e) {
421 throw new IllegalArgumentException(
422 "Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
427 * This method is for backward compatibility with generated code which passed an
428 * InternalDescriptorAssigner.
431 public static void internalBuildGeneratedFileFrom(
432 final String[] descriptorDataParts,
433 final Class<?> descriptorOuterClass,
434 final String[] dependencyClassNames,
435 final String[] dependencyFileNames,
436 final InternalDescriptorAssigner descriptorAssigner) {
437 FileDescriptor[] dependencies = findDescriptors(
438 descriptorOuterClass, dependencyClassNames, dependencyFileNames);
439 internalBuildGeneratedFileFrom(
440 descriptorDataParts, dependencies, descriptorAssigner);
444 * This method is to be called by generated code only. It uses Java reflection to load the
445 * dependencies' descriptors.
447 public static FileDescriptor internalBuildGeneratedFileFrom(
448 final String[] descriptorDataParts,
449 final Class<?> descriptorOuterClass,
450 final String[] dependencyClassNames,
451 final String[] dependencyFileNames) {
452 FileDescriptor[] dependencies = findDescriptors(
453 descriptorOuterClass, dependencyClassNames, dependencyFileNames);
454 return internalBuildGeneratedFileFrom(descriptorDataParts, dependencies);
458 * This method is to be called by generated code only. It is used to update the
459 * FileDescriptorProto associated with the descriptor by parsing it again with the given
460 * ExtensionRegistry. This is needed to recognize custom options.
462 public static void internalUpdateFileDescriptor(
463 final FileDescriptor descriptor, final ExtensionRegistry registry) {
464 ByteString bytes = descriptor.proto.toByteString();
465 FileDescriptorProto proto;
467 proto = FileDescriptorProto.parseFrom(bytes, registry);
468 } catch (InvalidProtocolBufferException e) {
469 throw new IllegalArgumentException(
470 "Failed to parse protocol buffer descriptor for generated code.", e);
472 descriptor.setProto(proto);
476 * This class should be used by generated code only. When calling {@link
477 * FileDescriptor#internalBuildGeneratedFileFrom}, the caller provides a callback implementing
478 * this interface. The callback is called after the FileDescriptor has been constructed, in
479 * order to assign all the global variables defined in the generated code which point at parts
480 * of the FileDescriptor. The callback returns an ExtensionRegistry which contains any
481 * extensions which might be used in the descriptor -- that is, extensions of the various
482 * "Options" messages defined in descriptor.proto. The callback may also return null to indicate
483 * that no extensions are used in the descriptor.
485 * This interface is deprecated. Use the return value of internalBuildGeneratedFrom() instead.
488 public interface InternalDescriptorAssigner {
489 ExtensionRegistry assignDescriptors(FileDescriptor root);
492 private FileDescriptorProto proto;
493 private final Descriptor[] messageTypes;
494 private final EnumDescriptor[] enumTypes;
495 private final ServiceDescriptor[] services;
496 private final FieldDescriptor[] extensions;
497 private final FileDescriptor[] dependencies;
498 private final FileDescriptor[] publicDependencies;
499 private final DescriptorPool pool;
501 private FileDescriptor(
502 final FileDescriptorProto proto,
503 final FileDescriptor[] dependencies,
504 final DescriptorPool pool,
505 boolean allowUnknownDependencies)
506 throws DescriptorValidationException {
509 this.dependencies = dependencies.clone();
510 HashMap<String, FileDescriptor> nameToFileMap = new HashMap<String, FileDescriptor>();
511 for (FileDescriptor file : dependencies) {
512 nameToFileMap.put(file.getName(), file);
514 List<FileDescriptor> publicDependencies = new ArrayList<FileDescriptor>();
515 for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
516 int index = proto.getPublicDependency(i);
517 if (index < 0 || index >= proto.getDependencyCount()) {
518 throw new DescriptorValidationException(this, "Invalid public dependency index.");
520 String name = proto.getDependency(index);
521 FileDescriptor file = nameToFileMap.get(name);
523 if (!allowUnknownDependencies) {
524 throw new DescriptorValidationException(this, "Invalid public dependency: " + name);
526 // Ignore unknown dependencies.
528 publicDependencies.add(file);
531 this.publicDependencies = new FileDescriptor[publicDependencies.size()];
532 publicDependencies.toArray(this.publicDependencies);
534 pool.addPackage(getPackage(), this);
536 messageTypes = new Descriptor[proto.getMessageTypeCount()];
537 for (int i = 0; i < proto.getMessageTypeCount(); i++) {
538 messageTypes[i] = new Descriptor(proto.getMessageType(i), this, null, i);
541 enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
542 for (int i = 0; i < proto.getEnumTypeCount(); i++) {
543 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i);
546 services = new ServiceDescriptor[proto.getServiceCount()];
547 for (int i = 0; i < proto.getServiceCount(); i++) {
548 services[i] = new ServiceDescriptor(proto.getService(i), this, i);
551 extensions = new FieldDescriptor[proto.getExtensionCount()];
552 for (int i = 0; i < proto.getExtensionCount(); i++) {
553 extensions[i] = new FieldDescriptor(proto.getExtension(i), this, null, i, true);
557 /** Create a placeholder FileDescriptor for a message Descriptor. */
558 FileDescriptor(String packageName, Descriptor message) throws DescriptorValidationException {
559 this.pool = new DescriptorPool(new FileDescriptor[0], true);
561 FileDescriptorProto.newBuilder()
562 .setName(message.getFullName() + ".placeholder.proto")
563 .setPackage(packageName)
564 .addMessageType(message.toProto())
566 this.dependencies = new FileDescriptor[0];
567 this.publicDependencies = new FileDescriptor[0];
569 messageTypes = new Descriptor[] {message};
570 enumTypes = new EnumDescriptor[0];
571 services = new ServiceDescriptor[0];
572 extensions = new FieldDescriptor[0];
574 pool.addPackage(packageName, this);
575 pool.addSymbol(message);
578 /** Look up and cross-link all field types, etc. */
579 private void crossLink() throws DescriptorValidationException {
580 for (final Descriptor messageType : messageTypes) {
581 messageType.crossLink();
584 for (final ServiceDescriptor service : services) {
588 for (final FieldDescriptor extension : extensions) {
589 extension.crossLink();
594 * Replace our {@link FileDescriptorProto} with the given one, which is identical except that it
595 * might contain extensions that weren't present in the original. This method is needed for
596 * bootstrapping when a file defines custom options. The options may be defined in the file
597 * itself, so we can't actually parse them until we've constructed the descriptors, but to
598 * construct the descriptors we have to have parsed the descriptor protos. So, we have to parse
599 * the descriptor protos a second time after constructing the descriptors.
601 private void setProto(final FileDescriptorProto proto) {
604 for (int i = 0; i < messageTypes.length; i++) {
605 messageTypes[i].setProto(proto.getMessageType(i));
608 for (int i = 0; i < enumTypes.length; i++) {
609 enumTypes[i].setProto(proto.getEnumType(i));
612 for (int i = 0; i < services.length; i++) {
613 services[i].setProto(proto.getService(i));
616 for (int i = 0; i < extensions.length; i++) {
617 extensions[i].setProto(proto.getExtension(i));
621 boolean supportsUnknownEnumValue() {
622 return getSyntax() == Syntax.PROTO3;
626 // =================================================================
628 /** Describes a message type. */
629 public static final class Descriptor extends GenericDescriptor {
631 * Get the index of this descriptor within its parent. In other words, given a {@link
632 * FileDescriptor} {@code file}, the following is true:
635 * for all i in [0, file.getMessageTypeCount()):
636 * file.getMessageType(i).getIndex() == i
639 * Similarly, for a {@link Descriptor} {@code messageType}:
642 * for all i in [0, messageType.getNestedTypeCount()):
643 * messageType.getNestedType(i).getIndex() == i
646 public int getIndex() {
650 /** Convert the descriptor to its protocol message representation. */
652 public DescriptorProto toProto() {
656 /** Get the type's unqualified name. */
658 public String getName() {
659 return proto.getName();
663 * Get the type's fully-qualified name, within the proto language's namespace. This differs from
664 * the Java name. For example, given this {@code .proto}:
668 * option java_package = "com.example.protos"
672 * {@code Baz}'s full name is "foo.bar.Baz".
675 public String getFullName() {
679 /** Get the {@link FileDescriptor} containing this descriptor. */
681 public FileDescriptor getFile() {
685 /** If this is a nested type, get the outer descriptor, otherwise null. */
686 public Descriptor getContainingType() {
687 return containingType;
690 /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */
691 public MessageOptions getOptions() {
692 return proto.getOptions();
695 /** Get a list of this message type's fields. */
696 public List<FieldDescriptor> getFields() {
697 return Collections.unmodifiableList(Arrays.asList(fields));
700 /** Get a list of this message type's oneofs. */
701 public List<OneofDescriptor> getOneofs() {
702 return Collections.unmodifiableList(Arrays.asList(oneofs));
705 /** Get a list of this message type's extensions. */
706 public List<FieldDescriptor> getExtensions() {
707 return Collections.unmodifiableList(Arrays.asList(extensions));
710 /** Get a list of message types nested within this one. */
711 public List<Descriptor> getNestedTypes() {
712 return Collections.unmodifiableList(Arrays.asList(nestedTypes));
715 /** Get a list of enum types nested within this one. */
716 public List<EnumDescriptor> getEnumTypes() {
717 return Collections.unmodifiableList(Arrays.asList(enumTypes));
720 /** Determines if the given field number is an extension. */
721 public boolean isExtensionNumber(final int number) {
722 for (final DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) {
723 if (range.getStart() <= number && number < range.getEnd()) {
730 /** Determines if the given field number is reserved. */
731 public boolean isReservedNumber(final int number) {
732 for (final DescriptorProto.ReservedRange range : proto.getReservedRangeList()) {
733 if (range.getStart() <= number && number < range.getEnd()) {
740 /** Determines if the given field name is reserved. */
741 public boolean isReservedName(final String name) {
743 for (final String reservedName : proto.getReservedNameList()) {
744 if (reservedName.equals(name)) {
752 * Indicates whether the message can be extended. That is, whether it has any "extensions x to
753 * y" ranges declared on it.
755 public boolean isExtendable() {
756 return proto.getExtensionRangeList().size() != 0;
760 * Finds a field by name.
762 * @param name The unqualified name of the field (e.g. "foo").
763 * @return The field's descriptor, or {@code null} if not found.
765 public FieldDescriptor findFieldByName(final String name) {
766 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
767 if (result != null && result instanceof FieldDescriptor) {
768 return (FieldDescriptor) result;
775 * Finds a field by field number.
777 * @param number The field number within this message type.
778 * @return The field's descriptor, or {@code null} if not found.
780 public FieldDescriptor findFieldByNumber(final int number) {
781 return file.pool.fieldsByNumber.get(new DescriptorPool.DescriptorIntPair(this, number));
785 * Finds a nested message type by name.
787 * @param name The unqualified name of the nested type (e.g. "Foo").
788 * @return The types's descriptor, or {@code null} if not found.
790 public Descriptor findNestedTypeByName(final String name) {
791 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
792 if (result != null && result instanceof Descriptor) {
793 return (Descriptor) result;
800 * Finds a nested enum type by name.
802 * @param name The unqualified name of the nested type (e.g. "Foo").
803 * @return The types's descriptor, or {@code null} if not found.
805 public EnumDescriptor findEnumTypeByName(final String name) {
806 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
807 if (result != null && result instanceof EnumDescriptor) {
808 return (EnumDescriptor) result;
814 private final int index;
815 private DescriptorProto proto;
816 private final String fullName;
817 private final FileDescriptor file;
818 private final Descriptor containingType;
819 private final Descriptor[] nestedTypes;
820 private final EnumDescriptor[] enumTypes;
821 private final FieldDescriptor[] fields;
822 private final FieldDescriptor[] extensions;
823 private final OneofDescriptor[] oneofs;
825 // Used to create a placeholder when the type cannot be found.
826 Descriptor(final String fullname) throws DescriptorValidationException {
827 String name = fullname;
828 String packageName = "";
829 int pos = fullname.lastIndexOf('.');
831 name = fullname.substring(pos + 1);
832 packageName = fullname.substring(0, pos);
836 DescriptorProto.newBuilder()
839 DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build())
841 this.fullName = fullname;
842 this.containingType = null;
844 this.nestedTypes = new Descriptor[0];
845 this.enumTypes = new EnumDescriptor[0];
846 this.fields = new FieldDescriptor[0];
847 this.extensions = new FieldDescriptor[0];
848 this.oneofs = new OneofDescriptor[0];
850 // Create a placeholder FileDescriptor to hold this message.
851 this.file = new FileDescriptor(packageName, this);
855 final DescriptorProto proto,
856 final FileDescriptor file,
857 final Descriptor parent,
859 throws DescriptorValidationException {
862 fullName = computeFullName(file, parent, proto.getName());
864 containingType = parent;
866 oneofs = new OneofDescriptor[proto.getOneofDeclCount()];
867 for (int i = 0; i < proto.getOneofDeclCount(); i++) {
868 oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i);
871 nestedTypes = new Descriptor[proto.getNestedTypeCount()];
872 for (int i = 0; i < proto.getNestedTypeCount(); i++) {
873 nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i);
876 enumTypes = new EnumDescriptor[proto.getEnumTypeCount()];
877 for (int i = 0; i < proto.getEnumTypeCount(); i++) {
878 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i);
881 fields = new FieldDescriptor[proto.getFieldCount()];
882 for (int i = 0; i < proto.getFieldCount(); i++) {
883 fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false);
886 extensions = new FieldDescriptor[proto.getExtensionCount()];
887 for (int i = 0; i < proto.getExtensionCount(); i++) {
888 extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true);
891 for (int i = 0; i < proto.getOneofDeclCount(); i++) {
892 oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()];
893 oneofs[i].fieldCount = 0;
895 for (int i = 0; i < proto.getFieldCount(); i++) {
896 OneofDescriptor oneofDescriptor = fields[i].getContainingOneof();
897 if (oneofDescriptor != null) {
898 oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
902 file.pool.addSymbol(this);
905 /** Look up and cross-link all field types, etc. */
906 private void crossLink() throws DescriptorValidationException {
907 for (final Descriptor nestedType : nestedTypes) {
908 nestedType.crossLink();
911 for (final FieldDescriptor field : fields) {
915 for (final FieldDescriptor extension : extensions) {
916 extension.crossLink();
920 /** See {@link FileDescriptor#setProto}. */
921 private void setProto(final DescriptorProto proto) {
924 for (int i = 0; i < nestedTypes.length; i++) {
925 nestedTypes[i].setProto(proto.getNestedType(i));
928 for (int i = 0; i < oneofs.length; i++) {
929 oneofs[i].setProto(proto.getOneofDecl(i));
932 for (int i = 0; i < enumTypes.length; i++) {
933 enumTypes[i].setProto(proto.getEnumType(i));
936 for (int i = 0; i < fields.length; i++) {
937 fields[i].setProto(proto.getField(i));
940 for (int i = 0; i < extensions.length; i++) {
941 extensions[i].setProto(proto.getExtension(i));
946 // =================================================================
948 /** Describes a field of a message type. */
949 public static final class FieldDescriptor extends GenericDescriptor
950 implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> {
952 * Get the index of this descriptor within its parent.
954 * @see Descriptors.Descriptor#getIndex()
956 public int getIndex() {
960 /** Convert the descriptor to its protocol message representation. */
962 public FieldDescriptorProto toProto() {
966 /** Get the field's unqualified name. */
968 public String getName() {
969 return proto.getName();
972 /** Get the field's number. */
974 public int getNumber() {
975 return proto.getNumber();
979 * Get the field's fully-qualified name.
981 * @see Descriptors.Descriptor#getFullName()
984 public String getFullName() {
988 /** Get the JSON name of this field. */
989 public String getJsonName() {
994 * Get the field's java type. This is just for convenience. Every {@code
995 * FieldDescriptorProto.Type} maps to exactly one Java type.
997 public JavaType getJavaType() {
998 return type.getJavaType();
1001 /** For internal use only. */
1003 public WireFormat.JavaType getLiteJavaType() {
1004 return getLiteType().getJavaType();
1007 /** Get the {@code FileDescriptor} containing this descriptor. */
1009 public FileDescriptor getFile() {
1013 /** Get the field's declared type. */
1014 public Type getType() {
1018 /** For internal use only. */
1020 public WireFormat.FieldType getLiteType() {
1021 return table[type.ordinal()];
1024 /** For internal use only. */
1025 public boolean needsUtf8Check() {
1026 if (type != Type.STRING) {
1029 if (getContainingType().getOptions().getMapEntry()) {
1030 // Always enforce strict UTF-8 checking for map fields.
1033 if (getFile().getSyntax() == Syntax.PROTO3) {
1036 return getFile().getOptions().getJavaStringCheckUtf8();
1039 public boolean isMapField() {
1040 return getType() == Type.MESSAGE
1042 && getMessageType().getOptions().getMapEntry();
1045 // I'm pretty sure values() constructs a new array every time, since there
1046 // is nothing stopping the caller from mutating the array. Therefore we
1047 // make a static copy here.
1048 private static final WireFormat.FieldType[] table = WireFormat.FieldType.values();
1050 /** Is this field declared required? */
1051 public boolean isRequired() {
1052 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED;
1055 /** Is this field declared optional? */
1056 public boolean isOptional() {
1057 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL;
1060 /** Is this field declared repeated? */
1062 public boolean isRepeated() {
1063 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
1067 * Does this field have the {@code [packed = true]} option or is this field packable in proto3
1068 * and not explicitly setted to unpacked?
1071 public boolean isPacked() {
1072 if (!isPackable()) {
1075 if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) {
1076 return getOptions().getPacked();
1078 return !getOptions().hasPacked() || getOptions().getPacked();
1082 /** Can this field be packed? i.e. is it a repeated primitive field? */
1083 public boolean isPackable() {
1084 return isRepeated() && getLiteType().isPackable();
1087 /** Returns true if the field had an explicitly-defined default value. */
1088 public boolean hasDefaultValue() {
1089 return proto.hasDefaultValue();
1093 * Returns the field's default value. Valid for all types except for messages and groups. For
1094 * all other types, the object returned is of the same class that would returned by
1095 * Message.getField(this).
1097 public Object getDefaultValue() {
1098 if (getJavaType() == JavaType.MESSAGE) {
1099 throw new UnsupportedOperationException(
1100 "FieldDescriptor.getDefaultValue() called on an embedded message field.");
1102 return defaultValue;
1105 /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */
1106 public FieldOptions getOptions() {
1107 return proto.getOptions();
1110 /** Is this field an extension? */
1111 public boolean isExtension() {
1112 return proto.hasExtendee();
1116 * Get the field's containing type. For extensions, this is the type being extended, not the
1117 * location where the extension was defined. See {@link #getExtensionScope()}.
1119 public Descriptor getContainingType() {
1120 return containingType;
1123 /** Get the field's containing oneof. */
1124 public OneofDescriptor getContainingOneof() {
1125 return containingOneof;
1129 * For extensions defined nested within message types, gets the outer type. Not valid for
1130 * non-extension fields. For example, consider this {@code .proto} file:
1134 * extensions 1000 to max;
1137 * optional int32 baz = 1234;
1141 * optional int32 qux = 4321;
1146 * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. However, {@code baz}'s
1147 * extension scope is {@code null} while {@code qux}'s extension scope is {@code Bar}.
1149 public Descriptor getExtensionScope() {
1150 if (!isExtension()) {
1151 throw new UnsupportedOperationException(
1152 String.format("This field is not an extension. (%s)", fullName));
1154 return extensionScope;
1157 /** For embedded message and group fields, gets the field's type. */
1158 public Descriptor getMessageType() {
1159 if (getJavaType() != JavaType.MESSAGE) {
1160 throw new UnsupportedOperationException(
1161 String.format("This field is not of message type. (%s)", fullName));
1166 /** For enum fields, gets the field's type. */
1168 public EnumDescriptor getEnumType() {
1169 if (getJavaType() != JavaType.ENUM) {
1170 throw new UnsupportedOperationException(
1171 String.format("This field is not of enum type. (%s)", fullName));
1177 * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which
1178 * simply means ascending order by field number. {@code other} must be a field of the same type
1179 * -- i.e. {@code getContainingType()} must return the same {@code Descriptor} for both fields.
1181 * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than
1182 * {@code other}, respectively.
1185 public int compareTo(final FieldDescriptor other) {
1186 if (other.containingType != containingType) {
1187 throw new IllegalArgumentException(
1188 "FieldDescriptors can only be compared to other FieldDescriptors "
1189 + "for fields of the same message type.");
1191 return getNumber() - other.getNumber();
1195 public String toString() {
1196 return getFullName();
1199 private final int index;
1201 private FieldDescriptorProto proto;
1202 private final String fullName;
1203 private final String jsonName;
1204 private final FileDescriptor file;
1205 private final Descriptor extensionScope;
1207 // Possibly initialized during cross-linking.
1209 private Descriptor containingType;
1210 private Descriptor messageType;
1211 private OneofDescriptor containingOneof;
1212 private EnumDescriptor enumType;
1213 private Object defaultValue;
1216 DOUBLE(JavaType.DOUBLE),
1217 FLOAT(JavaType.FLOAT),
1218 INT64(JavaType.LONG),
1219 UINT64(JavaType.LONG),
1220 INT32(JavaType.INT),
1221 FIXED64(JavaType.LONG),
1222 FIXED32(JavaType.INT),
1223 BOOL(JavaType.BOOLEAN),
1224 STRING(JavaType.STRING),
1225 GROUP(JavaType.MESSAGE),
1226 MESSAGE(JavaType.MESSAGE),
1227 BYTES(JavaType.BYTE_STRING),
1228 UINT32(JavaType.INT),
1229 ENUM(JavaType.ENUM),
1230 SFIXED32(JavaType.INT),
1231 SFIXED64(JavaType.LONG),
1232 SINT32(JavaType.INT),
1233 SINT64(JavaType.LONG);
1235 Type(final JavaType javaType) {
1236 this.javaType = javaType;
1239 private JavaType javaType;
1241 public FieldDescriptorProto.Type toProto() {
1242 return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
1245 public JavaType getJavaType() {
1249 public static Type valueOf(final FieldDescriptorProto.Type type) {
1250 return values()[type.getNumber() - 1];
1255 // Refuse to init if someone added a new declared type.
1256 if (Type.values().length != FieldDescriptorProto.Type.values().length) {
1257 throw new RuntimeException(
1258 "descriptor.proto has a new declared type but Descriptors.java wasn't updated.");
1262 public enum JavaType {
1269 BYTE_STRING(ByteString.EMPTY),
1273 JavaType(final Object defaultDefault) {
1274 this.defaultDefault = defaultDefault;
1278 * The default default value for fields of this type, if it's a primitive type. This is meant
1279 * for use inside this file only, hence is private.
1281 private final Object defaultDefault;
1284 // This method should match exactly with the ToJsonName() function in C++
1286 private static String fieldNameToJsonName(String name) {
1287 final int length = name.length();
1288 StringBuilder result = new StringBuilder(length);
1289 boolean isNextUpperCase = false;
1290 for (int i = 0; i < length; i++) {
1291 char ch = name.charAt(i);
1293 isNextUpperCase = true;
1294 } else if (isNextUpperCase) {
1295 // This closely matches the logic for ASCII characters in:
1296 // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689
1297 if ('a' <= ch && ch <= 'z') {
1298 ch = (char) (ch - 'a' + 'A');
1301 isNextUpperCase = false;
1306 return result.toString();
1309 private FieldDescriptor(
1310 final FieldDescriptorProto proto,
1311 final FileDescriptor file,
1312 final Descriptor parent,
1314 final boolean isExtension)
1315 throws DescriptorValidationException {
1318 fullName = computeFullName(file, parent, proto.getName());
1320 if (proto.hasJsonName()) {
1321 jsonName = proto.getJsonName();
1323 jsonName = fieldNameToJsonName(proto.getName());
1326 if (proto.hasType()) {
1327 type = Type.valueOf(proto.getType());
1330 if (getNumber() <= 0) {
1331 throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
1335 if (!proto.hasExtendee()) {
1336 throw new DescriptorValidationException(
1337 this, "FieldDescriptorProto.extendee not set for extension field.");
1339 containingType = null; // Will be filled in when cross-linking
1340 if (parent != null) {
1341 extensionScope = parent;
1343 extensionScope = null;
1346 if (proto.hasOneofIndex()) {
1347 throw new DescriptorValidationException(
1348 this, "FieldDescriptorProto.oneof_index set for extension field.");
1350 containingOneof = null;
1352 if (proto.hasExtendee()) {
1353 throw new DescriptorValidationException(
1354 this, "FieldDescriptorProto.extendee set for non-extension field.");
1356 containingType = parent;
1358 if (proto.hasOneofIndex()) {
1359 if (proto.getOneofIndex() < 0
1360 || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) {
1361 throw new DescriptorValidationException(
1363 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName());
1365 containingOneof = parent.getOneofs().get(proto.getOneofIndex());
1366 containingOneof.fieldCount++;
1368 containingOneof = null;
1370 extensionScope = null;
1373 file.pool.addSymbol(this);
1376 /** Look up and cross-link all field types, etc. */
1377 private void crossLink() throws DescriptorValidationException {
1378 if (proto.hasExtendee()) {
1379 final GenericDescriptor extendee =
1380 file.pool.lookupSymbol(
1381 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1382 if (!(extendee instanceof Descriptor)) {
1383 throw new DescriptorValidationException(
1384 this, '\"' + proto.getExtendee() + "\" is not a message type.");
1386 containingType = (Descriptor) extendee;
1388 if (!getContainingType().isExtensionNumber(getNumber())) {
1389 throw new DescriptorValidationException(
1392 + getContainingType().getFullName()
1393 + "\" does not declare "
1395 + " as an extension number.");
1399 if (proto.hasTypeName()) {
1400 final GenericDescriptor typeDescriptor =
1401 file.pool.lookupSymbol(
1402 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
1404 if (!proto.hasType()) {
1405 // Choose field type based on symbol.
1406 if (typeDescriptor instanceof Descriptor) {
1407 type = Type.MESSAGE;
1408 } else if (typeDescriptor instanceof EnumDescriptor) {
1411 throw new DescriptorValidationException(
1412 this, '\"' + proto.getTypeName() + "\" is not a type.");
1416 if (getJavaType() == JavaType.MESSAGE) {
1417 if (!(typeDescriptor instanceof Descriptor)) {
1418 throw new DescriptorValidationException(
1419 this, '\"' + proto.getTypeName() + "\" is not a message type.");
1421 messageType = (Descriptor) typeDescriptor;
1423 if (proto.hasDefaultValue()) {
1424 throw new DescriptorValidationException(this, "Messages can't have default values.");
1426 } else if (getJavaType() == JavaType.ENUM) {
1427 if (!(typeDescriptor instanceof EnumDescriptor)) {
1428 throw new DescriptorValidationException(
1429 this, '\"' + proto.getTypeName() + "\" is not an enum type.");
1431 enumType = (EnumDescriptor) typeDescriptor;
1433 throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
1436 if (getJavaType() == JavaType.MESSAGE || getJavaType() == JavaType.ENUM) {
1437 throw new DescriptorValidationException(
1438 this, "Field with message or enum type missing type_name.");
1442 // Only repeated primitive fields may be packed.
1443 if (proto.getOptions().getPacked() && !isPackable()) {
1444 throw new DescriptorValidationException(
1445 this, "[packed = true] can only be specified for repeated primitive fields.");
1448 // We don't attempt to parse the default value until here because for
1449 // enums we need the enum type's descriptor.
1450 if (proto.hasDefaultValue()) {
1452 throw new DescriptorValidationException(
1453 this, "Repeated fields cannot have default values.");
1457 switch (getType()) {
1461 defaultValue = TextFormat.parseInt32(proto.getDefaultValue());
1465 defaultValue = TextFormat.parseUInt32(proto.getDefaultValue());
1470 defaultValue = TextFormat.parseInt64(proto.getDefaultValue());
1474 defaultValue = TextFormat.parseUInt64(proto.getDefaultValue());
1477 if (proto.getDefaultValue().equals("inf")) {
1478 defaultValue = Float.POSITIVE_INFINITY;
1479 } else if (proto.getDefaultValue().equals("-inf")) {
1480 defaultValue = Float.NEGATIVE_INFINITY;
1481 } else if (proto.getDefaultValue().equals("nan")) {
1482 defaultValue = Float.NaN;
1484 defaultValue = Float.valueOf(proto.getDefaultValue());
1488 if (proto.getDefaultValue().equals("inf")) {
1489 defaultValue = Double.POSITIVE_INFINITY;
1490 } else if (proto.getDefaultValue().equals("-inf")) {
1491 defaultValue = Double.NEGATIVE_INFINITY;
1492 } else if (proto.getDefaultValue().equals("nan")) {
1493 defaultValue = Double.NaN;
1495 defaultValue = Double.valueOf(proto.getDefaultValue());
1499 defaultValue = Boolean.valueOf(proto.getDefaultValue());
1502 defaultValue = proto.getDefaultValue();
1506 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue());
1507 } catch (TextFormat.InvalidEscapeSequenceException e) {
1508 throw new DescriptorValidationException(
1509 this, "Couldn't parse default value: " + e.getMessage(), e);
1513 defaultValue = enumType.findValueByName(proto.getDefaultValue());
1514 if (defaultValue == null) {
1515 throw new DescriptorValidationException(
1516 this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"');
1521 throw new DescriptorValidationException(this, "Message type had default value.");
1523 } catch (NumberFormatException e) {
1524 throw new DescriptorValidationException(
1525 this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e);
1528 // Determine the default default for this field.
1530 defaultValue = Collections.emptyList();
1532 switch (getJavaType()) {
1534 // We guarantee elsewhere that an enum type always has at least
1535 // one possible value.
1536 defaultValue = enumType.getValues().get(0);
1539 defaultValue = null;
1542 defaultValue = getJavaType().defaultDefault;
1548 if (!isExtension()) {
1549 file.pool.addFieldByNumber(this);
1552 if (containingType != null && containingType.getOptions().getMessageSetWireFormat()) {
1553 if (isExtension()) {
1554 if (!isOptional() || getType() != Type.MESSAGE) {
1555 throw new DescriptorValidationException(
1556 this, "Extensions of MessageSets must be optional messages.");
1559 throw new DescriptorValidationException(
1560 this, "MessageSets cannot have fields, only extensions.");
1565 /** See {@link FileDescriptor#setProto}. */
1566 private void setProto(final FieldDescriptorProto proto) {
1570 /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */
1572 public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
1573 // FieldDescriptors are only used with non-lite messages so we can just
1574 // down-cast and call mergeFrom directly.
1575 return ((Message.Builder) to).mergeFrom((Message) from);
1580 // =================================================================
1582 /** Describes an enum type. */
1583 public static final class EnumDescriptor extends GenericDescriptor
1584 implements Internal.EnumLiteMap<EnumValueDescriptor> {
1586 * Get the index of this descriptor within its parent.
1588 * @see Descriptors.Descriptor#getIndex()
1590 public int getIndex() {
1594 /** Convert the descriptor to its protocol message representation. */
1596 public EnumDescriptorProto toProto() {
1600 /** Get the type's unqualified name. */
1602 public String getName() {
1603 return proto.getName();
1607 * Get the type's fully-qualified name.
1609 * @see Descriptors.Descriptor#getFullName()
1612 public String getFullName() {
1616 /** Get the {@link FileDescriptor} containing this descriptor. */
1618 public FileDescriptor getFile() {
1622 /** If this is a nested type, get the outer descriptor, otherwise null. */
1623 public Descriptor getContainingType() {
1624 return containingType;
1627 /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */
1628 public EnumOptions getOptions() {
1629 return proto.getOptions();
1632 /** Get a list of defined values for this enum. */
1633 public List<EnumValueDescriptor> getValues() {
1634 return Collections.unmodifiableList(Arrays.asList(values));
1638 * Find an enum value by name.
1640 * @param name The unqualified name of the value (e.g. "FOO").
1641 * @return the value's descriptor, or {@code null} if not found.
1643 public EnumValueDescriptor findValueByName(final String name) {
1644 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
1645 if (result != null && result instanceof EnumValueDescriptor) {
1646 return (EnumValueDescriptor) result;
1653 * Find an enum value by number. If multiple enum values have the same number, this returns the
1654 * first defined value with that number.
1656 * @param number The value's number.
1657 * @return the value's descriptor, or {@code null} if not found.
1660 public EnumValueDescriptor findValueByNumber(final int number) {
1661 return file.pool.enumValuesByNumber.get(new DescriptorPool.DescriptorIntPair(this, number));
1665 * Get the enum value for a number. If no enum value has this number, construct an
1666 * EnumValueDescriptor for it.
1668 public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) {
1669 EnumValueDescriptor result = findValueByNumber(number);
1670 if (result != null) {
1673 // The number represents an unknown enum value.
1674 synchronized (this) {
1675 // Descriptors are compared by object identity so for the same number
1676 // we need to return the same EnumValueDescriptor object. This means
1677 // we have to store created EnumValueDescriptors. However, as there
1678 // are potentially 2G unknown enum values, storing all of these
1679 // objects persistently will consume lots of memory for long-running
1680 // services and it's also unnecessary as not many EnumValueDescriptors
1681 // will be used at the same time.
1683 // To solve the problem we take advantage of Java's weak references and
1684 // rely on gc to release unused descriptors.
1686 // Here is how it works:
1687 // * We store unknown EnumValueDescriptors in a WeakHashMap with the
1688 // value being a weak reference to the descriptor.
1689 // * The descriptor holds a strong reference to the key so as long
1690 // as the EnumValueDescriptor is in use, the key will be there
1691 // and the corresponding map entry will be there. Following-up
1692 // queries with the same number will return the same descriptor.
1693 // * If the user no longer uses an unknown EnumValueDescriptor,
1694 // it will be gc-ed since we only hold a weak reference to it in
1695 // the map. The key in the corresponding map entry will also be
1696 // gc-ed as the only strong reference to it is in the descriptor
1697 // which is just gc-ed. With the key being gone WeakHashMap will
1698 // then remove the whole entry. This way unknown descriptors will
1699 // be freed automatically and we don't need to do anything to
1700 // clean-up unused map entries.
1702 // Note: We must use "new Integer(number)" here because we don't want
1703 // these Integer objects to be cached.
1704 Integer key = new Integer(number);
1705 WeakReference<EnumValueDescriptor> reference = unknownValues.get(key);
1706 if (reference != null) {
1707 result = reference.get();
1709 if (result == null) {
1710 result = new EnumValueDescriptor(file, this, key);
1711 unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result));
1717 // Used in tests only.
1718 int getUnknownEnumValueDescriptorCount() {
1719 return unknownValues.size();
1722 private final int index;
1723 private EnumDescriptorProto proto;
1724 private final String fullName;
1725 private final FileDescriptor file;
1726 private final Descriptor containingType;
1727 private EnumValueDescriptor[] values;
1728 private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues =
1729 new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>();
1731 private EnumDescriptor(
1732 final EnumDescriptorProto proto,
1733 final FileDescriptor file,
1734 final Descriptor parent,
1736 throws DescriptorValidationException {
1739 fullName = computeFullName(file, parent, proto.getName());
1741 containingType = parent;
1743 if (proto.getValueCount() == 0) {
1744 // We cannot allow enums with no values because this would mean there
1745 // would be no valid default value for fields of this type.
1746 throw new DescriptorValidationException(this, "Enums must contain at least one value.");
1749 values = new EnumValueDescriptor[proto.getValueCount()];
1750 for (int i = 0; i < proto.getValueCount(); i++) {
1751 values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i);
1754 file.pool.addSymbol(this);
1757 /** See {@link FileDescriptor#setProto}. */
1758 private void setProto(final EnumDescriptorProto proto) {
1761 for (int i = 0; i < values.length; i++) {
1762 values[i].setProto(proto.getValue(i));
1767 // =================================================================
1770 * Describes one value within an enum type. Note that multiple defined values may have the same
1771 * number. In generated Java code, all values with the same number after the first become aliases
1772 * of the first. However, they still have independent EnumValueDescriptors.
1774 public static final class EnumValueDescriptor extends GenericDescriptor
1775 implements Internal.EnumLite {
1777 * Get the index of this descriptor within its parent.
1779 * @see Descriptors.Descriptor#getIndex()
1781 public int getIndex() {
1785 /** Convert the descriptor to its protocol message representation. */
1787 public EnumValueDescriptorProto toProto() {
1791 /** Get the value's unqualified name. */
1793 public String getName() {
1794 return proto.getName();
1797 /** Get the value's number. */
1799 public int getNumber() {
1800 return proto.getNumber();
1804 public String toString() {
1805 return proto.getName();
1809 * Get the value's fully-qualified name.
1811 * @see Descriptors.Descriptor#getFullName()
1814 public String getFullName() {
1818 /** Get the {@link FileDescriptor} containing this descriptor. */
1820 public FileDescriptor getFile() {
1824 /** Get the value's enum type. */
1825 public EnumDescriptor getType() {
1829 /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */
1830 public EnumValueOptions getOptions() {
1831 return proto.getOptions();
1834 private final int index;
1835 private EnumValueDescriptorProto proto;
1836 private final String fullName;
1837 private final FileDescriptor file;
1838 private final EnumDescriptor type;
1840 private EnumValueDescriptor(
1841 final EnumValueDescriptorProto proto,
1842 final FileDescriptor file,
1843 final EnumDescriptor parent,
1845 throws DescriptorValidationException {
1851 fullName = parent.getFullName() + '.' + proto.getName();
1853 file.pool.addSymbol(this);
1854 file.pool.addEnumValueByNumber(this);
1857 // Create an unknown enum value.
1858 private EnumValueDescriptor(
1859 final FileDescriptor file, final EnumDescriptor parent, final Integer number) {
1860 String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number;
1861 EnumValueDescriptorProto proto =
1862 EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build();
1867 this.fullName = parent.getFullName() + '.' + proto.getName();
1869 // Don't add this descriptor into pool.
1872 /** See {@link FileDescriptor#setProto}. */
1873 private void setProto(final EnumValueDescriptorProto proto) {
1878 // =================================================================
1880 /** Describes a service type. */
1881 public static final class ServiceDescriptor extends GenericDescriptor {
1883 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
1885 public int getIndex() {
1889 /** Convert the descriptor to its protocol message representation. */
1891 public ServiceDescriptorProto toProto() {
1895 /** Get the type's unqualified name. */
1897 public String getName() {
1898 return proto.getName();
1902 * Get the type's fully-qualified name.
1904 * @see Descriptors.Descriptor#getFullName()
1907 public String getFullName() {
1911 /** Get the {@link FileDescriptor} containing this descriptor. */
1913 public FileDescriptor getFile() {
1917 /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
1918 public ServiceOptions getOptions() {
1919 return proto.getOptions();
1922 /** Get a list of methods for this service. */
1923 public List<MethodDescriptor> getMethods() {
1924 return Collections.unmodifiableList(Arrays.asList(methods));
1928 * Find a method by name.
1930 * @param name The unqualified name of the method (e.g. "Foo").
1931 * @return the method's descriptor, or {@code null} if not found.
1933 public MethodDescriptor findMethodByName(final String name) {
1934 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name);
1935 if (result != null && result instanceof MethodDescriptor) {
1936 return (MethodDescriptor) result;
1942 private final int index;
1943 private ServiceDescriptorProto proto;
1944 private final String fullName;
1945 private final FileDescriptor file;
1946 private MethodDescriptor[] methods;
1948 private ServiceDescriptor(
1949 final ServiceDescriptorProto proto, final FileDescriptor file, final int index)
1950 throws DescriptorValidationException {
1953 fullName = computeFullName(file, null, proto.getName());
1956 methods = new MethodDescriptor[proto.getMethodCount()];
1957 for (int i = 0; i < proto.getMethodCount(); i++) {
1958 methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i);
1961 file.pool.addSymbol(this);
1964 private void crossLink() throws DescriptorValidationException {
1965 for (final MethodDescriptor method : methods) {
1970 /** See {@link FileDescriptor#setProto}. */
1971 private void setProto(final ServiceDescriptorProto proto) {
1974 for (int i = 0; i < methods.length; i++) {
1975 methods[i].setProto(proto.getMethod(i));
1980 // =================================================================
1982 /** Describes one method within a service type. */
1983 public static final class MethodDescriptor extends GenericDescriptor {
1985 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex()
1987 public int getIndex() {
1991 /** Convert the descriptor to its protocol message representation. */
1993 public MethodDescriptorProto toProto() {
1997 /** Get the method's unqualified name. */
1999 public String getName() {
2000 return proto.getName();
2004 * Get the method's fully-qualified name.
2006 * @see Descriptors.Descriptor#getFullName()
2009 public String getFullName() {
2013 /** Get the {@link FileDescriptor} containing this descriptor. */
2015 public FileDescriptor getFile() {
2019 /** Get the method's service type. */
2020 public ServiceDescriptor getService() {
2024 /** Get the method's input type. */
2025 public Descriptor getInputType() {
2029 /** Get the method's output type. */
2030 public Descriptor getOutputType() {
2034 /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */
2035 public MethodOptions getOptions() {
2036 return proto.getOptions();
2039 private final int index;
2040 private MethodDescriptorProto proto;
2041 private final String fullName;
2042 private final FileDescriptor file;
2043 private final ServiceDescriptor service;
2045 // Initialized during cross-linking.
2046 private Descriptor inputType;
2047 private Descriptor outputType;
2049 private MethodDescriptor(
2050 final MethodDescriptorProto proto,
2051 final FileDescriptor file,
2052 final ServiceDescriptor parent,
2054 throws DescriptorValidationException {
2060 fullName = parent.getFullName() + '.' + proto.getName();
2062 file.pool.addSymbol(this);
2065 private void crossLink() throws DescriptorValidationException {
2066 final GenericDescriptor input =
2067 file.pool.lookupSymbol(
2068 proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2069 if (!(input instanceof Descriptor)) {
2070 throw new DescriptorValidationException(
2071 this, '\"' + proto.getInputType() + "\" is not a message type.");
2073 inputType = (Descriptor) input;
2075 final GenericDescriptor output =
2076 file.pool.lookupSymbol(
2077 proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY);
2078 if (!(output instanceof Descriptor)) {
2079 throw new DescriptorValidationException(
2080 this, '\"' + proto.getOutputType() + "\" is not a message type.");
2082 outputType = (Descriptor) output;
2085 /** See {@link FileDescriptor#setProto}. */
2086 private void setProto(final MethodDescriptorProto proto) {
2091 // =================================================================
2093 private static String computeFullName(
2094 final FileDescriptor file, final Descriptor parent, final String name) {
2095 if (parent != null) {
2096 return parent.getFullName() + '.' + name;
2099 final String packageName = file.getPackage();
2100 if (!packageName.isEmpty()) {
2101 return packageName + '.' + name;
2107 // =================================================================
2110 * All descriptors implement this to make it easier to implement tools like {@code
2113 * <p>This class is public so that the methods it exposes can be called from outside of this
2114 * package. However, it should only be subclassed from nested classes of Descriptors.
2116 public abstract static class GenericDescriptor {
2117 public abstract Message toProto();
2119 public abstract String getName();
2121 public abstract String getFullName();
2123 public abstract FileDescriptor getFile();
2126 /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */
2127 public static class DescriptorValidationException extends Exception {
2128 private static final long serialVersionUID = 5750205775490483148L;
2130 /** Gets the full name of the descriptor where the error occurred. */
2131 public String getProblemSymbolName() {
2135 /** Gets the protocol message representation of the invalid descriptor. */
2136 public Message getProblemProto() {
2140 /** Gets a human-readable description of the error. */
2141 public String getDescription() {
2145 private final String name;
2146 private final Message proto;
2147 private final String description;
2149 private DescriptorValidationException(
2150 final GenericDescriptor problemDescriptor, final String description) {
2151 super(problemDescriptor.getFullName() + ": " + description);
2153 // Note that problemDescriptor may be partially uninitialized, so we
2154 // don't want to expose it directly to the user. So, we only provide
2155 // the name and the original proto.
2156 name = problemDescriptor.getFullName();
2157 proto = problemDescriptor.toProto();
2158 this.description = description;
2161 private DescriptorValidationException(
2162 final GenericDescriptor problemDescriptor,
2163 final String description,
2164 final Throwable cause) {
2165 this(problemDescriptor, description);
2169 private DescriptorValidationException(
2170 final FileDescriptor problemDescriptor, final String description) {
2171 super(problemDescriptor.getName() + ": " + description);
2173 // Note that problemDescriptor may be partially uninitialized, so we
2174 // don't want to expose it directly to the user. So, we only provide
2175 // the name and the original proto.
2176 name = problemDescriptor.getName();
2177 proto = problemDescriptor.toProto();
2178 this.description = description;
2182 // =================================================================
2185 * A private helper class which contains lookup tables containing all the descriptors defined in a
2188 private static final class DescriptorPool {
2190 /** Defines what subclass of descriptors to search in the descriptor pool. */
2197 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) {
2198 this.dependencies = new HashSet<FileDescriptor>();
2199 this.allowUnknownDependencies = allowUnknownDependencies;
2201 for (int i = 0; i < dependencies.length; i++) {
2202 this.dependencies.add(dependencies[i]);
2203 importPublicDependencies(dependencies[i]);
2206 for (final FileDescriptor dependency : this.dependencies) {
2208 addPackage(dependency.getPackage(), dependency);
2209 } catch (DescriptorValidationException e) {
2210 // Can't happen, because addPackage() only fails when the name
2211 // conflicts with a non-package, but we have not yet added any
2212 // non-packages at this point.
2213 throw new AssertionError(e);
2218 /** Find and put public dependencies of the file into dependencies set. */
2219 private void importPublicDependencies(final FileDescriptor file) {
2220 for (FileDescriptor dependency : file.getPublicDependencies()) {
2221 if (dependencies.add(dependency)) {
2222 importPublicDependencies(dependency);
2227 private final Set<FileDescriptor> dependencies;
2228 private boolean allowUnknownDependencies;
2230 private final Map<String, GenericDescriptor> descriptorsByName =
2231 new HashMap<String, GenericDescriptor>();
2232 private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
2233 new HashMap<DescriptorIntPair, FieldDescriptor>();
2234 private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
2235 new HashMap<DescriptorIntPair, EnumValueDescriptor>();
2237 /** Find a generic descriptor by fully-qualified name. */
2238 GenericDescriptor findSymbol(final String fullName) {
2239 return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
2243 * Find a descriptor by fully-qualified name and given option to only search valid field type
2246 GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) {
2247 GenericDescriptor result = descriptorsByName.get(fullName);
2248 if (result != null) {
2249 if ((filter == SearchFilter.ALL_SYMBOLS)
2250 || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2251 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2256 for (final FileDescriptor dependency : dependencies) {
2257 result = dependency.pool.descriptorsByName.get(fullName);
2258 if (result != null) {
2259 if ((filter == SearchFilter.ALL_SYMBOLS)
2260 || ((filter == SearchFilter.TYPES_ONLY) && isType(result))
2261 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) {
2270 /** Checks if the descriptor is a valid type for a message field. */
2271 boolean isType(GenericDescriptor descriptor) {
2272 return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor);
2275 /** Checks if the descriptor is a valid namespace type. */
2276 boolean isAggregate(GenericDescriptor descriptor) {
2277 return (descriptor instanceof Descriptor)
2278 || (descriptor instanceof EnumDescriptor)
2279 || (descriptor instanceof PackageDescriptor)
2280 || (descriptor instanceof ServiceDescriptor);
2284 * Look up a type descriptor by name, relative to some other descriptor. The name may be
2285 * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name
2286 * lookup semantics are used to search for the matching descriptor.
2288 GenericDescriptor lookupSymbol(
2290 final GenericDescriptor relativeTo,
2291 final DescriptorPool.SearchFilter filter)
2292 throws DescriptorValidationException {
2293 // TODO(kenton): This could be optimized in a number of ways.
2295 GenericDescriptor result;
2297 if (name.startsWith(".")) {
2298 // Fully-qualified name.
2299 fullname = name.substring(1);
2300 result = findSymbol(fullname, filter);
2302 // If "name" is a compound identifier, we want to search for the
2303 // first component of it, then search within it for the rest.
2304 // If name is something like "Foo.Bar.baz", and symbols named "Foo" are
2305 // defined in multiple parent scopes, we only want to find "Bar.baz" in
2306 // the innermost one. E.g., the following should produce an error:
2307 // message Bar { message Baz {} }
2311 // optional Bar.Baz baz = 1;
2313 // So, we look for just "Foo" first, then look for "Bar.baz" within it
2315 final int firstPartLength = name.indexOf('.');
2316 final String firstPart;
2317 if (firstPartLength == -1) {
2320 firstPart = name.substring(0, firstPartLength);
2323 // We will search each parent scope of "relativeTo" looking for the
2325 final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName());
2328 // Chop off the last component of the scope.
2329 final int dotpos = scopeToTry.lastIndexOf(".");
2332 result = findSymbol(name, filter);
2335 scopeToTry.setLength(dotpos + 1);
2337 // Append firstPart and try to find
2338 scopeToTry.append(firstPart);
2339 result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY);
2341 if (result != null) {
2342 if (firstPartLength != -1) {
2343 // We only found the first part of the symbol. Now look for
2344 // the whole thing. If this fails, we *don't* want to keep
2345 // searching parent scopes.
2346 scopeToTry.setLength(dotpos + 1);
2347 scopeToTry.append(name);
2348 result = findSymbol(scopeToTry.toString(), filter);
2350 fullname = scopeToTry.toString();
2354 // Not found. Remove the name so we can try again.
2355 scopeToTry.setLength(dotpos);
2360 if (result == null) {
2361 if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) {
2363 "The descriptor for message type \""
2365 + "\" can not be found and a placeholder is created for it");
2366 // We create a dummy message descriptor here regardless of the
2367 // expected type. If the type should be message, this dummy
2368 // descriptor will work well and if the type should be enum, a
2369 // DescriptorValidationException will be thrown latter. In either
2370 // case, the code works as expected: we allow unknown message types
2371 // but not unknown enum types.
2372 result = new Descriptor(fullname);
2373 // Add the placeholder file as a dependency so we can find the
2374 // placeholder symbol when resolving other references.
2375 this.dependencies.add(result.getFile());
2378 throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined.");
2386 * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an
2389 void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException {
2390 validateSymbolName(descriptor);
2392 final String fullName = descriptor.getFullName();
2394 final GenericDescriptor old = descriptorsByName.put(fullName, descriptor);
2396 descriptorsByName.put(fullName, old);
2398 if (descriptor.getFile() == old.getFile()) {
2399 final int dotpos = fullName.lastIndexOf('.');
2401 throw new DescriptorValidationException(
2402 descriptor, '\"' + fullName + "\" is already defined.");
2404 throw new DescriptorValidationException(
2407 + fullName.substring(dotpos + 1)
2408 + "\" is already defined in \""
2409 + fullName.substring(0, dotpos)
2413 throw new DescriptorValidationException(
2417 + "\" is already defined in file \""
2418 + old.getFile().getName()
2425 * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so
2426 * that someone cannot define, say, a message type that has the same name as an existing
2429 private static final class PackageDescriptor extends GenericDescriptor {
2431 public Message toProto() {
2432 return file.toProto();
2436 public String getName() {
2441 public String getFullName() {
2446 public FileDescriptor getFile() {
2450 PackageDescriptor(final String name, final String fullName, final FileDescriptor file) {
2452 this.fullName = fullName;
2456 private final String name;
2457 private final String fullName;
2458 private final FileDescriptor file;
2462 * Adds a package to the symbol tables. If a package by the same name already exists, that is
2463 * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If
2464 * the package has multiple components, this also adds the parent package(s).
2466 void addPackage(final String fullName, final FileDescriptor file)
2467 throws DescriptorValidationException {
2468 final int dotpos = fullName.lastIndexOf('.');
2473 addPackage(fullName.substring(0, dotpos), file);
2474 name = fullName.substring(dotpos + 1);
2477 final GenericDescriptor old =
2478 descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file));
2480 descriptorsByName.put(fullName, old);
2481 if (!(old instanceof PackageDescriptor)) {
2482 throw new DescriptorValidationException(
2486 + "\" is already defined (as something other than a "
2487 + "package) in file \""
2488 + old.getFile().getName()
2494 /** A (GenericDescriptor, int) pair, used as a map key. */
2495 private static final class DescriptorIntPair {
2496 private final GenericDescriptor descriptor;
2497 private final int number;
2499 DescriptorIntPair(final GenericDescriptor descriptor, final int number) {
2500 this.descriptor = descriptor;
2501 this.number = number;
2505 public int hashCode() {
2506 return descriptor.hashCode() * ((1 << 16) - 1) + number;
2510 public boolean equals(final Object obj) {
2511 if (!(obj instanceof DescriptorIntPair)) {
2514 final DescriptorIntPair other = (DescriptorIntPair) obj;
2515 return descriptor == other.descriptor && number == other.number;
2520 * Adds a field to the fieldsByNumber table. Throws an exception if a field with the same
2521 * containing type and number already exists.
2523 void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException {
2524 final DescriptorIntPair key =
2525 new DescriptorIntPair(field.getContainingType(), field.getNumber());
2526 final FieldDescriptor old = fieldsByNumber.put(key, field);
2528 fieldsByNumber.put(key, old);
2529 throw new DescriptorValidationException(
2533 + " has already been used in \""
2534 + field.getContainingType().getFullName()
2542 * Adds an enum value to the enumValuesByNumber table. If an enum value with the same type and
2543 * number already exists, does nothing. (This is allowed; the first value define with the number
2544 * takes precedence.)
2546 void addEnumValueByNumber(final EnumValueDescriptor value) {
2547 final DescriptorIntPair key = new DescriptorIntPair(value.getType(), value.getNumber());
2548 final EnumValueDescriptor old = enumValuesByNumber.put(key, value);
2550 enumValuesByNumber.put(key, old);
2551 // Not an error: Multiple enum values may have the same number, but
2552 // we only want the first one in the map.
2557 * Verifies that the descriptor's name is valid (i.e. it contains only letters, digits, and
2558 * underscores, and does not start with a digit).
2560 static void validateSymbolName(final GenericDescriptor descriptor)
2561 throws DescriptorValidationException {
2562 final String name = descriptor.getName();
2563 if (name.length() == 0) {
2564 throw new DescriptorValidationException(descriptor, "Missing name.");
2567 // Non-ASCII characters are not valid in protobuf identifiers, even
2568 // if they are letters or digits.
2569 // The first character must be a letter or '_'.
2570 // Subsequent characters may be letters, numbers, or digits.
2571 for (int i = 0; i < name.length(); i++) {
2572 final char c = name.charAt(i);
2573 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
2575 || ('0' <= c && c <= '9' && i > 0)) {
2579 throw new DescriptorValidationException(
2580 descriptor, '\"' + name + "\" is not a valid identifier.");
2585 /** Describes an oneof of a message type. */
2586 public static final class OneofDescriptor {
2587 /** Get the index of this descriptor within its parent. */
2588 public int getIndex() {
2592 public String getName() {
2593 return proto.getName();
2596 public FileDescriptor getFile() {
2600 public String getFullName() {
2604 public Descriptor getContainingType() {
2605 return containingType;
2608 public int getFieldCount() {
2612 public OneofOptions getOptions() {
2613 return proto.getOptions();
2616 /** Get a list of this message type's fields. */
2617 public List<FieldDescriptor> getFields() {
2618 return Collections.unmodifiableList(Arrays.asList(fields));
2621 public FieldDescriptor getField(int index) {
2622 return fields[index];
2625 private void setProto(final OneofDescriptorProto proto) {
2629 private OneofDescriptor(
2630 final OneofDescriptorProto proto,
2631 final FileDescriptor file,
2632 final Descriptor parent,
2634 throws DescriptorValidationException {
2636 fullName = computeFullName(file, parent, proto.getName());
2640 containingType = parent;
2644 private final int index;
2645 private OneofDescriptorProto proto;
2646 private final String fullName;
2647 private final FileDescriptor file;
2649 private Descriptor containingType;
2650 private int fieldCount;
2651 private FieldDescriptor[] fields;