1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import static com.google.protobuf.Internal.checkNotNull; 34 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; 59 import java.util.Map; 60 import java.util.Set; 61 import java.util.WeakHashMap; 62 import java.util.logging.Logger; 63 64 /** 65 * Contains a collection of classes which describe protocol message types. 66 * 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. 74 * 75 * <p>Descriptors are built from DescriptorProtos, as defined in {@code 76 * google/protobuf/descriptor.proto}. 77 * 78 * @author kenton@google.com Kenton Varda 79 */ 80 public final class Descriptors { 81 private static final Logger logger = Logger.getLogger(Descriptors.class.getName()); 82 /** 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). 86 */ 87 public static final class FileDescriptor extends GenericDescriptor { 88 /** Convert the descriptor to its protocol message representation. */ 89 @Override toProto()90 public FileDescriptorProto toProto() { 91 return proto; 92 } 93 94 /** Get the file name. */ 95 @Override getName()96 public String getName() { 97 return proto.getName(); 98 } 99 100 /** Returns this object. */ 101 @Override getFile()102 public FileDescriptor getFile() { 103 return this; 104 } 105 106 /** Returns the same as getName(). */ 107 @Override getFullName()108 public String getFullName() { 109 return proto.getName(); 110 } 111 112 /** 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. 115 */ getPackage()116 public String getPackage() { 117 return proto.getPackage(); 118 } 119 120 /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */ getOptions()121 public FileOptions getOptions() { 122 return proto.getOptions(); 123 } 124 125 /** Get a list of top-level message types declared in this file. */ getMessageTypes()126 public List<Descriptor> getMessageTypes() { 127 return Collections.unmodifiableList(Arrays.asList(messageTypes)); 128 } 129 130 /** Get a list of top-level enum types declared in this file. */ getEnumTypes()131 public List<EnumDescriptor> getEnumTypes() { 132 return Collections.unmodifiableList(Arrays.asList(enumTypes)); 133 } 134 135 /** Get a list of top-level services declared in this file. */ getServices()136 public List<ServiceDescriptor> getServices() { 137 return Collections.unmodifiableList(Arrays.asList(services)); 138 } 139 140 /** Get a list of top-level extensions declared in this file. */ getExtensions()141 public List<FieldDescriptor> getExtensions() { 142 return Collections.unmodifiableList(Arrays.asList(extensions)); 143 } 144 145 /** Get a list of this file's dependencies (imports). */ getDependencies()146 public List<FileDescriptor> getDependencies() { 147 return Collections.unmodifiableList(Arrays.asList(dependencies)); 148 } 149 150 /** Get a list of this file's public dependencies (public imports). */ getPublicDependencies()151 public List<FileDescriptor> getPublicDependencies() { 152 return Collections.unmodifiableList(Arrays.asList(publicDependencies)); 153 } 154 155 /** The syntax of the .proto file. */ 156 public enum Syntax { 157 UNKNOWN("unknown"), 158 PROTO2("proto2"), 159 PROTO3("proto3"); 160 Syntax(String name)161 Syntax(String name) { 162 this.name = name; 163 } 164 165 private final String name; 166 } 167 168 /** Get the syntax of the .proto file. */ getSyntax()169 public Syntax getSyntax() { 170 if (Syntax.PROTO3.name.equals(proto.getSyntax())) { 171 return Syntax.PROTO3; 172 } 173 return Syntax.PROTO2; 174 } 175 176 /** 177 * Find a message type in the file by name. Does not find nested types. 178 * 179 * @param name The unqualified type name to look for. 180 * @return The message type's descriptor, or {@code null} if not found. 181 */ findMessageTypeByName(String name)182 public Descriptor findMessageTypeByName(String name) { 183 // Don't allow looking up nested types. This will make optimization 184 // easier later. 185 if (name.indexOf('.') != -1) { 186 return null; 187 } 188 final String packageName = getPackage(); 189 if (!packageName.isEmpty()) { 190 name = packageName + '.' + name; 191 } 192 final GenericDescriptor result = pool.findSymbol(name); 193 if (result != null && result instanceof Descriptor && result.getFile() == this) { 194 return (Descriptor) result; 195 } else { 196 return null; 197 } 198 } 199 200 /** 201 * Find an enum type in the file by name. Does not find nested types. 202 * 203 * @param name The unqualified type name to look for. 204 * @return The enum type's descriptor, or {@code null} if not found. 205 */ findEnumTypeByName(String name)206 public EnumDescriptor findEnumTypeByName(String name) { 207 // Don't allow looking up nested types. This will make optimization 208 // easier later. 209 if (name.indexOf('.') != -1) { 210 return null; 211 } 212 final String packageName = getPackage(); 213 if (!packageName.isEmpty()) { 214 name = packageName + '.' + name; 215 } 216 final GenericDescriptor result = pool.findSymbol(name); 217 if (result != null && result instanceof EnumDescriptor && result.getFile() == this) { 218 return (EnumDescriptor) result; 219 } else { 220 return null; 221 } 222 } 223 224 /** 225 * Find a service type in the file by name. 226 * 227 * @param name The unqualified type name to look for. 228 * @return The service type's descriptor, or {@code null} if not found. 229 */ findServiceByName(String name)230 public ServiceDescriptor findServiceByName(String name) { 231 // Don't allow looking up nested types. This will make optimization 232 // easier later. 233 if (name.indexOf('.') != -1) { 234 return null; 235 } 236 final String packageName = getPackage(); 237 if (!packageName.isEmpty()) { 238 name = packageName + '.' + name; 239 } 240 final GenericDescriptor result = pool.findSymbol(name); 241 if (result != null && result instanceof ServiceDescriptor && result.getFile() == this) { 242 return (ServiceDescriptor) result; 243 } else { 244 return null; 245 } 246 } 247 248 /** 249 * Find an extension in the file by name. Does not find extensions nested inside message types. 250 * 251 * @param name The unqualified extension name to look for. 252 * @return The extension's descriptor, or {@code null} if not found. 253 */ findExtensionByName(String name)254 public FieldDescriptor findExtensionByName(String name) { 255 if (name.indexOf('.') != -1) { 256 return null; 257 } 258 final String packageName = getPackage(); 259 if (!packageName.isEmpty()) { 260 name = packageName + '.' + name; 261 } 262 final GenericDescriptor result = pool.findSymbol(name); 263 if (result != null && result instanceof FieldDescriptor && result.getFile() == this) { 264 return (FieldDescriptor) result; 265 } else { 266 return null; 267 } 268 } 269 270 /** 271 * Construct a {@code FileDescriptor}. 272 * 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. 278 */ buildFrom( final FileDescriptorProto proto, final FileDescriptor[] dependencies)279 public static FileDescriptor buildFrom( 280 final FileDescriptorProto proto, final FileDescriptor[] dependencies) 281 throws DescriptorValidationException { 282 return buildFrom(proto, dependencies, false); 283 } 284 285 /** 286 * Construct a {@code FileDescriptor}. 287 * 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. 295 */ buildFrom( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final boolean allowUnknownDependencies)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); 313 result.crossLink(); 314 return result; 315 } 316 latin1Cat(final String[] strings)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); 329 } 330 StringBuilder descriptorData = new StringBuilder(); 331 for (String part : strings) { 332 descriptorData.append(part); 333 } 334 return descriptorData.toString().getBytes(Internal.ISO_8859_1); 335 } 336 findDescriptors( final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)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++) { 343 try { 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."); 350 } 351 } 352 return descriptors.toArray(new FileDescriptor[0]); 353 } 354 355 /** 356 * This method is for backward compatibility with generated code which passed an 357 * InternalDescriptorAssigner. 358 */ 359 @Deprecated internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final FileDescriptor[] dependencies, final InternalDescriptorAssigner descriptorAssigner)360 public static void internalBuildGeneratedFileFrom( 361 final String[] descriptorDataParts, 362 final FileDescriptor[] dependencies, 363 final InternalDescriptorAssigner descriptorAssigner) { 364 final byte[] descriptorBytes = latin1Cat(descriptorDataParts); 365 366 FileDescriptorProto proto; 367 try { 368 proto = FileDescriptorProto.parseFrom(descriptorBytes); 369 } catch (InvalidProtocolBufferException e) { 370 throw new IllegalArgumentException( 371 "Failed to parse protocol buffer descriptor for generated code.", e); 372 } 373 374 final FileDescriptor result; 375 try { 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); 382 } 383 384 final ExtensionRegistry registry = descriptorAssigner.assignDescriptors(result); 385 386 if (registry != null) { 387 // We must re-parse the proto using the registry. 388 try { 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); 393 } 394 395 result.setProto(proto); 396 } 397 } 398 399 /** 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. 402 */ internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final FileDescriptor[] dependencies)403 public static FileDescriptor internalBuildGeneratedFileFrom( 404 final String[] descriptorDataParts, 405 final FileDescriptor[] dependencies) { 406 final byte[] descriptorBytes = latin1Cat(descriptorDataParts); 407 408 FileDescriptorProto proto; 409 try { 410 proto = FileDescriptorProto.parseFrom(descriptorBytes); 411 } catch (InvalidProtocolBufferException e) { 412 throw new IllegalArgumentException( 413 "Failed to parse protocol buffer descriptor for generated code.", e); 414 } 415 416 try { 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); 423 } 424 } 425 426 /** 427 * This method is for backward compatibility with generated code which passed an 428 * InternalDescriptorAssigner. 429 */ 430 @Deprecated internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames, final InternalDescriptorAssigner descriptorAssigner)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); 441 } 442 443 /** 444 * This method is to be called by generated code only. It uses Java reflection to load the 445 * dependencies' descriptors. 446 */ internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)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); 455 } 456 457 /** 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. 461 */ internalUpdateFileDescriptor( final FileDescriptor descriptor, final ExtensionRegistry registry)462 public static void internalUpdateFileDescriptor( 463 final FileDescriptor descriptor, final ExtensionRegistry registry) { 464 ByteString bytes = descriptor.proto.toByteString(); 465 FileDescriptorProto proto; 466 try { 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); 471 } 472 descriptor.setProto(proto); 473 } 474 475 /** 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. 484 * 485 * This interface is deprecated. Use the return value of internalBuildGeneratedFrom() instead. 486 */ 487 @Deprecated 488 public interface InternalDescriptorAssigner { assignDescriptors(FileDescriptor root)489 ExtensionRegistry assignDescriptors(FileDescriptor root); 490 } 491 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; 500 FileDescriptor( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final DescriptorPool pool, boolean allowUnknownDependencies)501 private FileDescriptor( 502 final FileDescriptorProto proto, 503 final FileDescriptor[] dependencies, 504 final DescriptorPool pool, 505 boolean allowUnknownDependencies) 506 throws DescriptorValidationException { 507 this.pool = pool; 508 this.proto = proto; 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); 513 } 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."); 519 } 520 String name = proto.getDependency(index); 521 FileDescriptor file = nameToFileMap.get(name); 522 if (file == null) { 523 if (!allowUnknownDependencies) { 524 throw new DescriptorValidationException(this, "Invalid public dependency: " + name); 525 } 526 // Ignore unknown dependencies. 527 } else { 528 publicDependencies.add(file); 529 } 530 } 531 this.publicDependencies = new FileDescriptor[publicDependencies.size()]; 532 publicDependencies.toArray(this.publicDependencies); 533 534 pool.addPackage(getPackage(), this); 535 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); 539 } 540 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); 544 } 545 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); 549 } 550 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); 554 } 555 } 556 557 /** Create a placeholder FileDescriptor for a message Descriptor. */ FileDescriptor(String packageName, Descriptor message)558 FileDescriptor(String packageName, Descriptor message) throws DescriptorValidationException { 559 this.pool = new DescriptorPool(new FileDescriptor[0], true); 560 this.proto = 561 FileDescriptorProto.newBuilder() 562 .setName(message.getFullName() + ".placeholder.proto") 563 .setPackage(packageName) 564 .addMessageType(message.toProto()) 565 .build(); 566 this.dependencies = new FileDescriptor[0]; 567 this.publicDependencies = new FileDescriptor[0]; 568 569 messageTypes = new Descriptor[] {message}; 570 enumTypes = new EnumDescriptor[0]; 571 services = new ServiceDescriptor[0]; 572 extensions = new FieldDescriptor[0]; 573 574 pool.addPackage(packageName, this); 575 pool.addSymbol(message); 576 } 577 578 /** Look up and cross-link all field types, etc. */ crossLink()579 private void crossLink() throws DescriptorValidationException { 580 for (final Descriptor messageType : messageTypes) { 581 messageType.crossLink(); 582 } 583 584 for (final ServiceDescriptor service : services) { 585 service.crossLink(); 586 } 587 588 for (final FieldDescriptor extension : extensions) { 589 extension.crossLink(); 590 } 591 } 592 593 /** 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. 600 */ setProto(final FileDescriptorProto proto)601 private void setProto(final FileDescriptorProto proto) { 602 this.proto = proto; 603 604 for (int i = 0; i < messageTypes.length; i++) { 605 messageTypes[i].setProto(proto.getMessageType(i)); 606 } 607 608 for (int i = 0; i < enumTypes.length; i++) { 609 enumTypes[i].setProto(proto.getEnumType(i)); 610 } 611 612 for (int i = 0; i < services.length; i++) { 613 services[i].setProto(proto.getService(i)); 614 } 615 616 for (int i = 0; i < extensions.length; i++) { 617 extensions[i].setProto(proto.getExtension(i)); 618 } 619 } 620 supportsUnknownEnumValue()621 boolean supportsUnknownEnumValue() { 622 return getSyntax() == Syntax.PROTO3; 623 } 624 } 625 626 // ================================================================= 627 628 /** Describes a message type. */ 629 public static final class Descriptor extends GenericDescriptor { 630 /** 631 * Get the index of this descriptor within its parent. In other words, given a {@link 632 * FileDescriptor} {@code file}, the following is true: 633 * 634 * <pre> 635 * for all i in [0, file.getMessageTypeCount()): 636 * file.getMessageType(i).getIndex() == i 637 * </pre> 638 * 639 * Similarly, for a {@link Descriptor} {@code messageType}: 640 * 641 * <pre> 642 * for all i in [0, messageType.getNestedTypeCount()): 643 * messageType.getNestedType(i).getIndex() == i 644 * </pre> 645 */ getIndex()646 public int getIndex() { 647 return index; 648 } 649 650 /** Convert the descriptor to its protocol message representation. */ 651 @Override toProto()652 public DescriptorProto toProto() { 653 return proto; 654 } 655 656 /** Get the type's unqualified name. */ 657 @Override getName()658 public String getName() { 659 return proto.getName(); 660 } 661 662 /** 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}: 665 * 666 * <pre> 667 * package foo.bar; 668 * option java_package = "com.example.protos" 669 * message Baz {} 670 * </pre> 671 * 672 * {@code Baz}'s full name is "foo.bar.Baz". 673 */ 674 @Override getFullName()675 public String getFullName() { 676 return fullName; 677 } 678 679 /** Get the {@link FileDescriptor} containing this descriptor. */ 680 @Override getFile()681 public FileDescriptor getFile() { 682 return file; 683 } 684 685 /** If this is a nested type, get the outer descriptor, otherwise null. */ getContainingType()686 public Descriptor getContainingType() { 687 return containingType; 688 } 689 690 /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */ getOptions()691 public MessageOptions getOptions() { 692 return proto.getOptions(); 693 } 694 695 /** Get a list of this message type's fields. */ getFields()696 public List<FieldDescriptor> getFields() { 697 return Collections.unmodifiableList(Arrays.asList(fields)); 698 } 699 700 /** Get a list of this message type's oneofs. */ getOneofs()701 public List<OneofDescriptor> getOneofs() { 702 return Collections.unmodifiableList(Arrays.asList(oneofs)); 703 } 704 705 /** Get a list of this message type's real oneofs. */ getRealOneofs()706 public List<OneofDescriptor> getRealOneofs() { 707 return Collections.unmodifiableList(Arrays.asList(oneofs).subList(0, realOneofCount)); 708 } 709 710 /** Get a list of this message type's extensions. */ getExtensions()711 public List<FieldDescriptor> getExtensions() { 712 return Collections.unmodifiableList(Arrays.asList(extensions)); 713 } 714 715 /** Get a list of message types nested within this one. */ getNestedTypes()716 public List<Descriptor> getNestedTypes() { 717 return Collections.unmodifiableList(Arrays.asList(nestedTypes)); 718 } 719 720 /** Get a list of enum types nested within this one. */ getEnumTypes()721 public List<EnumDescriptor> getEnumTypes() { 722 return Collections.unmodifiableList(Arrays.asList(enumTypes)); 723 } 724 725 /** Determines if the given field number is an extension. */ isExtensionNumber(final int number)726 public boolean isExtensionNumber(final int number) { 727 for (final DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) { 728 if (range.getStart() <= number && number < range.getEnd()) { 729 return true; 730 } 731 } 732 return false; 733 } 734 735 /** Determines if the given field number is reserved. */ isReservedNumber(final int number)736 public boolean isReservedNumber(final int number) { 737 for (final DescriptorProto.ReservedRange range : proto.getReservedRangeList()) { 738 if (range.getStart() <= number && number < range.getEnd()) { 739 return true; 740 } 741 } 742 return false; 743 } 744 745 /** Determines if the given field name is reserved. */ isReservedName(final String name)746 public boolean isReservedName(final String name) { 747 checkNotNull(name); 748 for (final String reservedName : proto.getReservedNameList()) { 749 if (reservedName.equals(name)) { 750 return true; 751 } 752 } 753 return false; 754 } 755 756 /** 757 * Indicates whether the message can be extended. That is, whether it has any "extensions x to 758 * y" ranges declared on it. 759 */ isExtendable()760 public boolean isExtendable() { 761 return proto.getExtensionRangeList().size() != 0; 762 } 763 764 /** 765 * Finds a field by name. 766 * 767 * @param name The unqualified name of the field (e.g. "foo"). 768 * @return The field's descriptor, or {@code null} if not found. 769 */ findFieldByName(final String name)770 public FieldDescriptor findFieldByName(final String name) { 771 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 772 if (result != null && result instanceof FieldDescriptor) { 773 return (FieldDescriptor) result; 774 } else { 775 return null; 776 } 777 } 778 779 /** 780 * Finds a field by field number. 781 * 782 * @param number The field number within this message type. 783 * @return The field's descriptor, or {@code null} if not found. 784 */ findFieldByNumber(final int number)785 public FieldDescriptor findFieldByNumber(final int number) { 786 return file.pool.fieldsByNumber.get(new DescriptorPool.DescriptorIntPair(this, number)); 787 } 788 789 /** 790 * Finds a nested message type by name. 791 * 792 * @param name The unqualified name of the nested type (e.g. "Foo"). 793 * @return The types's descriptor, or {@code null} if not found. 794 */ findNestedTypeByName(final String name)795 public Descriptor findNestedTypeByName(final String name) { 796 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 797 if (result != null && result instanceof Descriptor) { 798 return (Descriptor) result; 799 } else { 800 return null; 801 } 802 } 803 804 /** 805 * Finds a nested enum type by name. 806 * 807 * @param name The unqualified name of the nested type (e.g. "Foo"). 808 * @return The types's descriptor, or {@code null} if not found. 809 */ findEnumTypeByName(final String name)810 public EnumDescriptor findEnumTypeByName(final String name) { 811 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 812 if (result != null && result instanceof EnumDescriptor) { 813 return (EnumDescriptor) result; 814 } else { 815 return null; 816 } 817 } 818 819 private final int index; 820 private DescriptorProto proto; 821 private final String fullName; 822 private final FileDescriptor file; 823 private final Descriptor containingType; 824 private final Descriptor[] nestedTypes; 825 private final EnumDescriptor[] enumTypes; 826 private final FieldDescriptor[] fields; 827 private final FieldDescriptor[] extensions; 828 private final OneofDescriptor[] oneofs; 829 private final int realOneofCount; 830 831 // Used to create a placeholder when the type cannot be found. Descriptor(final String fullname)832 Descriptor(final String fullname) throws DescriptorValidationException { 833 String name = fullname; 834 String packageName = ""; 835 int pos = fullname.lastIndexOf('.'); 836 if (pos != -1) { 837 name = fullname.substring(pos + 1); 838 packageName = fullname.substring(0, pos); 839 } 840 this.index = 0; 841 this.proto = 842 DescriptorProto.newBuilder() 843 .setName(name) 844 .addExtensionRange( 845 DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build()) 846 .build(); 847 this.fullName = fullname; 848 this.containingType = null; 849 850 this.nestedTypes = new Descriptor[0]; 851 this.enumTypes = new EnumDescriptor[0]; 852 this.fields = new FieldDescriptor[0]; 853 this.extensions = new FieldDescriptor[0]; 854 this.oneofs = new OneofDescriptor[0]; 855 this.realOneofCount = 0; 856 857 // Create a placeholder FileDescriptor to hold this message. 858 this.file = new FileDescriptor(packageName, this); 859 } 860 Descriptor( final DescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)861 private Descriptor( 862 final DescriptorProto proto, 863 final FileDescriptor file, 864 final Descriptor parent, 865 final int index) 866 throws DescriptorValidationException { 867 this.index = index; 868 this.proto = proto; 869 fullName = computeFullName(file, parent, proto.getName()); 870 this.file = file; 871 containingType = parent; 872 873 oneofs = new OneofDescriptor[proto.getOneofDeclCount()]; 874 for (int i = 0; i < proto.getOneofDeclCount(); i++) { 875 oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i); 876 } 877 878 nestedTypes = new Descriptor[proto.getNestedTypeCount()]; 879 for (int i = 0; i < proto.getNestedTypeCount(); i++) { 880 nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i); 881 } 882 883 enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; 884 for (int i = 0; i < proto.getEnumTypeCount(); i++) { 885 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i); 886 } 887 888 fields = new FieldDescriptor[proto.getFieldCount()]; 889 for (int i = 0; i < proto.getFieldCount(); i++) { 890 fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false); 891 } 892 893 extensions = new FieldDescriptor[proto.getExtensionCount()]; 894 for (int i = 0; i < proto.getExtensionCount(); i++) { 895 extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true); 896 } 897 898 for (int i = 0; i < proto.getOneofDeclCount(); i++) { 899 oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()]; 900 oneofs[i].fieldCount = 0; 901 } 902 for (int i = 0; i < proto.getFieldCount(); i++) { 903 OneofDescriptor oneofDescriptor = fields[i].getContainingOneof(); 904 if (oneofDescriptor != null) { 905 oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i]; 906 } 907 } 908 909 int syntheticOneofCount = 0; 910 for (OneofDescriptor oneof : this.oneofs) { 911 if (oneof.isSynthetic()) { 912 syntheticOneofCount++; 913 } else { 914 if (syntheticOneofCount > 0) { 915 throw new DescriptorValidationException(this, "Synthetic oneofs must come last."); 916 } 917 } 918 } 919 this.realOneofCount = this.oneofs.length - syntheticOneofCount; 920 921 file.pool.addSymbol(this); 922 } 923 924 /** Look up and cross-link all field types, etc. */ crossLink()925 private void crossLink() throws DescriptorValidationException { 926 for (final Descriptor nestedType : nestedTypes) { 927 nestedType.crossLink(); 928 } 929 930 for (final FieldDescriptor field : fields) { 931 field.crossLink(); 932 } 933 934 for (final FieldDescriptor extension : extensions) { 935 extension.crossLink(); 936 } 937 } 938 939 /** See {@link FileDescriptor#setProto}. */ setProto(final DescriptorProto proto)940 private void setProto(final DescriptorProto proto) { 941 this.proto = proto; 942 943 for (int i = 0; i < nestedTypes.length; i++) { 944 nestedTypes[i].setProto(proto.getNestedType(i)); 945 } 946 947 for (int i = 0; i < oneofs.length; i++) { 948 oneofs[i].setProto(proto.getOneofDecl(i)); 949 } 950 951 for (int i = 0; i < enumTypes.length; i++) { 952 enumTypes[i].setProto(proto.getEnumType(i)); 953 } 954 955 for (int i = 0; i < fields.length; i++) { 956 fields[i].setProto(proto.getField(i)); 957 } 958 959 for (int i = 0; i < extensions.length; i++) { 960 extensions[i].setProto(proto.getExtension(i)); 961 } 962 } 963 } 964 965 // ================================================================= 966 967 /** Describes a field of a message type. */ 968 public static final class FieldDescriptor extends GenericDescriptor 969 implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> { 970 /** 971 * Get the index of this descriptor within its parent. 972 * 973 * @see Descriptors.Descriptor#getIndex() 974 */ getIndex()975 public int getIndex() { 976 return index; 977 } 978 979 /** Convert the descriptor to its protocol message representation. */ 980 @Override toProto()981 public FieldDescriptorProto toProto() { 982 return proto; 983 } 984 985 /** Get the field's unqualified name. */ 986 @Override getName()987 public String getName() { 988 return proto.getName(); 989 } 990 991 /** Get the field's number. */ 992 @Override getNumber()993 public int getNumber() { 994 return proto.getNumber(); 995 } 996 997 /** 998 * Get the field's fully-qualified name. 999 * 1000 * @see Descriptors.Descriptor#getFullName() 1001 */ 1002 @Override getFullName()1003 public String getFullName() { 1004 return fullName; 1005 } 1006 1007 /** Get the JSON name of this field. */ getJsonName()1008 public String getJsonName() { 1009 return jsonName; 1010 } 1011 1012 /** 1013 * Get the field's java type. This is just for convenience. Every {@code 1014 * FieldDescriptorProto.Type} maps to exactly one Java type. 1015 */ getJavaType()1016 public JavaType getJavaType() { 1017 return type.getJavaType(); 1018 } 1019 1020 /** For internal use only. */ 1021 @Override getLiteJavaType()1022 public WireFormat.JavaType getLiteJavaType() { 1023 return getLiteType().getJavaType(); 1024 } 1025 1026 /** Get the {@code FileDescriptor} containing this descriptor. */ 1027 @Override getFile()1028 public FileDescriptor getFile() { 1029 return file; 1030 } 1031 1032 /** Get the field's declared type. */ getType()1033 public Type getType() { 1034 return type; 1035 } 1036 1037 /** For internal use only. */ 1038 @Override getLiteType()1039 public WireFormat.FieldType getLiteType() { 1040 return table[type.ordinal()]; 1041 } 1042 1043 /** For internal use only. */ needsUtf8Check()1044 public boolean needsUtf8Check() { 1045 if (type != Type.STRING) { 1046 return false; 1047 } 1048 if (getContainingType().getOptions().getMapEntry()) { 1049 // Always enforce strict UTF-8 checking for map fields. 1050 return true; 1051 } 1052 if (getFile().getSyntax() == Syntax.PROTO3) { 1053 return true; 1054 } 1055 return getFile().getOptions().getJavaStringCheckUtf8(); 1056 } 1057 isMapField()1058 public boolean isMapField() { 1059 return getType() == Type.MESSAGE 1060 && isRepeated() 1061 && getMessageType().getOptions().getMapEntry(); 1062 } 1063 1064 // I'm pretty sure values() constructs a new array every time, since there 1065 // is nothing stopping the caller from mutating the array. Therefore we 1066 // make a static copy here. 1067 private static final WireFormat.FieldType[] table = WireFormat.FieldType.values(); 1068 1069 /** Is this field declared required? */ isRequired()1070 public boolean isRequired() { 1071 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; 1072 } 1073 1074 /** Is this field declared optional? */ isOptional()1075 public boolean isOptional() { 1076 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL; 1077 } 1078 1079 /** Is this field declared repeated? */ 1080 @Override isRepeated()1081 public boolean isRepeated() { 1082 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; 1083 } 1084 1085 /** 1086 * Does this field have the {@code [packed = true]} option or is this field packable in proto3 1087 * and not explicitly setted to unpacked? 1088 */ 1089 @Override isPacked()1090 public boolean isPacked() { 1091 if (!isPackable()) { 1092 return false; 1093 } 1094 if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) { 1095 return getOptions().getPacked(); 1096 } else { 1097 return !getOptions().hasPacked() || getOptions().getPacked(); 1098 } 1099 } 1100 1101 /** Can this field be packed? i.e. is it a repeated primitive field? */ isPackable()1102 public boolean isPackable() { 1103 return isRepeated() && getLiteType().isPackable(); 1104 } 1105 1106 /** Returns true if the field had an explicitly-defined default value. */ hasDefaultValue()1107 public boolean hasDefaultValue() { 1108 return proto.hasDefaultValue(); 1109 } 1110 1111 /** 1112 * Returns the field's default value. Valid for all types except for messages and groups. For 1113 * all other types, the object returned is of the same class that would returned by 1114 * Message.getField(this). 1115 */ getDefaultValue()1116 public Object getDefaultValue() { 1117 if (getJavaType() == JavaType.MESSAGE) { 1118 throw new UnsupportedOperationException( 1119 "FieldDescriptor.getDefaultValue() called on an embedded message field."); 1120 } 1121 return defaultValue; 1122 } 1123 1124 /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ getOptions()1125 public FieldOptions getOptions() { 1126 return proto.getOptions(); 1127 } 1128 1129 /** Is this field an extension? */ isExtension()1130 public boolean isExtension() { 1131 return proto.hasExtendee(); 1132 } 1133 1134 /** 1135 * Get the field's containing type. For extensions, this is the type being extended, not the 1136 * location where the extension was defined. See {@link #getExtensionScope()}. 1137 */ getContainingType()1138 public Descriptor getContainingType() { 1139 return containingType; 1140 } 1141 1142 /** Get the field's containing oneof. */ getContainingOneof()1143 public OneofDescriptor getContainingOneof() { 1144 return containingOneof; 1145 } 1146 1147 /** Get the field's containing oneof, only if non-synthetic. */ getRealContainingOneof()1148 public OneofDescriptor getRealContainingOneof() { 1149 return containingOneof != null && !containingOneof.isSynthetic() ? containingOneof : null; 1150 } 1151 1152 /** 1153 * Returns true if this field was syntactically written with "optional" in the .proto file. 1154 * Excludes singular proto3 fields that do not have a label. 1155 */ hasOptionalKeyword()1156 public boolean hasOptionalKeyword() { 1157 return isProto3Optional 1158 || (file.getSyntax() == Syntax.PROTO2 && isOptional() && getContainingOneof() == null); 1159 } 1160 1161 /** 1162 * Returns true if this field tracks presence, ie. does the field distinguish between "unset" 1163 * and "present with default value." 1164 * 1165 * <p>This includes required, optional, and oneof fields. It excludes maps, repeated fields, and 1166 * singular proto3 fields without "optional". 1167 * 1168 * <p>For fields where hasPresence() == true, the return value of msg.hasField() is semantically 1169 * meaningful. 1170 */ hasPresence()1171 boolean hasPresence() { 1172 if (isRepeated()) { 1173 return false; 1174 } 1175 return getType() == Type.MESSAGE 1176 || getType() == Type.GROUP 1177 || getContainingOneof() != null 1178 || file.getSyntax() == Syntax.PROTO2; 1179 } 1180 1181 /** 1182 * For extensions defined nested within message types, gets the outer type. Not valid for 1183 * non-extension fields. For example, consider this {@code .proto} file: 1184 * 1185 * <pre> 1186 * message Foo { 1187 * extensions 1000 to max; 1188 * } 1189 * extend Foo { 1190 * optional int32 baz = 1234; 1191 * } 1192 * message Bar { 1193 * extend Foo { 1194 * optional int32 qux = 4321; 1195 * } 1196 * } 1197 * </pre> 1198 * 1199 * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. However, {@code baz}'s 1200 * extension scope is {@code null} while {@code qux}'s extension scope is {@code Bar}. 1201 */ getExtensionScope()1202 public Descriptor getExtensionScope() { 1203 if (!isExtension()) { 1204 throw new UnsupportedOperationException( 1205 String.format("This field is not an extension. (%s)", fullName)); 1206 } 1207 return extensionScope; 1208 } 1209 1210 /** For embedded message and group fields, gets the field's type. */ getMessageType()1211 public Descriptor getMessageType() { 1212 if (getJavaType() != JavaType.MESSAGE) { 1213 throw new UnsupportedOperationException( 1214 String.format("This field is not of message type. (%s)", fullName)); 1215 } 1216 return messageType; 1217 } 1218 1219 /** For enum fields, gets the field's type. */ 1220 @Override getEnumType()1221 public EnumDescriptor getEnumType() { 1222 if (getJavaType() != JavaType.ENUM) { 1223 throw new UnsupportedOperationException( 1224 String.format("This field is not of enum type. (%s)", fullName)); 1225 } 1226 return enumType; 1227 } 1228 1229 /** 1230 * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which 1231 * simply means ascending order by field number. {@code other} must be a field of the same type 1232 * -- i.e. {@code getContainingType()} must return the same {@code Descriptor} for both fields. 1233 * 1234 * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than 1235 * {@code other}, respectively. 1236 */ 1237 @Override compareTo(final FieldDescriptor other)1238 public int compareTo(final FieldDescriptor other) { 1239 if (other.containingType != containingType) { 1240 throw new IllegalArgumentException( 1241 "FieldDescriptors can only be compared to other FieldDescriptors " 1242 + "for fields of the same message type."); 1243 } 1244 return getNumber() - other.getNumber(); 1245 } 1246 1247 @Override toString()1248 public String toString() { 1249 return getFullName(); 1250 } 1251 1252 private final int index; 1253 1254 private FieldDescriptorProto proto; 1255 private final String fullName; 1256 private final String jsonName; 1257 private final FileDescriptor file; 1258 private final Descriptor extensionScope; 1259 private final boolean isProto3Optional; 1260 1261 // Possibly initialized during cross-linking. 1262 private Type type; 1263 private Descriptor containingType; 1264 private Descriptor messageType; 1265 private OneofDescriptor containingOneof; 1266 private EnumDescriptor enumType; 1267 private Object defaultValue; 1268 1269 public enum Type { 1270 DOUBLE(JavaType.DOUBLE), 1271 FLOAT(JavaType.FLOAT), 1272 INT64(JavaType.LONG), 1273 UINT64(JavaType.LONG), 1274 INT32(JavaType.INT), 1275 FIXED64(JavaType.LONG), 1276 FIXED32(JavaType.INT), 1277 BOOL(JavaType.BOOLEAN), 1278 STRING(JavaType.STRING), 1279 GROUP(JavaType.MESSAGE), 1280 MESSAGE(JavaType.MESSAGE), 1281 BYTES(JavaType.BYTE_STRING), 1282 UINT32(JavaType.INT), 1283 ENUM(JavaType.ENUM), 1284 SFIXED32(JavaType.INT), 1285 SFIXED64(JavaType.LONG), 1286 SINT32(JavaType.INT), 1287 SINT64(JavaType.LONG); 1288 Type(final JavaType javaType)1289 Type(final JavaType javaType) { 1290 this.javaType = javaType; 1291 } 1292 1293 private JavaType javaType; 1294 toProto()1295 public FieldDescriptorProto.Type toProto() { 1296 return FieldDescriptorProto.Type.forNumber(ordinal() + 1); 1297 } 1298 getJavaType()1299 public JavaType getJavaType() { 1300 return javaType; 1301 } 1302 valueOf(final FieldDescriptorProto.Type type)1303 public static Type valueOf(final FieldDescriptorProto.Type type) { 1304 return values()[type.getNumber() - 1]; 1305 } 1306 } 1307 1308 static { 1309 // Refuse to init if someone added a new declared type. 1310 if (Type.values().length != FieldDescriptorProto.Type.values().length) { 1311 throw new RuntimeException( 1312 "descriptor.proto has a new declared type but Descriptors.java wasn't updated."); 1313 } 1314 } 1315 1316 public enum JavaType { 1317 INT(0), 1318 LONG(0L), 1319 FLOAT(0F), 1320 DOUBLE(0D), 1321 BOOLEAN(false), 1322 STRING(""), 1323 BYTE_STRING(ByteString.EMPTY), 1324 ENUM(null), 1325 MESSAGE(null); 1326 JavaType(final Object defaultDefault)1327 JavaType(final Object defaultDefault) { 1328 this.defaultDefault = defaultDefault; 1329 } 1330 1331 /** 1332 * The default default value for fields of this type, if it's a primitive type. This is meant 1333 * for use inside this file only, hence is private. 1334 */ 1335 private final Object defaultDefault; 1336 } 1337 1338 // This method should match exactly with the ToJsonName() function in C++ 1339 // descriptor.cc. fieldNameToJsonName(String name)1340 private static String fieldNameToJsonName(String name) { 1341 final int length = name.length(); 1342 StringBuilder result = new StringBuilder(length); 1343 boolean isNextUpperCase = false; 1344 for (int i = 0; i < length; i++) { 1345 char ch = name.charAt(i); 1346 if (ch == '_') { 1347 isNextUpperCase = true; 1348 } else if (isNextUpperCase) { 1349 // This closely matches the logic for ASCII characters in: 1350 // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689 1351 if ('a' <= ch && ch <= 'z') { 1352 ch = (char) (ch - 'a' + 'A'); 1353 } 1354 result.append(ch); 1355 isNextUpperCase = false; 1356 } else { 1357 result.append(ch); 1358 } 1359 } 1360 return result.toString(); 1361 } 1362 FieldDescriptor( final FieldDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index, final boolean isExtension)1363 private FieldDescriptor( 1364 final FieldDescriptorProto proto, 1365 final FileDescriptor file, 1366 final Descriptor parent, 1367 final int index, 1368 final boolean isExtension) 1369 throws DescriptorValidationException { 1370 this.index = index; 1371 this.proto = proto; 1372 fullName = computeFullName(file, parent, proto.getName()); 1373 this.file = file; 1374 if (proto.hasJsonName()) { 1375 jsonName = proto.getJsonName(); 1376 } else { 1377 jsonName = fieldNameToJsonName(proto.getName()); 1378 } 1379 1380 if (proto.hasType()) { 1381 type = Type.valueOf(proto.getType()); 1382 } 1383 1384 isProto3Optional = proto.getProto3Optional(); 1385 1386 if (getNumber() <= 0) { 1387 throw new DescriptorValidationException(this, "Field numbers must be positive integers."); 1388 } 1389 1390 if (isExtension) { 1391 if (!proto.hasExtendee()) { 1392 throw new DescriptorValidationException( 1393 this, "FieldDescriptorProto.extendee not set for extension field."); 1394 } 1395 containingType = null; // Will be filled in when cross-linking 1396 if (parent != null) { 1397 extensionScope = parent; 1398 } else { 1399 extensionScope = null; 1400 } 1401 1402 if (proto.hasOneofIndex()) { 1403 throw new DescriptorValidationException( 1404 this, "FieldDescriptorProto.oneof_index set for extension field."); 1405 } 1406 containingOneof = null; 1407 } else { 1408 if (proto.hasExtendee()) { 1409 throw new DescriptorValidationException( 1410 this, "FieldDescriptorProto.extendee set for non-extension field."); 1411 } 1412 containingType = parent; 1413 1414 if (proto.hasOneofIndex()) { 1415 if (proto.getOneofIndex() < 0 1416 || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) { 1417 throw new DescriptorValidationException( 1418 this, 1419 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName()); 1420 } 1421 containingOneof = parent.getOneofs().get(proto.getOneofIndex()); 1422 containingOneof.fieldCount++; 1423 } else { 1424 containingOneof = null; 1425 } 1426 extensionScope = null; 1427 } 1428 1429 file.pool.addSymbol(this); 1430 } 1431 1432 /** Look up and cross-link all field types, etc. */ crossLink()1433 private void crossLink() throws DescriptorValidationException { 1434 if (proto.hasExtendee()) { 1435 final GenericDescriptor extendee = 1436 file.pool.lookupSymbol( 1437 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 1438 if (!(extendee instanceof Descriptor)) { 1439 throw new DescriptorValidationException( 1440 this, '\"' + proto.getExtendee() + "\" is not a message type."); 1441 } 1442 containingType = (Descriptor) extendee; 1443 1444 if (!getContainingType().isExtensionNumber(getNumber())) { 1445 throw new DescriptorValidationException( 1446 this, 1447 '\"' 1448 + getContainingType().getFullName() 1449 + "\" does not declare " 1450 + getNumber() 1451 + " as an extension number."); 1452 } 1453 } 1454 1455 if (proto.hasTypeName()) { 1456 final GenericDescriptor typeDescriptor = 1457 file.pool.lookupSymbol( 1458 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 1459 1460 if (!proto.hasType()) { 1461 // Choose field type based on symbol. 1462 if (typeDescriptor instanceof Descriptor) { 1463 type = Type.MESSAGE; 1464 } else if (typeDescriptor instanceof EnumDescriptor) { 1465 type = Type.ENUM; 1466 } else { 1467 throw new DescriptorValidationException( 1468 this, '\"' + proto.getTypeName() + "\" is not a type."); 1469 } 1470 } 1471 1472 if (getJavaType() == JavaType.MESSAGE) { 1473 if (!(typeDescriptor instanceof Descriptor)) { 1474 throw new DescriptorValidationException( 1475 this, '\"' + proto.getTypeName() + "\" is not a message type."); 1476 } 1477 messageType = (Descriptor) typeDescriptor; 1478 1479 if (proto.hasDefaultValue()) { 1480 throw new DescriptorValidationException(this, "Messages can't have default values."); 1481 } 1482 } else if (getJavaType() == JavaType.ENUM) { 1483 if (!(typeDescriptor instanceof EnumDescriptor)) { 1484 throw new DescriptorValidationException( 1485 this, '\"' + proto.getTypeName() + "\" is not an enum type."); 1486 } 1487 enumType = (EnumDescriptor) typeDescriptor; 1488 } else { 1489 throw new DescriptorValidationException(this, "Field with primitive type has type_name."); 1490 } 1491 } else { 1492 if (getJavaType() == JavaType.MESSAGE || getJavaType() == JavaType.ENUM) { 1493 throw new DescriptorValidationException( 1494 this, "Field with message or enum type missing type_name."); 1495 } 1496 } 1497 1498 // Only repeated primitive fields may be packed. 1499 if (proto.getOptions().getPacked() && !isPackable()) { 1500 throw new DescriptorValidationException( 1501 this, "[packed = true] can only be specified for repeated primitive fields."); 1502 } 1503 1504 // We don't attempt to parse the default value until here because for 1505 // enums we need the enum type's descriptor. 1506 if (proto.hasDefaultValue()) { 1507 if (isRepeated()) { 1508 throw new DescriptorValidationException( 1509 this, "Repeated fields cannot have default values."); 1510 } 1511 1512 try { 1513 switch (getType()) { 1514 case INT32: 1515 case SINT32: 1516 case SFIXED32: 1517 defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); 1518 break; 1519 case UINT32: 1520 case FIXED32: 1521 defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); 1522 break; 1523 case INT64: 1524 case SINT64: 1525 case SFIXED64: 1526 defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); 1527 break; 1528 case UINT64: 1529 case FIXED64: 1530 defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); 1531 break; 1532 case FLOAT: 1533 if (proto.getDefaultValue().equals("inf")) { 1534 defaultValue = Float.POSITIVE_INFINITY; 1535 } else if (proto.getDefaultValue().equals("-inf")) { 1536 defaultValue = Float.NEGATIVE_INFINITY; 1537 } else if (proto.getDefaultValue().equals("nan")) { 1538 defaultValue = Float.NaN; 1539 } else { 1540 defaultValue = Float.valueOf(proto.getDefaultValue()); 1541 } 1542 break; 1543 case DOUBLE: 1544 if (proto.getDefaultValue().equals("inf")) { 1545 defaultValue = Double.POSITIVE_INFINITY; 1546 } else if (proto.getDefaultValue().equals("-inf")) { 1547 defaultValue = Double.NEGATIVE_INFINITY; 1548 } else if (proto.getDefaultValue().equals("nan")) { 1549 defaultValue = Double.NaN; 1550 } else { 1551 defaultValue = Double.valueOf(proto.getDefaultValue()); 1552 } 1553 break; 1554 case BOOL: 1555 defaultValue = Boolean.valueOf(proto.getDefaultValue()); 1556 break; 1557 case STRING: 1558 defaultValue = proto.getDefaultValue(); 1559 break; 1560 case BYTES: 1561 try { 1562 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue()); 1563 } catch (TextFormat.InvalidEscapeSequenceException e) { 1564 throw new DescriptorValidationException( 1565 this, "Couldn't parse default value: " + e.getMessage(), e); 1566 } 1567 break; 1568 case ENUM: 1569 defaultValue = enumType.findValueByName(proto.getDefaultValue()); 1570 if (defaultValue == null) { 1571 throw new DescriptorValidationException( 1572 this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"'); 1573 } 1574 break; 1575 case MESSAGE: 1576 case GROUP: 1577 throw new DescriptorValidationException(this, "Message type had default value."); 1578 } 1579 } catch (NumberFormatException e) { 1580 throw new DescriptorValidationException( 1581 this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e); 1582 } 1583 } else { 1584 // Determine the default default for this field. 1585 if (isRepeated()) { 1586 defaultValue = Collections.emptyList(); 1587 } else { 1588 switch (getJavaType()) { 1589 case ENUM: 1590 // We guarantee elsewhere that an enum type always has at least 1591 // one possible value. 1592 defaultValue = enumType.getValues().get(0); 1593 break; 1594 case MESSAGE: 1595 defaultValue = null; 1596 break; 1597 default: 1598 defaultValue = getJavaType().defaultDefault; 1599 break; 1600 } 1601 } 1602 } 1603 1604 if (!isExtension()) { 1605 file.pool.addFieldByNumber(this); 1606 } 1607 1608 if (containingType != null && containingType.getOptions().getMessageSetWireFormat()) { 1609 if (isExtension()) { 1610 if (!isOptional() || getType() != Type.MESSAGE) { 1611 throw new DescriptorValidationException( 1612 this, "Extensions of MessageSets must be optional messages."); 1613 } 1614 } else { 1615 throw new DescriptorValidationException( 1616 this, "MessageSets cannot have fields, only extensions."); 1617 } 1618 } 1619 } 1620 1621 /** See {@link FileDescriptor#setProto}. */ setProto(final FieldDescriptorProto proto)1622 private void setProto(final FieldDescriptorProto proto) { 1623 this.proto = proto; 1624 } 1625 1626 /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */ 1627 @Override internalMergeFrom(MessageLite.Builder to, MessageLite from)1628 public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) { 1629 // FieldDescriptors are only used with non-lite messages so we can just 1630 // down-cast and call mergeFrom directly. 1631 return ((Message.Builder) to).mergeFrom((Message) from); 1632 } 1633 1634 } 1635 1636 // ================================================================= 1637 1638 /** Describes an enum type. */ 1639 public static final class EnumDescriptor extends GenericDescriptor 1640 implements Internal.EnumLiteMap<EnumValueDescriptor> { 1641 /** 1642 * Get the index of this descriptor within its parent. 1643 * 1644 * @see Descriptors.Descriptor#getIndex() 1645 */ getIndex()1646 public int getIndex() { 1647 return index; 1648 } 1649 1650 /** Convert the descriptor to its protocol message representation. */ 1651 @Override toProto()1652 public EnumDescriptorProto toProto() { 1653 return proto; 1654 } 1655 1656 /** Get the type's unqualified name. */ 1657 @Override getName()1658 public String getName() { 1659 return proto.getName(); 1660 } 1661 1662 /** 1663 * Get the type's fully-qualified name. 1664 * 1665 * @see Descriptors.Descriptor#getFullName() 1666 */ 1667 @Override getFullName()1668 public String getFullName() { 1669 return fullName; 1670 } 1671 1672 /** Get the {@link FileDescriptor} containing this descriptor. */ 1673 @Override getFile()1674 public FileDescriptor getFile() { 1675 return file; 1676 } 1677 1678 /** If this is a nested type, get the outer descriptor, otherwise null. */ getContainingType()1679 public Descriptor getContainingType() { 1680 return containingType; 1681 } 1682 1683 /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ getOptions()1684 public EnumOptions getOptions() { 1685 return proto.getOptions(); 1686 } 1687 1688 /** Get a list of defined values for this enum. */ getValues()1689 public List<EnumValueDescriptor> getValues() { 1690 return Collections.unmodifiableList(Arrays.asList(values)); 1691 } 1692 1693 /** 1694 * Find an enum value by name. 1695 * 1696 * @param name The unqualified name of the value (e.g. "FOO"). 1697 * @return the value's descriptor, or {@code null} if not found. 1698 */ findValueByName(final String name)1699 public EnumValueDescriptor findValueByName(final String name) { 1700 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 1701 if (result != null && result instanceof EnumValueDescriptor) { 1702 return (EnumValueDescriptor) result; 1703 } else { 1704 return null; 1705 } 1706 } 1707 1708 /** 1709 * Find an enum value by number. If multiple enum values have the same number, this returns the 1710 * first defined value with that number. 1711 * 1712 * @param number The value's number. 1713 * @return the value's descriptor, or {@code null} if not found. 1714 */ 1715 @Override findValueByNumber(final int number)1716 public EnumValueDescriptor findValueByNumber(final int number) { 1717 return file.pool.enumValuesByNumber.get(new DescriptorPool.DescriptorIntPair(this, number)); 1718 } 1719 1720 /** 1721 * Get the enum value for a number. If no enum value has this number, construct an 1722 * EnumValueDescriptor for it. 1723 */ findValueByNumberCreatingIfUnknown(final int number)1724 public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) { 1725 EnumValueDescriptor result = findValueByNumber(number); 1726 if (result != null) { 1727 return result; 1728 } 1729 // The number represents an unknown enum value. 1730 synchronized (this) { 1731 // Descriptors are compared by object identity so for the same number 1732 // we need to return the same EnumValueDescriptor object. This means 1733 // we have to store created EnumValueDescriptors. However, as there 1734 // are potentially 2G unknown enum values, storing all of these 1735 // objects persistently will consume lots of memory for long-running 1736 // services and it's also unnecessary as not many EnumValueDescriptors 1737 // will be used at the same time. 1738 // 1739 // To solve the problem we take advantage of Java's weak references and 1740 // rely on gc to release unused descriptors. 1741 // 1742 // Here is how it works: 1743 // * We store unknown EnumValueDescriptors in a WeakHashMap with the 1744 // value being a weak reference to the descriptor. 1745 // * The descriptor holds a strong reference to the key so as long 1746 // as the EnumValueDescriptor is in use, the key will be there 1747 // and the corresponding map entry will be there. Following-up 1748 // queries with the same number will return the same descriptor. 1749 // * If the user no longer uses an unknown EnumValueDescriptor, 1750 // it will be gc-ed since we only hold a weak reference to it in 1751 // the map. The key in the corresponding map entry will also be 1752 // gc-ed as the only strong reference to it is in the descriptor 1753 // which is just gc-ed. With the key being gone WeakHashMap will 1754 // then remove the whole entry. This way unknown descriptors will 1755 // be freed automatically and we don't need to do anything to 1756 // clean-up unused map entries. 1757 1758 // Note: We must use "new Integer(number)" here because we don't want 1759 // these Integer objects to be cached. 1760 Integer key = new Integer(number); 1761 WeakReference<EnumValueDescriptor> reference = unknownValues.get(key); 1762 if (reference != null) { 1763 result = reference.get(); 1764 } 1765 if (result == null) { 1766 result = new EnumValueDescriptor(file, this, key); 1767 unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result)); 1768 } 1769 } 1770 return result; 1771 } 1772 1773 // Used in tests only. getUnknownEnumValueDescriptorCount()1774 int getUnknownEnumValueDescriptorCount() { 1775 return unknownValues.size(); 1776 } 1777 1778 private final int index; 1779 private EnumDescriptorProto proto; 1780 private final String fullName; 1781 private final FileDescriptor file; 1782 private final Descriptor containingType; 1783 private EnumValueDescriptor[] values; 1784 private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues = 1785 new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>(); 1786 EnumDescriptor( final EnumDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)1787 private EnumDescriptor( 1788 final EnumDescriptorProto proto, 1789 final FileDescriptor file, 1790 final Descriptor parent, 1791 final int index) 1792 throws DescriptorValidationException { 1793 this.index = index; 1794 this.proto = proto; 1795 fullName = computeFullName(file, parent, proto.getName()); 1796 this.file = file; 1797 containingType = parent; 1798 1799 if (proto.getValueCount() == 0) { 1800 // We cannot allow enums with no values because this would mean there 1801 // would be no valid default value for fields of this type. 1802 throw new DescriptorValidationException(this, "Enums must contain at least one value."); 1803 } 1804 1805 values = new EnumValueDescriptor[proto.getValueCount()]; 1806 for (int i = 0; i < proto.getValueCount(); i++) { 1807 values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i); 1808 } 1809 1810 file.pool.addSymbol(this); 1811 } 1812 1813 /** See {@link FileDescriptor#setProto}. */ setProto(final EnumDescriptorProto proto)1814 private void setProto(final EnumDescriptorProto proto) { 1815 this.proto = proto; 1816 1817 for (int i = 0; i < values.length; i++) { 1818 values[i].setProto(proto.getValue(i)); 1819 } 1820 } 1821 } 1822 1823 // ================================================================= 1824 1825 /** 1826 * Describes one value within an enum type. Note that multiple defined values may have the same 1827 * number. In generated Java code, all values with the same number after the first become aliases 1828 * of the first. However, they still have independent EnumValueDescriptors. 1829 */ 1830 public static final class EnumValueDescriptor extends GenericDescriptor 1831 implements Internal.EnumLite { 1832 /** 1833 * Get the index of this descriptor within its parent. 1834 * 1835 * @see Descriptors.Descriptor#getIndex() 1836 */ getIndex()1837 public int getIndex() { 1838 return index; 1839 } 1840 1841 /** Convert the descriptor to its protocol message representation. */ 1842 @Override toProto()1843 public EnumValueDescriptorProto toProto() { 1844 return proto; 1845 } 1846 1847 /** Get the value's unqualified name. */ 1848 @Override getName()1849 public String getName() { 1850 return proto.getName(); 1851 } 1852 1853 /** Get the value's number. */ 1854 @Override getNumber()1855 public int getNumber() { 1856 return proto.getNumber(); 1857 } 1858 1859 @Override toString()1860 public String toString() { 1861 return proto.getName(); 1862 } 1863 1864 /** 1865 * Get the value's fully-qualified name. 1866 * 1867 * @see Descriptors.Descriptor#getFullName() 1868 */ 1869 @Override getFullName()1870 public String getFullName() { 1871 return fullName; 1872 } 1873 1874 /** Get the {@link FileDescriptor} containing this descriptor. */ 1875 @Override getFile()1876 public FileDescriptor getFile() { 1877 return file; 1878 } 1879 1880 /** Get the value's enum type. */ getType()1881 public EnumDescriptor getType() { 1882 return type; 1883 } 1884 1885 /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */ getOptions()1886 public EnumValueOptions getOptions() { 1887 return proto.getOptions(); 1888 } 1889 1890 private final int index; 1891 private EnumValueDescriptorProto proto; 1892 private final String fullName; 1893 private final FileDescriptor file; 1894 private final EnumDescriptor type; 1895 EnumValueDescriptor( final EnumValueDescriptorProto proto, final FileDescriptor file, final EnumDescriptor parent, final int index)1896 private EnumValueDescriptor( 1897 final EnumValueDescriptorProto proto, 1898 final FileDescriptor file, 1899 final EnumDescriptor parent, 1900 final int index) 1901 throws DescriptorValidationException { 1902 this.index = index; 1903 this.proto = proto; 1904 this.file = file; 1905 type = parent; 1906 1907 fullName = parent.getFullName() + '.' + proto.getName(); 1908 1909 file.pool.addSymbol(this); 1910 file.pool.addEnumValueByNumber(this); 1911 } 1912 1913 // Create an unknown enum value. EnumValueDescriptor( final FileDescriptor file, final EnumDescriptor parent, final Integer number)1914 private EnumValueDescriptor( 1915 final FileDescriptor file, final EnumDescriptor parent, final Integer number) { 1916 String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number; 1917 EnumValueDescriptorProto proto = 1918 EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build(); 1919 this.index = -1; 1920 this.proto = proto; 1921 this.file = file; 1922 this.type = parent; 1923 this.fullName = parent.getFullName() + '.' + proto.getName(); 1924 1925 // Don't add this descriptor into pool. 1926 } 1927 1928 /** See {@link FileDescriptor#setProto}. */ setProto(final EnumValueDescriptorProto proto)1929 private void setProto(final EnumValueDescriptorProto proto) { 1930 this.proto = proto; 1931 } 1932 } 1933 1934 // ================================================================= 1935 1936 /** Describes a service type. */ 1937 public static final class ServiceDescriptor extends GenericDescriptor { 1938 /** 1939 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex() 1940 */ getIndex()1941 public int getIndex() { 1942 return index; 1943 } 1944 1945 /** Convert the descriptor to its protocol message representation. */ 1946 @Override toProto()1947 public ServiceDescriptorProto toProto() { 1948 return proto; 1949 } 1950 1951 /** Get the type's unqualified name. */ 1952 @Override getName()1953 public String getName() { 1954 return proto.getName(); 1955 } 1956 1957 /** 1958 * Get the type's fully-qualified name. 1959 * 1960 * @see Descriptors.Descriptor#getFullName() 1961 */ 1962 @Override getFullName()1963 public String getFullName() { 1964 return fullName; 1965 } 1966 1967 /** Get the {@link FileDescriptor} containing this descriptor. */ 1968 @Override getFile()1969 public FileDescriptor getFile() { 1970 return file; 1971 } 1972 1973 /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ getOptions()1974 public ServiceOptions getOptions() { 1975 return proto.getOptions(); 1976 } 1977 1978 /** Get a list of methods for this service. */ getMethods()1979 public List<MethodDescriptor> getMethods() { 1980 return Collections.unmodifiableList(Arrays.asList(methods)); 1981 } 1982 1983 /** 1984 * Find a method by name. 1985 * 1986 * @param name The unqualified name of the method (e.g. "Foo"). 1987 * @return the method's descriptor, or {@code null} if not found. 1988 */ findMethodByName(final String name)1989 public MethodDescriptor findMethodByName(final String name) { 1990 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 1991 if (result != null && result instanceof MethodDescriptor) { 1992 return (MethodDescriptor) result; 1993 } else { 1994 return null; 1995 } 1996 } 1997 1998 private final int index; 1999 private ServiceDescriptorProto proto; 2000 private final String fullName; 2001 private final FileDescriptor file; 2002 private MethodDescriptor[] methods; 2003 ServiceDescriptor( final ServiceDescriptorProto proto, final FileDescriptor file, final int index)2004 private ServiceDescriptor( 2005 final ServiceDescriptorProto proto, final FileDescriptor file, final int index) 2006 throws DescriptorValidationException { 2007 this.index = index; 2008 this.proto = proto; 2009 fullName = computeFullName(file, null, proto.getName()); 2010 this.file = file; 2011 2012 methods = new MethodDescriptor[proto.getMethodCount()]; 2013 for (int i = 0; i < proto.getMethodCount(); i++) { 2014 methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i); 2015 } 2016 2017 file.pool.addSymbol(this); 2018 } 2019 crossLink()2020 private void crossLink() throws DescriptorValidationException { 2021 for (final MethodDescriptor method : methods) { 2022 method.crossLink(); 2023 } 2024 } 2025 2026 /** See {@link FileDescriptor#setProto}. */ setProto(final ServiceDescriptorProto proto)2027 private void setProto(final ServiceDescriptorProto proto) { 2028 this.proto = proto; 2029 2030 for (int i = 0; i < methods.length; i++) { 2031 methods[i].setProto(proto.getMethod(i)); 2032 } 2033 } 2034 } 2035 2036 // ================================================================= 2037 2038 /** Describes one method within a service type. */ 2039 public static final class MethodDescriptor extends GenericDescriptor { 2040 /** 2041 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex() 2042 */ getIndex()2043 public int getIndex() { 2044 return index; 2045 } 2046 2047 /** Convert the descriptor to its protocol message representation. */ 2048 @Override toProto()2049 public MethodDescriptorProto toProto() { 2050 return proto; 2051 } 2052 2053 /** Get the method's unqualified name. */ 2054 @Override getName()2055 public String getName() { 2056 return proto.getName(); 2057 } 2058 2059 /** 2060 * Get the method's fully-qualified name. 2061 * 2062 * @see Descriptors.Descriptor#getFullName() 2063 */ 2064 @Override getFullName()2065 public String getFullName() { 2066 return fullName; 2067 } 2068 2069 /** Get the {@link FileDescriptor} containing this descriptor. */ 2070 @Override getFile()2071 public FileDescriptor getFile() { 2072 return file; 2073 } 2074 2075 /** Get the method's service type. */ getService()2076 public ServiceDescriptor getService() { 2077 return service; 2078 } 2079 2080 /** Get the method's input type. */ getInputType()2081 public Descriptor getInputType() { 2082 return inputType; 2083 } 2084 2085 /** Get the method's output type. */ getOutputType()2086 public Descriptor getOutputType() { 2087 return outputType; 2088 } 2089 2090 /** Get whether or not the inputs are streaming. */ isClientStreaming()2091 public boolean isClientStreaming() { 2092 return proto.getClientStreaming(); 2093 } 2094 2095 /** Get whether or not the outputs are streaming. */ isServerStreaming()2096 public boolean isServerStreaming() { 2097 return proto.getServerStreaming(); 2098 } 2099 2100 /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */ getOptions()2101 public MethodOptions getOptions() { 2102 return proto.getOptions(); 2103 } 2104 2105 private final int index; 2106 private MethodDescriptorProto proto; 2107 private final String fullName; 2108 private final FileDescriptor file; 2109 private final ServiceDescriptor service; 2110 2111 // Initialized during cross-linking. 2112 private Descriptor inputType; 2113 private Descriptor outputType; 2114 MethodDescriptor( final MethodDescriptorProto proto, final FileDescriptor file, final ServiceDescriptor parent, final int index)2115 private MethodDescriptor( 2116 final MethodDescriptorProto proto, 2117 final FileDescriptor file, 2118 final ServiceDescriptor parent, 2119 final int index) 2120 throws DescriptorValidationException { 2121 this.index = index; 2122 this.proto = proto; 2123 this.file = file; 2124 service = parent; 2125 2126 fullName = parent.getFullName() + '.' + proto.getName(); 2127 2128 file.pool.addSymbol(this); 2129 } 2130 crossLink()2131 private void crossLink() throws DescriptorValidationException { 2132 final GenericDescriptor input = 2133 file.pool.lookupSymbol( 2134 proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 2135 if (!(input instanceof Descriptor)) { 2136 throw new DescriptorValidationException( 2137 this, '\"' + proto.getInputType() + "\" is not a message type."); 2138 } 2139 inputType = (Descriptor) input; 2140 2141 final GenericDescriptor output = 2142 file.pool.lookupSymbol( 2143 proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 2144 if (!(output instanceof Descriptor)) { 2145 throw new DescriptorValidationException( 2146 this, '\"' + proto.getOutputType() + "\" is not a message type."); 2147 } 2148 outputType = (Descriptor) output; 2149 } 2150 2151 /** See {@link FileDescriptor#setProto}. */ setProto(final MethodDescriptorProto proto)2152 private void setProto(final MethodDescriptorProto proto) { 2153 this.proto = proto; 2154 } 2155 } 2156 2157 // ================================================================= 2158 computeFullName( final FileDescriptor file, final Descriptor parent, final String name)2159 private static String computeFullName( 2160 final FileDescriptor file, final Descriptor parent, final String name) { 2161 if (parent != null) { 2162 return parent.getFullName() + '.' + name; 2163 } 2164 2165 final String packageName = file.getPackage(); 2166 if (!packageName.isEmpty()) { 2167 return packageName + '.' + name; 2168 } 2169 2170 return name; 2171 } 2172 2173 // ================================================================= 2174 2175 /** 2176 * All descriptors implement this to make it easier to implement tools like {@code 2177 * DescriptorPool}. 2178 */ 2179 public abstract static class GenericDescriptor { 2180 2181 // Private constructor to prevent subclasses outside of com.google.protobuf.Descriptors GenericDescriptor()2182 private GenericDescriptor() {} 2183 toProto()2184 public abstract Message toProto(); 2185 getName()2186 public abstract String getName(); 2187 getFullName()2188 public abstract String getFullName(); 2189 getFile()2190 public abstract FileDescriptor getFile(); 2191 } 2192 2193 /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */ 2194 public static class DescriptorValidationException extends Exception { 2195 private static final long serialVersionUID = 5750205775490483148L; 2196 2197 /** Gets the full name of the descriptor where the error occurred. */ getProblemSymbolName()2198 public String getProblemSymbolName() { 2199 return name; 2200 } 2201 2202 /** Gets the protocol message representation of the invalid descriptor. */ getProblemProto()2203 public Message getProblemProto() { 2204 return proto; 2205 } 2206 2207 /** Gets a human-readable description of the error. */ getDescription()2208 public String getDescription() { 2209 return description; 2210 } 2211 2212 private final String name; 2213 private final Message proto; 2214 private final String description; 2215 DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description)2216 private DescriptorValidationException( 2217 final GenericDescriptor problemDescriptor, final String description) { 2218 super(problemDescriptor.getFullName() + ": " + description); 2219 2220 // Note that problemDescriptor may be partially uninitialized, so we 2221 // don't want to expose it directly to the user. So, we only provide 2222 // the name and the original proto. 2223 name = problemDescriptor.getFullName(); 2224 proto = problemDescriptor.toProto(); 2225 this.description = description; 2226 } 2227 DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description, final Throwable cause)2228 private DescriptorValidationException( 2229 final GenericDescriptor problemDescriptor, 2230 final String description, 2231 final Throwable cause) { 2232 this(problemDescriptor, description); 2233 initCause(cause); 2234 } 2235 DescriptorValidationException( final FileDescriptor problemDescriptor, final String description)2236 private DescriptorValidationException( 2237 final FileDescriptor problemDescriptor, final String description) { 2238 super(problemDescriptor.getName() + ": " + description); 2239 2240 // Note that problemDescriptor may be partially uninitialized, so we 2241 // don't want to expose it directly to the user. So, we only provide 2242 // the name and the original proto. 2243 name = problemDescriptor.getName(); 2244 proto = problemDescriptor.toProto(); 2245 this.description = description; 2246 } 2247 } 2248 2249 // ================================================================= 2250 2251 /** 2252 * A private helper class which contains lookup tables containing all the descriptors defined in a 2253 * particular file. 2254 */ 2255 private static final class DescriptorPool { 2256 2257 /** Defines what subclass of descriptors to search in the descriptor pool. */ 2258 enum SearchFilter { 2259 TYPES_ONLY, 2260 AGGREGATES_ONLY, 2261 ALL_SYMBOLS 2262 } 2263 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies)2264 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) { 2265 this.dependencies = new HashSet<FileDescriptor>(); 2266 this.allowUnknownDependencies = allowUnknownDependencies; 2267 2268 for (int i = 0; i < dependencies.length; i++) { 2269 this.dependencies.add(dependencies[i]); 2270 importPublicDependencies(dependencies[i]); 2271 } 2272 2273 for (final FileDescriptor dependency : this.dependencies) { 2274 try { 2275 addPackage(dependency.getPackage(), dependency); 2276 } catch (DescriptorValidationException e) { 2277 // Can't happen, because addPackage() only fails when the name 2278 // conflicts with a non-package, but we have not yet added any 2279 // non-packages at this point. 2280 throw new AssertionError(e); 2281 } 2282 } 2283 } 2284 2285 /** Find and put public dependencies of the file into dependencies set. */ importPublicDependencies(final FileDescriptor file)2286 private void importPublicDependencies(final FileDescriptor file) { 2287 for (FileDescriptor dependency : file.getPublicDependencies()) { 2288 if (dependencies.add(dependency)) { 2289 importPublicDependencies(dependency); 2290 } 2291 } 2292 } 2293 2294 private final Set<FileDescriptor> dependencies; 2295 private boolean allowUnknownDependencies; 2296 2297 private final Map<String, GenericDescriptor> descriptorsByName = 2298 new HashMap<String, GenericDescriptor>(); 2299 private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber = 2300 new HashMap<DescriptorIntPair, FieldDescriptor>(); 2301 private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber = 2302 new HashMap<DescriptorIntPair, EnumValueDescriptor>(); 2303 2304 /** Find a generic descriptor by fully-qualified name. */ findSymbol(final String fullName)2305 GenericDescriptor findSymbol(final String fullName) { 2306 return findSymbol(fullName, SearchFilter.ALL_SYMBOLS); 2307 } 2308 2309 /** 2310 * Find a descriptor by fully-qualified name and given option to only search valid field type 2311 * descriptors. 2312 */ findSymbol(final String fullName, final SearchFilter filter)2313 GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) { 2314 GenericDescriptor result = descriptorsByName.get(fullName); 2315 if (result != null) { 2316 if ((filter == SearchFilter.ALL_SYMBOLS) 2317 || ((filter == SearchFilter.TYPES_ONLY) && isType(result)) 2318 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { 2319 return result; 2320 } 2321 } 2322 2323 for (final FileDescriptor dependency : dependencies) { 2324 result = dependency.pool.descriptorsByName.get(fullName); 2325 if (result != null) { 2326 if ((filter == SearchFilter.ALL_SYMBOLS) 2327 || ((filter == SearchFilter.TYPES_ONLY) && isType(result)) 2328 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { 2329 return result; 2330 } 2331 } 2332 } 2333 2334 return null; 2335 } 2336 2337 /** Checks if the descriptor is a valid type for a message field. */ isType(GenericDescriptor descriptor)2338 boolean isType(GenericDescriptor descriptor) { 2339 return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor); 2340 } 2341 2342 /** Checks if the descriptor is a valid namespace type. */ isAggregate(GenericDescriptor descriptor)2343 boolean isAggregate(GenericDescriptor descriptor) { 2344 return (descriptor instanceof Descriptor) 2345 || (descriptor instanceof EnumDescriptor) 2346 || (descriptor instanceof PackageDescriptor) 2347 || (descriptor instanceof ServiceDescriptor); 2348 } 2349 2350 /** 2351 * Look up a type descriptor by name, relative to some other descriptor. The name may be 2352 * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name 2353 * lookup semantics are used to search for the matching descriptor. 2354 */ lookupSymbol( final String name, final GenericDescriptor relativeTo, final DescriptorPool.SearchFilter filter)2355 GenericDescriptor lookupSymbol( 2356 final String name, 2357 final GenericDescriptor relativeTo, 2358 final DescriptorPool.SearchFilter filter) 2359 throws DescriptorValidationException { 2360 // TODO(kenton): This could be optimized in a number of ways. 2361 2362 GenericDescriptor result; 2363 String fullname; 2364 if (name.startsWith(".")) { 2365 // Fully-qualified name. 2366 fullname = name.substring(1); 2367 result = findSymbol(fullname, filter); 2368 } else { 2369 // If "name" is a compound identifier, we want to search for the 2370 // first component of it, then search within it for the rest. 2371 // If name is something like "Foo.Bar.baz", and symbols named "Foo" are 2372 // defined in multiple parent scopes, we only want to find "Bar.baz" in 2373 // the innermost one. E.g., the following should produce an error: 2374 // message Bar { message Baz {} } 2375 // message Foo { 2376 // message Bar { 2377 // } 2378 // optional Bar.Baz baz = 1; 2379 // } 2380 // So, we look for just "Foo" first, then look for "Bar.baz" within it 2381 // if found. 2382 final int firstPartLength = name.indexOf('.'); 2383 final String firstPart; 2384 if (firstPartLength == -1) { 2385 firstPart = name; 2386 } else { 2387 firstPart = name.substring(0, firstPartLength); 2388 } 2389 2390 // We will search each parent scope of "relativeTo" looking for the 2391 // symbol. 2392 final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName()); 2393 2394 while (true) { 2395 // Chop off the last component of the scope. 2396 final int dotpos = scopeToTry.lastIndexOf("."); 2397 if (dotpos == -1) { 2398 fullname = name; 2399 result = findSymbol(name, filter); 2400 break; 2401 } else { 2402 scopeToTry.setLength(dotpos + 1); 2403 2404 // Append firstPart and try to find 2405 scopeToTry.append(firstPart); 2406 result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY); 2407 2408 if (result != null) { 2409 if (firstPartLength != -1) { 2410 // We only found the first part of the symbol. Now look for 2411 // the whole thing. If this fails, we *don't* want to keep 2412 // searching parent scopes. 2413 scopeToTry.setLength(dotpos + 1); 2414 scopeToTry.append(name); 2415 result = findSymbol(scopeToTry.toString(), filter); 2416 } 2417 fullname = scopeToTry.toString(); 2418 break; 2419 } 2420 2421 // Not found. Remove the name so we can try again. 2422 scopeToTry.setLength(dotpos); 2423 } 2424 } 2425 } 2426 2427 if (result == null) { 2428 if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) { 2429 logger.warning( 2430 "The descriptor for message type \"" 2431 + name 2432 + "\" can not be found and a placeholder is created for it"); 2433 // We create a dummy message descriptor here regardless of the 2434 // expected type. If the type should be message, this dummy 2435 // descriptor will work well and if the type should be enum, a 2436 // DescriptorValidationException will be thrown latter. In either 2437 // case, the code works as expected: we allow unknown message types 2438 // but not unknown enum types. 2439 result = new Descriptor(fullname); 2440 // Add the placeholder file as a dependency so we can find the 2441 // placeholder symbol when resolving other references. 2442 this.dependencies.add(result.getFile()); 2443 return result; 2444 } else { 2445 throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined."); 2446 } 2447 } else { 2448 return result; 2449 } 2450 } 2451 2452 /** 2453 * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an 2454 * error. 2455 */ addSymbol(final GenericDescriptor descriptor)2456 void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException { 2457 validateSymbolName(descriptor); 2458 2459 final String fullName = descriptor.getFullName(); 2460 2461 final GenericDescriptor old = descriptorsByName.put(fullName, descriptor); 2462 if (old != null) { 2463 descriptorsByName.put(fullName, old); 2464 2465 if (descriptor.getFile() == old.getFile()) { 2466 final int dotpos = fullName.lastIndexOf('.'); 2467 if (dotpos == -1) { 2468 throw new DescriptorValidationException( 2469 descriptor, '\"' + fullName + "\" is already defined."); 2470 } else { 2471 throw new DescriptorValidationException( 2472 descriptor, 2473 '\"' 2474 + fullName.substring(dotpos + 1) 2475 + "\" is already defined in \"" 2476 + fullName.substring(0, dotpos) 2477 + "\"."); 2478 } 2479 } else { 2480 throw new DescriptorValidationException( 2481 descriptor, 2482 '\"' 2483 + fullName 2484 + "\" is already defined in file \"" 2485 + old.getFile().getName() 2486 + "\"."); 2487 } 2488 } 2489 } 2490 2491 /** 2492 * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so 2493 * that someone cannot define, say, a message type that has the same name as an existing 2494 * package. 2495 */ 2496 private static final class PackageDescriptor extends GenericDescriptor { 2497 @Override toProto()2498 public Message toProto() { 2499 return file.toProto(); 2500 } 2501 2502 @Override getName()2503 public String getName() { 2504 return name; 2505 } 2506 2507 @Override getFullName()2508 public String getFullName() { 2509 return fullName; 2510 } 2511 2512 @Override getFile()2513 public FileDescriptor getFile() { 2514 return file; 2515 } 2516 PackageDescriptor(final String name, final String fullName, final FileDescriptor file)2517 PackageDescriptor(final String name, final String fullName, final FileDescriptor file) { 2518 this.file = file; 2519 this.fullName = fullName; 2520 this.name = name; 2521 } 2522 2523 private final String name; 2524 private final String fullName; 2525 private final FileDescriptor file; 2526 } 2527 2528 /** 2529 * Adds a package to the symbol tables. If a package by the same name already exists, that is 2530 * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If 2531 * the package has multiple components, this also adds the parent package(s). 2532 */ addPackage(final String fullName, final FileDescriptor file)2533 void addPackage(final String fullName, final FileDescriptor file) 2534 throws DescriptorValidationException { 2535 final int dotpos = fullName.lastIndexOf('.'); 2536 final String name; 2537 if (dotpos == -1) { 2538 name = fullName; 2539 } else { 2540 addPackage(fullName.substring(0, dotpos), file); 2541 name = fullName.substring(dotpos + 1); 2542 } 2543 2544 final GenericDescriptor old = 2545 descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file)); 2546 if (old != null) { 2547 descriptorsByName.put(fullName, old); 2548 if (!(old instanceof PackageDescriptor)) { 2549 throw new DescriptorValidationException( 2550 file, 2551 '\"' 2552 + name 2553 + "\" is already defined (as something other than a " 2554 + "package) in file \"" 2555 + old.getFile().getName() 2556 + "\"."); 2557 } 2558 } 2559 } 2560 2561 /** A (GenericDescriptor, int) pair, used as a map key. */ 2562 private static final class DescriptorIntPair { 2563 private final GenericDescriptor descriptor; 2564 private final int number; 2565 DescriptorIntPair(final GenericDescriptor descriptor, final int number)2566 DescriptorIntPair(final GenericDescriptor descriptor, final int number) { 2567 this.descriptor = descriptor; 2568 this.number = number; 2569 } 2570 2571 @Override hashCode()2572 public int hashCode() { 2573 return descriptor.hashCode() * ((1 << 16) - 1) + number; 2574 } 2575 2576 @Override equals(final Object obj)2577 public boolean equals(final Object obj) { 2578 if (!(obj instanceof DescriptorIntPair)) { 2579 return false; 2580 } 2581 final DescriptorIntPair other = (DescriptorIntPair) obj; 2582 return descriptor == other.descriptor && number == other.number; 2583 } 2584 } 2585 2586 /** 2587 * Adds a field to the fieldsByNumber table. Throws an exception if a field with the same 2588 * containing type and number already exists. 2589 */ addFieldByNumber(final FieldDescriptor field)2590 void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException { 2591 final DescriptorIntPair key = 2592 new DescriptorIntPair(field.getContainingType(), field.getNumber()); 2593 final FieldDescriptor old = fieldsByNumber.put(key, field); 2594 if (old != null) { 2595 fieldsByNumber.put(key, old); 2596 throw new DescriptorValidationException( 2597 field, 2598 "Field number " 2599 + field.getNumber() 2600 + " has already been used in \"" 2601 + field.getContainingType().getFullName() 2602 + "\" by field \"" 2603 + old.getName() 2604 + "\"."); 2605 } 2606 } 2607 2608 /** 2609 * Adds an enum value to the enumValuesByNumber table. If an enum value with the same type and 2610 * number already exists, does nothing. (This is allowed; the first value define with the number 2611 * takes precedence.) 2612 */ addEnumValueByNumber(final EnumValueDescriptor value)2613 void addEnumValueByNumber(final EnumValueDescriptor value) { 2614 final DescriptorIntPair key = new DescriptorIntPair(value.getType(), value.getNumber()); 2615 final EnumValueDescriptor old = enumValuesByNumber.put(key, value); 2616 if (old != null) { 2617 enumValuesByNumber.put(key, old); 2618 // Not an error: Multiple enum values may have the same number, but 2619 // we only want the first one in the map. 2620 } 2621 } 2622 2623 /** 2624 * Verifies that the descriptor's name is valid (i.e. it contains only letters, digits, and 2625 * underscores, and does not start with a digit). 2626 */ validateSymbolName(final GenericDescriptor descriptor)2627 static void validateSymbolName(final GenericDescriptor descriptor) 2628 throws DescriptorValidationException { 2629 final String name = descriptor.getName(); 2630 if (name.length() == 0) { 2631 throw new DescriptorValidationException(descriptor, "Missing name."); 2632 } 2633 2634 // Non-ASCII characters are not valid in protobuf identifiers, even 2635 // if they are letters or digits. 2636 // The first character must be a letter or '_'. 2637 // Subsequent characters may be letters, numbers, or digits. 2638 for (int i = 0; i < name.length(); i++) { 2639 final char c = name.charAt(i); 2640 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 2641 || (c == '_') 2642 || ('0' <= c && c <= '9' && i > 0)) { 2643 // Valid 2644 continue; 2645 } 2646 throw new DescriptorValidationException( 2647 descriptor, '\"' + name + "\" is not a valid identifier."); 2648 } 2649 } 2650 } 2651 2652 /** Describes an oneof of a message type. */ 2653 public static final class OneofDescriptor extends GenericDescriptor { 2654 /** Get the index of this descriptor within its parent. */ getIndex()2655 public int getIndex() { 2656 return index; 2657 } 2658 2659 @Override getName()2660 public String getName() { 2661 return proto.getName(); 2662 } 2663 2664 @Override getFile()2665 public FileDescriptor getFile() { 2666 return file; 2667 } 2668 2669 @Override getFullName()2670 public String getFullName() { 2671 return fullName; 2672 } 2673 getContainingType()2674 public Descriptor getContainingType() { 2675 return containingType; 2676 } 2677 getFieldCount()2678 public int getFieldCount() { 2679 return fieldCount; 2680 } 2681 getOptions()2682 public OneofOptions getOptions() { 2683 return proto.getOptions(); 2684 } 2685 isSynthetic()2686 public boolean isSynthetic() { 2687 return fields.length == 1 && fields[0].isProto3Optional; 2688 } 2689 2690 /** Get a list of this message type's fields. */ getFields()2691 public List<FieldDescriptor> getFields() { 2692 return Collections.unmodifiableList(Arrays.asList(fields)); 2693 } 2694 getField(int index)2695 public FieldDescriptor getField(int index) { 2696 return fields[index]; 2697 } 2698 2699 @Override toProto()2700 public OneofDescriptorProto toProto() { 2701 return proto; 2702 } 2703 setProto(final OneofDescriptorProto proto)2704 private void setProto(final OneofDescriptorProto proto) { 2705 this.proto = proto; 2706 } 2707 OneofDescriptor( final OneofDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)2708 private OneofDescriptor( 2709 final OneofDescriptorProto proto, 2710 final FileDescriptor file, 2711 final Descriptor parent, 2712 final int index) 2713 throws DescriptorValidationException { 2714 this.proto = proto; 2715 fullName = computeFullName(file, parent, proto.getName()); 2716 this.file = file; 2717 this.index = index; 2718 2719 containingType = parent; 2720 fieldCount = 0; 2721 } 2722 2723 private final int index; 2724 private OneofDescriptorProto proto; 2725 private final String fullName; 2726 private final FileDescriptor file; 2727 2728 private Descriptor containingType; 2729 private int fieldCount; 2730 private FieldDescriptor[] fields; 2731 } 2732 } 2733