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"). For protocol buffer messages that 768 * follow <a 769 * href=https://developers.google.com/protocol-buffers/docs/style#message_and_field_names>Google's 770 * guidance on naming</a> this will be a snake case string, such as <pre>song_name</pre>. 771 * @return The field's descriptor, or {@code null} if not found. 772 */ findFieldByName(final String name)773 public FieldDescriptor findFieldByName(final String name) { 774 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 775 if (result != null && result instanceof FieldDescriptor) { 776 return (FieldDescriptor) result; 777 } else { 778 return null; 779 } 780 } 781 782 /** 783 * Finds a field by field number. 784 * 785 * @param number The field number within this message type. 786 * @return The field's descriptor, or {@code null} if not found. 787 */ findFieldByNumber(final int number)788 public FieldDescriptor findFieldByNumber(final int number) { 789 return file.pool.fieldsByNumber.get(new DescriptorPool.DescriptorIntPair(this, number)); 790 } 791 792 /** 793 * Finds a nested message type by name. 794 * 795 * @param name The unqualified name of the nested type (e.g. "Foo"). 796 * @return The types's descriptor, or {@code null} if not found. 797 */ findNestedTypeByName(final String name)798 public Descriptor findNestedTypeByName(final String name) { 799 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 800 if (result != null && result instanceof Descriptor) { 801 return (Descriptor) result; 802 } else { 803 return null; 804 } 805 } 806 807 /** 808 * Finds a nested enum type by name. 809 * 810 * @param name The unqualified name of the nested type (e.g. "Foo"). 811 * @return The types's descriptor, or {@code null} if not found. 812 */ findEnumTypeByName(final String name)813 public EnumDescriptor findEnumTypeByName(final String name) { 814 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 815 if (result != null && result instanceof EnumDescriptor) { 816 return (EnumDescriptor) result; 817 } else { 818 return null; 819 } 820 } 821 822 private final int index; 823 private DescriptorProto proto; 824 private final String fullName; 825 private final FileDescriptor file; 826 private final Descriptor containingType; 827 private final Descriptor[] nestedTypes; 828 private final EnumDescriptor[] enumTypes; 829 private final FieldDescriptor[] fields; 830 private final FieldDescriptor[] extensions; 831 private final OneofDescriptor[] oneofs; 832 private final int realOneofCount; 833 834 // Used to create a placeholder when the type cannot be found. Descriptor(final String fullname)835 Descriptor(final String fullname) throws DescriptorValidationException { 836 String name = fullname; 837 String packageName = ""; 838 int pos = fullname.lastIndexOf('.'); 839 if (pos != -1) { 840 name = fullname.substring(pos + 1); 841 packageName = fullname.substring(0, pos); 842 } 843 this.index = 0; 844 this.proto = 845 DescriptorProto.newBuilder() 846 .setName(name) 847 .addExtensionRange( 848 DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build()) 849 .build(); 850 this.fullName = fullname; 851 this.containingType = null; 852 853 this.nestedTypes = new Descriptor[0]; 854 this.enumTypes = new EnumDescriptor[0]; 855 this.fields = new FieldDescriptor[0]; 856 this.extensions = new FieldDescriptor[0]; 857 this.oneofs = new OneofDescriptor[0]; 858 this.realOneofCount = 0; 859 860 // Create a placeholder FileDescriptor to hold this message. 861 this.file = new FileDescriptor(packageName, this); 862 } 863 Descriptor( final DescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)864 private Descriptor( 865 final DescriptorProto proto, 866 final FileDescriptor file, 867 final Descriptor parent, 868 final int index) 869 throws DescriptorValidationException { 870 this.index = index; 871 this.proto = proto; 872 fullName = computeFullName(file, parent, proto.getName()); 873 this.file = file; 874 containingType = parent; 875 876 oneofs = new OneofDescriptor[proto.getOneofDeclCount()]; 877 for (int i = 0; i < proto.getOneofDeclCount(); i++) { 878 oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i); 879 } 880 881 nestedTypes = new Descriptor[proto.getNestedTypeCount()]; 882 for (int i = 0; i < proto.getNestedTypeCount(); i++) { 883 nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i); 884 } 885 886 enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; 887 for (int i = 0; i < proto.getEnumTypeCount(); i++) { 888 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i); 889 } 890 891 fields = new FieldDescriptor[proto.getFieldCount()]; 892 for (int i = 0; i < proto.getFieldCount(); i++) { 893 fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false); 894 } 895 896 extensions = new FieldDescriptor[proto.getExtensionCount()]; 897 for (int i = 0; i < proto.getExtensionCount(); i++) { 898 extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true); 899 } 900 901 for (int i = 0; i < proto.getOneofDeclCount(); i++) { 902 oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()]; 903 oneofs[i].fieldCount = 0; 904 } 905 for (int i = 0; i < proto.getFieldCount(); i++) { 906 OneofDescriptor oneofDescriptor = fields[i].getContainingOneof(); 907 if (oneofDescriptor != null) { 908 oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i]; 909 } 910 } 911 912 int syntheticOneofCount = 0; 913 for (OneofDescriptor oneof : this.oneofs) { 914 if (oneof.isSynthetic()) { 915 syntheticOneofCount++; 916 } else { 917 if (syntheticOneofCount > 0) { 918 throw new DescriptorValidationException(this, "Synthetic oneofs must come last."); 919 } 920 } 921 } 922 this.realOneofCount = this.oneofs.length - syntheticOneofCount; 923 924 file.pool.addSymbol(this); 925 } 926 927 /** Look up and cross-link all field types, etc. */ crossLink()928 private void crossLink() throws DescriptorValidationException { 929 for (final Descriptor nestedType : nestedTypes) { 930 nestedType.crossLink(); 931 } 932 933 for (final FieldDescriptor field : fields) { 934 field.crossLink(); 935 } 936 937 for (final FieldDescriptor extension : extensions) { 938 extension.crossLink(); 939 } 940 } 941 942 /** See {@link FileDescriptor#setProto}. */ setProto(final DescriptorProto proto)943 private void setProto(final DescriptorProto proto) { 944 this.proto = proto; 945 946 for (int i = 0; i < nestedTypes.length; i++) { 947 nestedTypes[i].setProto(proto.getNestedType(i)); 948 } 949 950 for (int i = 0; i < oneofs.length; i++) { 951 oneofs[i].setProto(proto.getOneofDecl(i)); 952 } 953 954 for (int i = 0; i < enumTypes.length; i++) { 955 enumTypes[i].setProto(proto.getEnumType(i)); 956 } 957 958 for (int i = 0; i < fields.length; i++) { 959 fields[i].setProto(proto.getField(i)); 960 } 961 962 for (int i = 0; i < extensions.length; i++) { 963 extensions[i].setProto(proto.getExtension(i)); 964 } 965 } 966 } 967 968 // ================================================================= 969 970 /** Describes a field of a message type. */ 971 public static final class FieldDescriptor extends GenericDescriptor 972 implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> { 973 /** 974 * Get the index of this descriptor within its parent. 975 * 976 * @see Descriptors.Descriptor#getIndex() 977 */ getIndex()978 public int getIndex() { 979 return index; 980 } 981 982 /** Convert the descriptor to its protocol message representation. */ 983 @Override toProto()984 public FieldDescriptorProto toProto() { 985 return proto; 986 } 987 988 /** Get the field's unqualified name. */ 989 @Override getName()990 public String getName() { 991 return proto.getName(); 992 } 993 994 /** Get the field's number. */ 995 @Override getNumber()996 public int getNumber() { 997 return proto.getNumber(); 998 } 999 1000 /** 1001 * Get the field's fully-qualified name. 1002 * 1003 * @see Descriptors.Descriptor#getFullName() 1004 */ 1005 @Override getFullName()1006 public String getFullName() { 1007 return fullName; 1008 } 1009 1010 /** Get the JSON name of this field. */ getJsonName()1011 public String getJsonName() { 1012 return jsonName; 1013 } 1014 1015 /** 1016 * Get the field's java type. This is just for convenience. Every {@code 1017 * FieldDescriptorProto.Type} maps to exactly one Java type. 1018 */ getJavaType()1019 public JavaType getJavaType() { 1020 return type.getJavaType(); 1021 } 1022 1023 /** For internal use only. */ 1024 @Override getLiteJavaType()1025 public WireFormat.JavaType getLiteJavaType() { 1026 return getLiteType().getJavaType(); 1027 } 1028 1029 /** Get the {@code FileDescriptor} containing this descriptor. */ 1030 @Override getFile()1031 public FileDescriptor getFile() { 1032 return file; 1033 } 1034 1035 /** Get the field's declared type. */ getType()1036 public Type getType() { 1037 return type; 1038 } 1039 1040 /** For internal use only. */ 1041 @Override getLiteType()1042 public WireFormat.FieldType getLiteType() { 1043 return table[type.ordinal()]; 1044 } 1045 1046 /** For internal use only. */ needsUtf8Check()1047 public boolean needsUtf8Check() { 1048 if (type != Type.STRING) { 1049 return false; 1050 } 1051 if (getContainingType().getOptions().getMapEntry()) { 1052 // Always enforce strict UTF-8 checking for map fields. 1053 return true; 1054 } 1055 if (getFile().getSyntax() == Syntax.PROTO3) { 1056 return true; 1057 } 1058 return getFile().getOptions().getJavaStringCheckUtf8(); 1059 } 1060 isMapField()1061 public boolean isMapField() { 1062 return getType() == Type.MESSAGE 1063 && isRepeated() 1064 && getMessageType().getOptions().getMapEntry(); 1065 } 1066 1067 // I'm pretty sure values() constructs a new array every time, since there 1068 // is nothing stopping the caller from mutating the array. Therefore we 1069 // make a static copy here. 1070 private static final WireFormat.FieldType[] table = WireFormat.FieldType.values(); 1071 1072 /** Is this field declared required? */ isRequired()1073 public boolean isRequired() { 1074 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; 1075 } 1076 1077 /** Is this field declared optional? */ isOptional()1078 public boolean isOptional() { 1079 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL; 1080 } 1081 1082 /** Is this field declared repeated? */ 1083 @Override isRepeated()1084 public boolean isRepeated() { 1085 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; 1086 } 1087 1088 /** 1089 * Does this field have the {@code [packed = true]} option or is this field packable in proto3 1090 * and not explicitly set to unpacked? 1091 */ 1092 @Override isPacked()1093 public boolean isPacked() { 1094 if (!isPackable()) { 1095 return false; 1096 } 1097 if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) { 1098 return getOptions().getPacked(); 1099 } else { 1100 return !getOptions().hasPacked() || getOptions().getPacked(); 1101 } 1102 } 1103 1104 /** Can this field be packed? i.e. is it a repeated primitive field? */ isPackable()1105 public boolean isPackable() { 1106 return isRepeated() && getLiteType().isPackable(); 1107 } 1108 1109 /** Returns true if the field had an explicitly-defined default value. */ hasDefaultValue()1110 public boolean hasDefaultValue() { 1111 return proto.hasDefaultValue(); 1112 } 1113 1114 /** 1115 * Returns the field's default value. Valid for all types except for messages and groups. For 1116 * all other types, the object returned is of the same class that would returned by 1117 * Message.getField(this). 1118 */ getDefaultValue()1119 public Object getDefaultValue() { 1120 if (getJavaType() == JavaType.MESSAGE) { 1121 throw new UnsupportedOperationException( 1122 "FieldDescriptor.getDefaultValue() called on an embedded message field."); 1123 } 1124 return defaultValue; 1125 } 1126 1127 /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ getOptions()1128 public FieldOptions getOptions() { 1129 return proto.getOptions(); 1130 } 1131 1132 /** Is this field an extension? */ isExtension()1133 public boolean isExtension() { 1134 return proto.hasExtendee(); 1135 } 1136 1137 /** 1138 * Get the field's containing type. For extensions, this is the type being extended, not the 1139 * location where the extension was defined. See {@link #getExtensionScope()}. 1140 */ getContainingType()1141 public Descriptor getContainingType() { 1142 return containingType; 1143 } 1144 1145 /** Get the field's containing oneof. */ getContainingOneof()1146 public OneofDescriptor getContainingOneof() { 1147 return containingOneof; 1148 } 1149 1150 /** Get the field's containing oneof, only if non-synthetic. */ getRealContainingOneof()1151 public OneofDescriptor getRealContainingOneof() { 1152 return containingOneof != null && !containingOneof.isSynthetic() ? containingOneof : null; 1153 } 1154 1155 /** 1156 * Returns true if this field was syntactically written with "optional" in the .proto file. 1157 * Excludes singular proto3 fields that do not have a label. 1158 */ hasOptionalKeyword()1159 public boolean hasOptionalKeyword() { 1160 return isProto3Optional 1161 || (file.getSyntax() == Syntax.PROTO2 && isOptional() && getContainingOneof() == null); 1162 } 1163 1164 /** 1165 * Returns true if this field tracks presence, ie. does the field distinguish between "unset" 1166 * and "present with default value." 1167 * 1168 * <p>This includes required, optional, and oneof fields. It excludes maps, repeated fields, and 1169 * singular proto3 fields without "optional". 1170 * 1171 * <p>For fields where hasPresence() == true, the return value of msg.hasField() is semantically 1172 * meaningful. 1173 */ hasPresence()1174 boolean hasPresence() { 1175 if (isRepeated()) { 1176 return false; 1177 } 1178 return getType() == Type.MESSAGE 1179 || getType() == Type.GROUP 1180 || getContainingOneof() != null 1181 || file.getSyntax() == Syntax.PROTO2; 1182 } 1183 1184 /** 1185 * For extensions defined nested within message types, gets the outer type. Not valid for 1186 * non-extension fields. For example, consider this {@code .proto} file: 1187 * 1188 * <pre> 1189 * message Foo { 1190 * extensions 1000 to max; 1191 * } 1192 * extend Foo { 1193 * optional int32 baz = 1234; 1194 * } 1195 * message Bar { 1196 * extend Foo { 1197 * optional int32 qux = 4321; 1198 * } 1199 * } 1200 * </pre> 1201 * 1202 * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. However, {@code baz}'s 1203 * extension scope is {@code null} while {@code qux}'s extension scope is {@code Bar}. 1204 */ getExtensionScope()1205 public Descriptor getExtensionScope() { 1206 if (!isExtension()) { 1207 throw new UnsupportedOperationException( 1208 String.format("This field is not an extension. (%s)", fullName)); 1209 } 1210 return extensionScope; 1211 } 1212 1213 /** For embedded message and group fields, gets the field's type. */ getMessageType()1214 public Descriptor getMessageType() { 1215 if (getJavaType() != JavaType.MESSAGE) { 1216 throw new UnsupportedOperationException( 1217 String.format("This field is not of message type. (%s)", fullName)); 1218 } 1219 return messageType; 1220 } 1221 1222 /** For enum fields, gets the field's type. */ 1223 @Override getEnumType()1224 public EnumDescriptor getEnumType() { 1225 if (getJavaType() != JavaType.ENUM) { 1226 throw new UnsupportedOperationException( 1227 String.format("This field is not of enum type. (%s)", fullName)); 1228 } 1229 return enumType; 1230 } 1231 1232 /** 1233 * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which 1234 * simply means ascending order by field number. {@code other} must be a field of the same type 1235 * -- i.e. {@code getContainingType()} must return the same {@code Descriptor} for both fields. 1236 * 1237 * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than 1238 * {@code other}, respectively. 1239 */ 1240 @Override compareTo(final FieldDescriptor other)1241 public int compareTo(final FieldDescriptor other) { 1242 if (other.containingType != containingType) { 1243 throw new IllegalArgumentException( 1244 "FieldDescriptors can only be compared to other FieldDescriptors " 1245 + "for fields of the same message type."); 1246 } 1247 return getNumber() - other.getNumber(); 1248 } 1249 1250 @Override toString()1251 public String toString() { 1252 return getFullName(); 1253 } 1254 1255 private final int index; 1256 1257 private FieldDescriptorProto proto; 1258 private final String fullName; 1259 private final String jsonName; 1260 private final FileDescriptor file; 1261 private final Descriptor extensionScope; 1262 private final boolean isProto3Optional; 1263 1264 // Possibly initialized during cross-linking. 1265 private Type type; 1266 private Descriptor containingType; 1267 private Descriptor messageType; 1268 private OneofDescriptor containingOneof; 1269 private EnumDescriptor enumType; 1270 private Object defaultValue; 1271 1272 public enum Type { 1273 DOUBLE(JavaType.DOUBLE), 1274 FLOAT(JavaType.FLOAT), 1275 INT64(JavaType.LONG), 1276 UINT64(JavaType.LONG), 1277 INT32(JavaType.INT), 1278 FIXED64(JavaType.LONG), 1279 FIXED32(JavaType.INT), 1280 BOOL(JavaType.BOOLEAN), 1281 STRING(JavaType.STRING), 1282 GROUP(JavaType.MESSAGE), 1283 MESSAGE(JavaType.MESSAGE), 1284 BYTES(JavaType.BYTE_STRING), 1285 UINT32(JavaType.INT), 1286 ENUM(JavaType.ENUM), 1287 SFIXED32(JavaType.INT), 1288 SFIXED64(JavaType.LONG), 1289 SINT32(JavaType.INT), 1290 SINT64(JavaType.LONG); 1291 Type(final JavaType javaType)1292 Type(final JavaType javaType) { 1293 this.javaType = javaType; 1294 } 1295 1296 private JavaType javaType; 1297 toProto()1298 public FieldDescriptorProto.Type toProto() { 1299 return FieldDescriptorProto.Type.forNumber(ordinal() + 1); 1300 } 1301 getJavaType()1302 public JavaType getJavaType() { 1303 return javaType; 1304 } 1305 valueOf(final FieldDescriptorProto.Type type)1306 public static Type valueOf(final FieldDescriptorProto.Type type) { 1307 return values()[type.getNumber() - 1]; 1308 } 1309 } 1310 1311 static { 1312 // Refuse to init if someone added a new declared type. 1313 if (Type.values().length != FieldDescriptorProto.Type.values().length) { 1314 throw new RuntimeException( 1315 "descriptor.proto has a new declared type but Descriptors.java wasn't updated."); 1316 } 1317 } 1318 1319 public enum JavaType { 1320 INT(0), 1321 LONG(0L), 1322 FLOAT(0F), 1323 DOUBLE(0D), 1324 BOOLEAN(false), 1325 STRING(""), 1326 BYTE_STRING(ByteString.EMPTY), 1327 ENUM(null), 1328 MESSAGE(null); 1329 JavaType(final Object defaultDefault)1330 JavaType(final Object defaultDefault) { 1331 this.defaultDefault = defaultDefault; 1332 } 1333 1334 /** 1335 * The default default value for fields of this type, if it's a primitive type. This is meant 1336 * for use inside this file only, hence is private. 1337 */ 1338 private final Object defaultDefault; 1339 } 1340 1341 // This method should match exactly with the ToJsonName() function in C++ 1342 // descriptor.cc. fieldNameToJsonName(String name)1343 private static String fieldNameToJsonName(String name) { 1344 final int length = name.length(); 1345 StringBuilder result = new StringBuilder(length); 1346 boolean isNextUpperCase = false; 1347 for (int i = 0; i < length; i++) { 1348 char ch = name.charAt(i); 1349 if (ch == '_') { 1350 isNextUpperCase = true; 1351 } else if (isNextUpperCase) { 1352 // This closely matches the logic for ASCII characters in: 1353 // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689 1354 if ('a' <= ch && ch <= 'z') { 1355 ch = (char) (ch - 'a' + 'A'); 1356 } 1357 result.append(ch); 1358 isNextUpperCase = false; 1359 } else { 1360 result.append(ch); 1361 } 1362 } 1363 return result.toString(); 1364 } 1365 FieldDescriptor( final FieldDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index, final boolean isExtension)1366 private FieldDescriptor( 1367 final FieldDescriptorProto proto, 1368 final FileDescriptor file, 1369 final Descriptor parent, 1370 final int index, 1371 final boolean isExtension) 1372 throws DescriptorValidationException { 1373 this.index = index; 1374 this.proto = proto; 1375 fullName = computeFullName(file, parent, proto.getName()); 1376 this.file = file; 1377 if (proto.hasJsonName()) { 1378 jsonName = proto.getJsonName(); 1379 } else { 1380 jsonName = fieldNameToJsonName(proto.getName()); 1381 } 1382 1383 if (proto.hasType()) { 1384 type = Type.valueOf(proto.getType()); 1385 } 1386 1387 isProto3Optional = proto.getProto3Optional(); 1388 1389 if (getNumber() <= 0) { 1390 throw new DescriptorValidationException(this, "Field numbers must be positive integers."); 1391 } 1392 1393 if (isExtension) { 1394 if (!proto.hasExtendee()) { 1395 throw new DescriptorValidationException( 1396 this, "FieldDescriptorProto.extendee not set for extension field."); 1397 } 1398 containingType = null; // Will be filled in when cross-linking 1399 if (parent != null) { 1400 extensionScope = parent; 1401 } else { 1402 extensionScope = null; 1403 } 1404 1405 if (proto.hasOneofIndex()) { 1406 throw new DescriptorValidationException( 1407 this, "FieldDescriptorProto.oneof_index set for extension field."); 1408 } 1409 containingOneof = null; 1410 } else { 1411 if (proto.hasExtendee()) { 1412 throw new DescriptorValidationException( 1413 this, "FieldDescriptorProto.extendee set for non-extension field."); 1414 } 1415 containingType = parent; 1416 1417 if (proto.hasOneofIndex()) { 1418 if (proto.getOneofIndex() < 0 1419 || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) { 1420 throw new DescriptorValidationException( 1421 this, 1422 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName()); 1423 } 1424 containingOneof = parent.getOneofs().get(proto.getOneofIndex()); 1425 containingOneof.fieldCount++; 1426 } else { 1427 containingOneof = null; 1428 } 1429 extensionScope = null; 1430 } 1431 1432 file.pool.addSymbol(this); 1433 } 1434 1435 /** Look up and cross-link all field types, etc. */ crossLink()1436 private void crossLink() throws DescriptorValidationException { 1437 if (proto.hasExtendee()) { 1438 final GenericDescriptor extendee = 1439 file.pool.lookupSymbol( 1440 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 1441 if (!(extendee instanceof Descriptor)) { 1442 throw new DescriptorValidationException( 1443 this, '\"' + proto.getExtendee() + "\" is not a message type."); 1444 } 1445 containingType = (Descriptor) extendee; 1446 1447 if (!getContainingType().isExtensionNumber(getNumber())) { 1448 throw new DescriptorValidationException( 1449 this, 1450 '\"' 1451 + getContainingType().getFullName() 1452 + "\" does not declare " 1453 + getNumber() 1454 + " as an extension number."); 1455 } 1456 } 1457 1458 if (proto.hasTypeName()) { 1459 final GenericDescriptor typeDescriptor = 1460 file.pool.lookupSymbol( 1461 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 1462 1463 if (!proto.hasType()) { 1464 // Choose field type based on symbol. 1465 if (typeDescriptor instanceof Descriptor) { 1466 type = Type.MESSAGE; 1467 } else if (typeDescriptor instanceof EnumDescriptor) { 1468 type = Type.ENUM; 1469 } else { 1470 throw new DescriptorValidationException( 1471 this, '\"' + proto.getTypeName() + "\" is not a type."); 1472 } 1473 } 1474 1475 if (getJavaType() == JavaType.MESSAGE) { 1476 if (!(typeDescriptor instanceof Descriptor)) { 1477 throw new DescriptorValidationException( 1478 this, '\"' + proto.getTypeName() + "\" is not a message type."); 1479 } 1480 messageType = (Descriptor) typeDescriptor; 1481 1482 if (proto.hasDefaultValue()) { 1483 throw new DescriptorValidationException(this, "Messages can't have default values."); 1484 } 1485 } else if (getJavaType() == JavaType.ENUM) { 1486 if (!(typeDescriptor instanceof EnumDescriptor)) { 1487 throw new DescriptorValidationException( 1488 this, '\"' + proto.getTypeName() + "\" is not an enum type."); 1489 } 1490 enumType = (EnumDescriptor) typeDescriptor; 1491 } else { 1492 throw new DescriptorValidationException(this, "Field with primitive type has type_name."); 1493 } 1494 } else { 1495 if (getJavaType() == JavaType.MESSAGE || getJavaType() == JavaType.ENUM) { 1496 throw new DescriptorValidationException( 1497 this, "Field with message or enum type missing type_name."); 1498 } 1499 } 1500 1501 // Only repeated primitive fields may be packed. 1502 if (proto.getOptions().getPacked() && !isPackable()) { 1503 throw new DescriptorValidationException( 1504 this, "[packed = true] can only be specified for repeated primitive fields."); 1505 } 1506 1507 // We don't attempt to parse the default value until here because for 1508 // enums we need the enum type's descriptor. 1509 if (proto.hasDefaultValue()) { 1510 if (isRepeated()) { 1511 throw new DescriptorValidationException( 1512 this, "Repeated fields cannot have default values."); 1513 } 1514 1515 try { 1516 switch (getType()) { 1517 case INT32: 1518 case SINT32: 1519 case SFIXED32: 1520 defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); 1521 break; 1522 case UINT32: 1523 case FIXED32: 1524 defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); 1525 break; 1526 case INT64: 1527 case SINT64: 1528 case SFIXED64: 1529 defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); 1530 break; 1531 case UINT64: 1532 case FIXED64: 1533 defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); 1534 break; 1535 case FLOAT: 1536 if (proto.getDefaultValue().equals("inf")) { 1537 defaultValue = Float.POSITIVE_INFINITY; 1538 } else if (proto.getDefaultValue().equals("-inf")) { 1539 defaultValue = Float.NEGATIVE_INFINITY; 1540 } else if (proto.getDefaultValue().equals("nan")) { 1541 defaultValue = Float.NaN; 1542 } else { 1543 defaultValue = Float.valueOf(proto.getDefaultValue()); 1544 } 1545 break; 1546 case DOUBLE: 1547 if (proto.getDefaultValue().equals("inf")) { 1548 defaultValue = Double.POSITIVE_INFINITY; 1549 } else if (proto.getDefaultValue().equals("-inf")) { 1550 defaultValue = Double.NEGATIVE_INFINITY; 1551 } else if (proto.getDefaultValue().equals("nan")) { 1552 defaultValue = Double.NaN; 1553 } else { 1554 defaultValue = Double.valueOf(proto.getDefaultValue()); 1555 } 1556 break; 1557 case BOOL: 1558 defaultValue = Boolean.valueOf(proto.getDefaultValue()); 1559 break; 1560 case STRING: 1561 defaultValue = proto.getDefaultValue(); 1562 break; 1563 case BYTES: 1564 try { 1565 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue()); 1566 } catch (TextFormat.InvalidEscapeSequenceException e) { 1567 throw new DescriptorValidationException( 1568 this, "Couldn't parse default value: " + e.getMessage(), e); 1569 } 1570 break; 1571 case ENUM: 1572 defaultValue = enumType.findValueByName(proto.getDefaultValue()); 1573 if (defaultValue == null) { 1574 throw new DescriptorValidationException( 1575 this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"'); 1576 } 1577 break; 1578 case MESSAGE: 1579 case GROUP: 1580 throw new DescriptorValidationException(this, "Message type had default value."); 1581 } 1582 } catch (NumberFormatException e) { 1583 throw new DescriptorValidationException( 1584 this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e); 1585 } 1586 } else { 1587 // Determine the default default for this field. 1588 if (isRepeated()) { 1589 defaultValue = Collections.emptyList(); 1590 } else { 1591 switch (getJavaType()) { 1592 case ENUM: 1593 // We guarantee elsewhere that an enum type always has at least 1594 // one possible value. 1595 defaultValue = enumType.getValues().get(0); 1596 break; 1597 case MESSAGE: 1598 defaultValue = null; 1599 break; 1600 default: 1601 defaultValue = getJavaType().defaultDefault; 1602 break; 1603 } 1604 } 1605 } 1606 1607 if (!isExtension()) { 1608 file.pool.addFieldByNumber(this); 1609 } 1610 1611 if (containingType != null && containingType.getOptions().getMessageSetWireFormat()) { 1612 if (isExtension()) { 1613 if (!isOptional() || getType() != Type.MESSAGE) { 1614 throw new DescriptorValidationException( 1615 this, "Extensions of MessageSets must be optional messages."); 1616 } 1617 } else { 1618 throw new DescriptorValidationException( 1619 this, "MessageSets cannot have fields, only extensions."); 1620 } 1621 } 1622 } 1623 1624 /** See {@link FileDescriptor#setProto}. */ setProto(final FieldDescriptorProto proto)1625 private void setProto(final FieldDescriptorProto proto) { 1626 this.proto = proto; 1627 } 1628 1629 /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */ 1630 @Override internalMergeFrom(MessageLite.Builder to, MessageLite from)1631 public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) { 1632 // FieldDescriptors are only used with non-lite messages so we can just 1633 // down-cast and call mergeFrom directly. 1634 return ((Message.Builder) to).mergeFrom((Message) from); 1635 } 1636 1637 } 1638 1639 // ================================================================= 1640 1641 /** Describes an enum type. */ 1642 public static final class EnumDescriptor extends GenericDescriptor 1643 implements Internal.EnumLiteMap<EnumValueDescriptor> { 1644 /** 1645 * Get the index of this descriptor within its parent. 1646 * 1647 * @see Descriptors.Descriptor#getIndex() 1648 */ getIndex()1649 public int getIndex() { 1650 return index; 1651 } 1652 1653 /** Convert the descriptor to its protocol message representation. */ 1654 @Override toProto()1655 public EnumDescriptorProto toProto() { 1656 return proto; 1657 } 1658 1659 /** Get the type's unqualified name. */ 1660 @Override getName()1661 public String getName() { 1662 return proto.getName(); 1663 } 1664 1665 /** 1666 * Get the type's fully-qualified name. 1667 * 1668 * @see Descriptors.Descriptor#getFullName() 1669 */ 1670 @Override getFullName()1671 public String getFullName() { 1672 return fullName; 1673 } 1674 1675 /** Get the {@link FileDescriptor} containing this descriptor. */ 1676 @Override getFile()1677 public FileDescriptor getFile() { 1678 return file; 1679 } 1680 1681 /** If this is a nested type, get the outer descriptor, otherwise null. */ getContainingType()1682 public Descriptor getContainingType() { 1683 return containingType; 1684 } 1685 1686 /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ getOptions()1687 public EnumOptions getOptions() { 1688 return proto.getOptions(); 1689 } 1690 1691 /** Get a list of defined values for this enum. */ getValues()1692 public List<EnumValueDescriptor> getValues() { 1693 return Collections.unmodifiableList(Arrays.asList(values)); 1694 } 1695 1696 /** 1697 * Find an enum value by name. 1698 * 1699 * @param name The unqualified name of the value (e.g. "FOO"). 1700 * @return the value's descriptor, or {@code null} if not found. 1701 */ findValueByName(final String name)1702 public EnumValueDescriptor findValueByName(final String name) { 1703 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 1704 if (result != null && result instanceof EnumValueDescriptor) { 1705 return (EnumValueDescriptor) result; 1706 } else { 1707 return null; 1708 } 1709 } 1710 1711 /** 1712 * Find an enum value by number. If multiple enum values have the same number, this returns the 1713 * first defined value with that number. 1714 * 1715 * @param number The value's number. 1716 * @return the value's descriptor, or {@code null} if not found. 1717 */ 1718 @Override findValueByNumber(final int number)1719 public EnumValueDescriptor findValueByNumber(final int number) { 1720 return file.pool.enumValuesByNumber.get(new DescriptorPool.DescriptorIntPair(this, number)); 1721 } 1722 1723 /** 1724 * Get the enum value for a number. If no enum value has this number, construct an 1725 * EnumValueDescriptor for it. 1726 */ findValueByNumberCreatingIfUnknown(final int number)1727 public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) { 1728 EnumValueDescriptor result = findValueByNumber(number); 1729 if (result != null) { 1730 return result; 1731 } 1732 // The number represents an unknown enum value. 1733 synchronized (this) { 1734 // Descriptors are compared by object identity so for the same number 1735 // we need to return the same EnumValueDescriptor object. This means 1736 // we have to store created EnumValueDescriptors. However, as there 1737 // are potentially 2G unknown enum values, storing all of these 1738 // objects persistently will consume lots of memory for long-running 1739 // services and it's also unnecessary as not many EnumValueDescriptors 1740 // will be used at the same time. 1741 // 1742 // To solve the problem we take advantage of Java's weak references and 1743 // rely on gc to release unused descriptors. 1744 // 1745 // Here is how it works: 1746 // * We store unknown EnumValueDescriptors in a WeakHashMap with the 1747 // value being a weak reference to the descriptor. 1748 // * The descriptor holds a strong reference to the key so as long 1749 // as the EnumValueDescriptor is in use, the key will be there 1750 // and the corresponding map entry will be there. Following-up 1751 // queries with the same number will return the same descriptor. 1752 // * If the user no longer uses an unknown EnumValueDescriptor, 1753 // it will be gc-ed since we only hold a weak reference to it in 1754 // the map. The key in the corresponding map entry will also be 1755 // gc-ed as the only strong reference to it is in the descriptor 1756 // which is just gc-ed. With the key being gone WeakHashMap will 1757 // then remove the whole entry. This way unknown descriptors will 1758 // be freed automatically and we don't need to do anything to 1759 // clean-up unused map entries. 1760 1761 // Note: We must use "new Integer(number)" here because we don't want 1762 // these Integer objects to be cached. 1763 Integer key = new Integer(number); 1764 WeakReference<EnumValueDescriptor> reference = unknownValues.get(key); 1765 if (reference != null) { 1766 result = reference.get(); 1767 } 1768 if (result == null) { 1769 result = new EnumValueDescriptor(file, this, key); 1770 unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result)); 1771 } 1772 } 1773 return result; 1774 } 1775 1776 // Used in tests only. getUnknownEnumValueDescriptorCount()1777 int getUnknownEnumValueDescriptorCount() { 1778 return unknownValues.size(); 1779 } 1780 1781 private final int index; 1782 private EnumDescriptorProto proto; 1783 private final String fullName; 1784 private final FileDescriptor file; 1785 private final Descriptor containingType; 1786 private EnumValueDescriptor[] values; 1787 private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues = 1788 new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>(); 1789 EnumDescriptor( final EnumDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)1790 private EnumDescriptor( 1791 final EnumDescriptorProto proto, 1792 final FileDescriptor file, 1793 final Descriptor parent, 1794 final int index) 1795 throws DescriptorValidationException { 1796 this.index = index; 1797 this.proto = proto; 1798 fullName = computeFullName(file, parent, proto.getName()); 1799 this.file = file; 1800 containingType = parent; 1801 1802 if (proto.getValueCount() == 0) { 1803 // We cannot allow enums with no values because this would mean there 1804 // would be no valid default value for fields of this type. 1805 throw new DescriptorValidationException(this, "Enums must contain at least one value."); 1806 } 1807 1808 values = new EnumValueDescriptor[proto.getValueCount()]; 1809 for (int i = 0; i < proto.getValueCount(); i++) { 1810 values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i); 1811 } 1812 1813 file.pool.addSymbol(this); 1814 } 1815 1816 /** See {@link FileDescriptor#setProto}. */ setProto(final EnumDescriptorProto proto)1817 private void setProto(final EnumDescriptorProto proto) { 1818 this.proto = proto; 1819 1820 for (int i = 0; i < values.length; i++) { 1821 values[i].setProto(proto.getValue(i)); 1822 } 1823 } 1824 } 1825 1826 // ================================================================= 1827 1828 /** 1829 * Describes one value within an enum type. Note that multiple defined values may have the same 1830 * number. In generated Java code, all values with the same number after the first become aliases 1831 * of the first. However, they still have independent EnumValueDescriptors. 1832 */ 1833 public static final class EnumValueDescriptor extends GenericDescriptor 1834 implements Internal.EnumLite { 1835 /** 1836 * Get the index of this descriptor within its parent. 1837 * 1838 * @see Descriptors.Descriptor#getIndex() 1839 */ getIndex()1840 public int getIndex() { 1841 return index; 1842 } 1843 1844 /** Convert the descriptor to its protocol message representation. */ 1845 @Override toProto()1846 public EnumValueDescriptorProto toProto() { 1847 return proto; 1848 } 1849 1850 /** Get the value's unqualified name. */ 1851 @Override getName()1852 public String getName() { 1853 return proto.getName(); 1854 } 1855 1856 /** Get the value's number. */ 1857 @Override getNumber()1858 public int getNumber() { 1859 return proto.getNumber(); 1860 } 1861 1862 @Override toString()1863 public String toString() { 1864 return proto.getName(); 1865 } 1866 1867 /** 1868 * Get the value's fully-qualified name. 1869 * 1870 * @see Descriptors.Descriptor#getFullName() 1871 */ 1872 @Override getFullName()1873 public String getFullName() { 1874 return fullName; 1875 } 1876 1877 /** Get the {@link FileDescriptor} containing this descriptor. */ 1878 @Override getFile()1879 public FileDescriptor getFile() { 1880 return file; 1881 } 1882 1883 /** Get the value's enum type. */ getType()1884 public EnumDescriptor getType() { 1885 return type; 1886 } 1887 1888 /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */ getOptions()1889 public EnumValueOptions getOptions() { 1890 return proto.getOptions(); 1891 } 1892 1893 private final int index; 1894 private EnumValueDescriptorProto proto; 1895 private final String fullName; 1896 private final FileDescriptor file; 1897 private final EnumDescriptor type; 1898 EnumValueDescriptor( final EnumValueDescriptorProto proto, final FileDescriptor file, final EnumDescriptor parent, final int index)1899 private EnumValueDescriptor( 1900 final EnumValueDescriptorProto proto, 1901 final FileDescriptor file, 1902 final EnumDescriptor parent, 1903 final int index) 1904 throws DescriptorValidationException { 1905 this.index = index; 1906 this.proto = proto; 1907 this.file = file; 1908 type = parent; 1909 1910 fullName = parent.getFullName() + '.' + proto.getName(); 1911 1912 file.pool.addSymbol(this); 1913 file.pool.addEnumValueByNumber(this); 1914 } 1915 1916 // Create an unknown enum value. EnumValueDescriptor( final FileDescriptor file, final EnumDescriptor parent, final Integer number)1917 private EnumValueDescriptor( 1918 final FileDescriptor file, final EnumDescriptor parent, final Integer number) { 1919 String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number; 1920 EnumValueDescriptorProto proto = 1921 EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build(); 1922 this.index = -1; 1923 this.proto = proto; 1924 this.file = file; 1925 this.type = parent; 1926 this.fullName = parent.getFullName() + '.' + proto.getName(); 1927 1928 // Don't add this descriptor into pool. 1929 } 1930 1931 /** See {@link FileDescriptor#setProto}. */ setProto(final EnumValueDescriptorProto proto)1932 private void setProto(final EnumValueDescriptorProto proto) { 1933 this.proto = proto; 1934 } 1935 } 1936 1937 // ================================================================= 1938 1939 /** Describes a service type. */ 1940 public static final class ServiceDescriptor extends GenericDescriptor { 1941 /** 1942 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex() 1943 */ getIndex()1944 public int getIndex() { 1945 return index; 1946 } 1947 1948 /** Convert the descriptor to its protocol message representation. */ 1949 @Override toProto()1950 public ServiceDescriptorProto toProto() { 1951 return proto; 1952 } 1953 1954 /** Get the type's unqualified name. */ 1955 @Override getName()1956 public String getName() { 1957 return proto.getName(); 1958 } 1959 1960 /** 1961 * Get the type's fully-qualified name. 1962 * 1963 * @see Descriptors.Descriptor#getFullName() 1964 */ 1965 @Override getFullName()1966 public String getFullName() { 1967 return fullName; 1968 } 1969 1970 /** Get the {@link FileDescriptor} containing this descriptor. */ 1971 @Override getFile()1972 public FileDescriptor getFile() { 1973 return file; 1974 } 1975 1976 /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ getOptions()1977 public ServiceOptions getOptions() { 1978 return proto.getOptions(); 1979 } 1980 1981 /** Get a list of methods for this service. */ getMethods()1982 public List<MethodDescriptor> getMethods() { 1983 return Collections.unmodifiableList(Arrays.asList(methods)); 1984 } 1985 1986 /** 1987 * Find a method by name. 1988 * 1989 * @param name The unqualified name of the method (e.g. "Foo"). 1990 * @return the method's descriptor, or {@code null} if not found. 1991 */ findMethodByName(final String name)1992 public MethodDescriptor findMethodByName(final String name) { 1993 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 1994 if (result != null && result instanceof MethodDescriptor) { 1995 return (MethodDescriptor) result; 1996 } else { 1997 return null; 1998 } 1999 } 2000 2001 private final int index; 2002 private ServiceDescriptorProto proto; 2003 private final String fullName; 2004 private final FileDescriptor file; 2005 private MethodDescriptor[] methods; 2006 ServiceDescriptor( final ServiceDescriptorProto proto, final FileDescriptor file, final int index)2007 private ServiceDescriptor( 2008 final ServiceDescriptorProto proto, final FileDescriptor file, final int index) 2009 throws DescriptorValidationException { 2010 this.index = index; 2011 this.proto = proto; 2012 fullName = computeFullName(file, null, proto.getName()); 2013 this.file = file; 2014 2015 methods = new MethodDescriptor[proto.getMethodCount()]; 2016 for (int i = 0; i < proto.getMethodCount(); i++) { 2017 methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i); 2018 } 2019 2020 file.pool.addSymbol(this); 2021 } 2022 crossLink()2023 private void crossLink() throws DescriptorValidationException { 2024 for (final MethodDescriptor method : methods) { 2025 method.crossLink(); 2026 } 2027 } 2028 2029 /** See {@link FileDescriptor#setProto}. */ setProto(final ServiceDescriptorProto proto)2030 private void setProto(final ServiceDescriptorProto proto) { 2031 this.proto = proto; 2032 2033 for (int i = 0; i < methods.length; i++) { 2034 methods[i].setProto(proto.getMethod(i)); 2035 } 2036 } 2037 } 2038 2039 // ================================================================= 2040 2041 /** Describes one method within a service type. */ 2042 public static final class MethodDescriptor extends GenericDescriptor { 2043 /** 2044 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex() 2045 */ getIndex()2046 public int getIndex() { 2047 return index; 2048 } 2049 2050 /** Convert the descriptor to its protocol message representation. */ 2051 @Override toProto()2052 public MethodDescriptorProto toProto() { 2053 return proto; 2054 } 2055 2056 /** Get the method's unqualified name. */ 2057 @Override getName()2058 public String getName() { 2059 return proto.getName(); 2060 } 2061 2062 /** 2063 * Get the method's fully-qualified name. 2064 * 2065 * @see Descriptors.Descriptor#getFullName() 2066 */ 2067 @Override getFullName()2068 public String getFullName() { 2069 return fullName; 2070 } 2071 2072 /** Get the {@link FileDescriptor} containing this descriptor. */ 2073 @Override getFile()2074 public FileDescriptor getFile() { 2075 return file; 2076 } 2077 2078 /** Get the method's service type. */ getService()2079 public ServiceDescriptor getService() { 2080 return service; 2081 } 2082 2083 /** Get the method's input type. */ getInputType()2084 public Descriptor getInputType() { 2085 return inputType; 2086 } 2087 2088 /** Get the method's output type. */ getOutputType()2089 public Descriptor getOutputType() { 2090 return outputType; 2091 } 2092 2093 /** Get whether or not the inputs are streaming. */ isClientStreaming()2094 public boolean isClientStreaming() { 2095 return proto.getClientStreaming(); 2096 } 2097 2098 /** Get whether or not the outputs are streaming. */ isServerStreaming()2099 public boolean isServerStreaming() { 2100 return proto.getServerStreaming(); 2101 } 2102 2103 /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */ getOptions()2104 public MethodOptions getOptions() { 2105 return proto.getOptions(); 2106 } 2107 2108 private final int index; 2109 private MethodDescriptorProto proto; 2110 private final String fullName; 2111 private final FileDescriptor file; 2112 private final ServiceDescriptor service; 2113 2114 // Initialized during cross-linking. 2115 private Descriptor inputType; 2116 private Descriptor outputType; 2117 MethodDescriptor( final MethodDescriptorProto proto, final FileDescriptor file, final ServiceDescriptor parent, final int index)2118 private MethodDescriptor( 2119 final MethodDescriptorProto proto, 2120 final FileDescriptor file, 2121 final ServiceDescriptor parent, 2122 final int index) 2123 throws DescriptorValidationException { 2124 this.index = index; 2125 this.proto = proto; 2126 this.file = file; 2127 service = parent; 2128 2129 fullName = parent.getFullName() + '.' + proto.getName(); 2130 2131 file.pool.addSymbol(this); 2132 } 2133 crossLink()2134 private void crossLink() throws DescriptorValidationException { 2135 final GenericDescriptor input = 2136 file.pool.lookupSymbol( 2137 proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 2138 if (!(input instanceof Descriptor)) { 2139 throw new DescriptorValidationException( 2140 this, '\"' + proto.getInputType() + "\" is not a message type."); 2141 } 2142 inputType = (Descriptor) input; 2143 2144 final GenericDescriptor output = 2145 file.pool.lookupSymbol( 2146 proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 2147 if (!(output instanceof Descriptor)) { 2148 throw new DescriptorValidationException( 2149 this, '\"' + proto.getOutputType() + "\" is not a message type."); 2150 } 2151 outputType = (Descriptor) output; 2152 } 2153 2154 /** See {@link FileDescriptor#setProto}. */ setProto(final MethodDescriptorProto proto)2155 private void setProto(final MethodDescriptorProto proto) { 2156 this.proto = proto; 2157 } 2158 } 2159 2160 // ================================================================= 2161 computeFullName( final FileDescriptor file, final Descriptor parent, final String name)2162 private static String computeFullName( 2163 final FileDescriptor file, final Descriptor parent, final String name) { 2164 if (parent != null) { 2165 return parent.getFullName() + '.' + name; 2166 } 2167 2168 final String packageName = file.getPackage(); 2169 if (!packageName.isEmpty()) { 2170 return packageName + '.' + name; 2171 } 2172 2173 return name; 2174 } 2175 2176 // ================================================================= 2177 2178 /** 2179 * All descriptors implement this to make it easier to implement tools like {@code 2180 * DescriptorPool}. 2181 */ 2182 public abstract static class GenericDescriptor { 2183 2184 // Private constructor to prevent subclasses outside of com.google.protobuf.Descriptors GenericDescriptor()2185 private GenericDescriptor() {} 2186 toProto()2187 public abstract Message toProto(); 2188 getName()2189 public abstract String getName(); 2190 getFullName()2191 public abstract String getFullName(); 2192 getFile()2193 public abstract FileDescriptor getFile(); 2194 } 2195 2196 /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */ 2197 public static class DescriptorValidationException extends Exception { 2198 private static final long serialVersionUID = 5750205775490483148L; 2199 2200 /** Gets the full name of the descriptor where the error occurred. */ getProblemSymbolName()2201 public String getProblemSymbolName() { 2202 return name; 2203 } 2204 2205 /** Gets the protocol message representation of the invalid descriptor. */ getProblemProto()2206 public Message getProblemProto() { 2207 return proto; 2208 } 2209 2210 /** Gets a human-readable description of the error. */ getDescription()2211 public String getDescription() { 2212 return description; 2213 } 2214 2215 private final String name; 2216 private final Message proto; 2217 private final String description; 2218 DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description)2219 private DescriptorValidationException( 2220 final GenericDescriptor problemDescriptor, final String description) { 2221 super(problemDescriptor.getFullName() + ": " + description); 2222 2223 // Note that problemDescriptor may be partially uninitialized, so we 2224 // don't want to expose it directly to the user. So, we only provide 2225 // the name and the original proto. 2226 name = problemDescriptor.getFullName(); 2227 proto = problemDescriptor.toProto(); 2228 this.description = description; 2229 } 2230 DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description, final Throwable cause)2231 private DescriptorValidationException( 2232 final GenericDescriptor problemDescriptor, 2233 final String description, 2234 final Throwable cause) { 2235 this(problemDescriptor, description); 2236 initCause(cause); 2237 } 2238 DescriptorValidationException( final FileDescriptor problemDescriptor, final String description)2239 private DescriptorValidationException( 2240 final FileDescriptor problemDescriptor, final String description) { 2241 super(problemDescriptor.getName() + ": " + description); 2242 2243 // Note that problemDescriptor may be partially uninitialized, so we 2244 // don't want to expose it directly to the user. So, we only provide 2245 // the name and the original proto. 2246 name = problemDescriptor.getName(); 2247 proto = problemDescriptor.toProto(); 2248 this.description = description; 2249 } 2250 } 2251 2252 // ================================================================= 2253 2254 /** 2255 * A private helper class which contains lookup tables containing all the descriptors defined in a 2256 * particular file. 2257 */ 2258 private static final class DescriptorPool { 2259 2260 /** Defines what subclass of descriptors to search in the descriptor pool. */ 2261 enum SearchFilter { 2262 TYPES_ONLY, 2263 AGGREGATES_ONLY, 2264 ALL_SYMBOLS 2265 } 2266 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies)2267 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) { 2268 this.dependencies = new HashSet<FileDescriptor>(); 2269 this.allowUnknownDependencies = allowUnknownDependencies; 2270 2271 for (int i = 0; i < dependencies.length; i++) { 2272 this.dependencies.add(dependencies[i]); 2273 importPublicDependencies(dependencies[i]); 2274 } 2275 2276 for (final FileDescriptor dependency : this.dependencies) { 2277 try { 2278 addPackage(dependency.getPackage(), dependency); 2279 } catch (DescriptorValidationException e) { 2280 // Can't happen, because addPackage() only fails when the name 2281 // conflicts with a non-package, but we have not yet added any 2282 // non-packages at this point. 2283 throw new AssertionError(e); 2284 } 2285 } 2286 } 2287 2288 /** Find and put public dependencies of the file into dependencies set. */ importPublicDependencies(final FileDescriptor file)2289 private void importPublicDependencies(final FileDescriptor file) { 2290 for (FileDescriptor dependency : file.getPublicDependencies()) { 2291 if (dependencies.add(dependency)) { 2292 importPublicDependencies(dependency); 2293 } 2294 } 2295 } 2296 2297 private final Set<FileDescriptor> dependencies; 2298 private boolean allowUnknownDependencies; 2299 2300 private final Map<String, GenericDescriptor> descriptorsByName = 2301 new HashMap<String, GenericDescriptor>(); 2302 private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber = 2303 new HashMap<DescriptorIntPair, FieldDescriptor>(); 2304 private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber = 2305 new HashMap<DescriptorIntPair, EnumValueDescriptor>(); 2306 2307 /** Find a generic descriptor by fully-qualified name. */ findSymbol(final String fullName)2308 GenericDescriptor findSymbol(final String fullName) { 2309 return findSymbol(fullName, SearchFilter.ALL_SYMBOLS); 2310 } 2311 2312 /** 2313 * Find a descriptor by fully-qualified name and given option to only search valid field type 2314 * descriptors. 2315 */ findSymbol(final String fullName, final SearchFilter filter)2316 GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) { 2317 GenericDescriptor result = descriptorsByName.get(fullName); 2318 if (result != null) { 2319 if ((filter == SearchFilter.ALL_SYMBOLS) 2320 || ((filter == SearchFilter.TYPES_ONLY) && isType(result)) 2321 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { 2322 return result; 2323 } 2324 } 2325 2326 for (final FileDescriptor dependency : dependencies) { 2327 result = dependency.pool.descriptorsByName.get(fullName); 2328 if (result != null) { 2329 if ((filter == SearchFilter.ALL_SYMBOLS) 2330 || ((filter == SearchFilter.TYPES_ONLY) && isType(result)) 2331 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { 2332 return result; 2333 } 2334 } 2335 } 2336 2337 return null; 2338 } 2339 2340 /** Checks if the descriptor is a valid type for a message field. */ isType(GenericDescriptor descriptor)2341 boolean isType(GenericDescriptor descriptor) { 2342 return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor); 2343 } 2344 2345 /** Checks if the descriptor is a valid namespace type. */ isAggregate(GenericDescriptor descriptor)2346 boolean isAggregate(GenericDescriptor descriptor) { 2347 return (descriptor instanceof Descriptor) 2348 || (descriptor instanceof EnumDescriptor) 2349 || (descriptor instanceof PackageDescriptor) 2350 || (descriptor instanceof ServiceDescriptor); 2351 } 2352 2353 /** 2354 * Look up a type descriptor by name, relative to some other descriptor. The name may be 2355 * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name 2356 * lookup semantics are used to search for the matching descriptor. 2357 */ lookupSymbol( final String name, final GenericDescriptor relativeTo, final DescriptorPool.SearchFilter filter)2358 GenericDescriptor lookupSymbol( 2359 final String name, 2360 final GenericDescriptor relativeTo, 2361 final DescriptorPool.SearchFilter filter) 2362 throws DescriptorValidationException { 2363 // TODO(kenton): This could be optimized in a number of ways. 2364 2365 GenericDescriptor result; 2366 String fullname; 2367 if (name.startsWith(".")) { 2368 // Fully-qualified name. 2369 fullname = name.substring(1); 2370 result = findSymbol(fullname, filter); 2371 } else { 2372 // If "name" is a compound identifier, we want to search for the 2373 // first component of it, then search within it for the rest. 2374 // If name is something like "Foo.Bar.baz", and symbols named "Foo" are 2375 // defined in multiple parent scopes, we only want to find "Bar.baz" in 2376 // the innermost one. E.g., the following should produce an error: 2377 // message Bar { message Baz {} } 2378 // message Foo { 2379 // message Bar { 2380 // } 2381 // optional Bar.Baz baz = 1; 2382 // } 2383 // So, we look for just "Foo" first, then look for "Bar.baz" within it 2384 // if found. 2385 final int firstPartLength = name.indexOf('.'); 2386 final String firstPart; 2387 if (firstPartLength == -1) { 2388 firstPart = name; 2389 } else { 2390 firstPart = name.substring(0, firstPartLength); 2391 } 2392 2393 // We will search each parent scope of "relativeTo" looking for the 2394 // symbol. 2395 final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName()); 2396 2397 while (true) { 2398 // Chop off the last component of the scope. 2399 final int dotpos = scopeToTry.lastIndexOf("."); 2400 if (dotpos == -1) { 2401 fullname = name; 2402 result = findSymbol(name, filter); 2403 break; 2404 } else { 2405 scopeToTry.setLength(dotpos + 1); 2406 2407 // Append firstPart and try to find 2408 scopeToTry.append(firstPart); 2409 result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY); 2410 2411 if (result != null) { 2412 if (firstPartLength != -1) { 2413 // We only found the first part of the symbol. Now look for 2414 // the whole thing. If this fails, we *don't* want to keep 2415 // searching parent scopes. 2416 scopeToTry.setLength(dotpos + 1); 2417 scopeToTry.append(name); 2418 result = findSymbol(scopeToTry.toString(), filter); 2419 } 2420 fullname = scopeToTry.toString(); 2421 break; 2422 } 2423 2424 // Not found. Remove the name so we can try again. 2425 scopeToTry.setLength(dotpos); 2426 } 2427 } 2428 } 2429 2430 if (result == null) { 2431 if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) { 2432 logger.warning( 2433 "The descriptor for message type \"" 2434 + name 2435 + "\" can not be found and a placeholder is created for it"); 2436 // We create a dummy message descriptor here regardless of the 2437 // expected type. If the type should be message, this dummy 2438 // descriptor will work well and if the type should be enum, a 2439 // DescriptorValidationException will be thrown latter. In either 2440 // case, the code works as expected: we allow unknown message types 2441 // but not unknown enum types. 2442 result = new Descriptor(fullname); 2443 // Add the placeholder file as a dependency so we can find the 2444 // placeholder symbol when resolving other references. 2445 this.dependencies.add(result.getFile()); 2446 return result; 2447 } else { 2448 throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined."); 2449 } 2450 } else { 2451 return result; 2452 } 2453 } 2454 2455 /** 2456 * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an 2457 * error. 2458 */ addSymbol(final GenericDescriptor descriptor)2459 void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException { 2460 validateSymbolName(descriptor); 2461 2462 final String fullName = descriptor.getFullName(); 2463 2464 final GenericDescriptor old = descriptorsByName.put(fullName, descriptor); 2465 if (old != null) { 2466 descriptorsByName.put(fullName, old); 2467 2468 if (descriptor.getFile() == old.getFile()) { 2469 final int dotpos = fullName.lastIndexOf('.'); 2470 if (dotpos == -1) { 2471 throw new DescriptorValidationException( 2472 descriptor, '\"' + fullName + "\" is already defined."); 2473 } else { 2474 throw new DescriptorValidationException( 2475 descriptor, 2476 '\"' 2477 + fullName.substring(dotpos + 1) 2478 + "\" is already defined in \"" 2479 + fullName.substring(0, dotpos) 2480 + "\"."); 2481 } 2482 } else { 2483 throw new DescriptorValidationException( 2484 descriptor, 2485 '\"' 2486 + fullName 2487 + "\" is already defined in file \"" 2488 + old.getFile().getName() 2489 + "\"."); 2490 } 2491 } 2492 } 2493 2494 /** 2495 * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so 2496 * that someone cannot define, say, a message type that has the same name as an existing 2497 * package. 2498 */ 2499 private static final class PackageDescriptor extends GenericDescriptor { 2500 @Override toProto()2501 public Message toProto() { 2502 return file.toProto(); 2503 } 2504 2505 @Override getName()2506 public String getName() { 2507 return name; 2508 } 2509 2510 @Override getFullName()2511 public String getFullName() { 2512 return fullName; 2513 } 2514 2515 @Override getFile()2516 public FileDescriptor getFile() { 2517 return file; 2518 } 2519 PackageDescriptor(final String name, final String fullName, final FileDescriptor file)2520 PackageDescriptor(final String name, final String fullName, final FileDescriptor file) { 2521 this.file = file; 2522 this.fullName = fullName; 2523 this.name = name; 2524 } 2525 2526 private final String name; 2527 private final String fullName; 2528 private final FileDescriptor file; 2529 } 2530 2531 /** 2532 * Adds a package to the symbol tables. If a package by the same name already exists, that is 2533 * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If 2534 * the package has multiple components, this also adds the parent package(s). 2535 */ addPackage(final String fullName, final FileDescriptor file)2536 void addPackage(final String fullName, final FileDescriptor file) 2537 throws DescriptorValidationException { 2538 final int dotpos = fullName.lastIndexOf('.'); 2539 final String name; 2540 if (dotpos == -1) { 2541 name = fullName; 2542 } else { 2543 addPackage(fullName.substring(0, dotpos), file); 2544 name = fullName.substring(dotpos + 1); 2545 } 2546 2547 final GenericDescriptor old = 2548 descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file)); 2549 if (old != null) { 2550 descriptorsByName.put(fullName, old); 2551 if (!(old instanceof PackageDescriptor)) { 2552 throw new DescriptorValidationException( 2553 file, 2554 '\"' 2555 + name 2556 + "\" is already defined (as something other than a " 2557 + "package) in file \"" 2558 + old.getFile().getName() 2559 + "\"."); 2560 } 2561 } 2562 } 2563 2564 /** A (GenericDescriptor, int) pair, used as a map key. */ 2565 private static final class DescriptorIntPair { 2566 private final GenericDescriptor descriptor; 2567 private final int number; 2568 DescriptorIntPair(final GenericDescriptor descriptor, final int number)2569 DescriptorIntPair(final GenericDescriptor descriptor, final int number) { 2570 this.descriptor = descriptor; 2571 this.number = number; 2572 } 2573 2574 @Override hashCode()2575 public int hashCode() { 2576 return descriptor.hashCode() * ((1 << 16) - 1) + number; 2577 } 2578 2579 @Override equals(final Object obj)2580 public boolean equals(final Object obj) { 2581 if (!(obj instanceof DescriptorIntPair)) { 2582 return false; 2583 } 2584 final DescriptorIntPair other = (DescriptorIntPair) obj; 2585 return descriptor == other.descriptor && number == other.number; 2586 } 2587 } 2588 2589 /** 2590 * Adds a field to the fieldsByNumber table. Throws an exception if a field with the same 2591 * containing type and number already exists. 2592 */ addFieldByNumber(final FieldDescriptor field)2593 void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException { 2594 final DescriptorIntPair key = 2595 new DescriptorIntPair(field.getContainingType(), field.getNumber()); 2596 final FieldDescriptor old = fieldsByNumber.put(key, field); 2597 if (old != null) { 2598 fieldsByNumber.put(key, old); 2599 throw new DescriptorValidationException( 2600 field, 2601 "Field number " 2602 + field.getNumber() 2603 + " has already been used in \"" 2604 + field.getContainingType().getFullName() 2605 + "\" by field \"" 2606 + old.getName() 2607 + "\"."); 2608 } 2609 } 2610 2611 /** 2612 * Adds an enum value to the enumValuesByNumber table. If an enum value with the same type and 2613 * number already exists, does nothing. (This is allowed; the first value define with the number 2614 * takes precedence.) 2615 */ addEnumValueByNumber(final EnumValueDescriptor value)2616 void addEnumValueByNumber(final EnumValueDescriptor value) { 2617 final DescriptorIntPair key = new DescriptorIntPair(value.getType(), value.getNumber()); 2618 final EnumValueDescriptor old = enumValuesByNumber.put(key, value); 2619 if (old != null) { 2620 enumValuesByNumber.put(key, old); 2621 // Not an error: Multiple enum values may have the same number, but 2622 // we only want the first one in the map. 2623 } 2624 } 2625 2626 /** 2627 * Verifies that the descriptor's name is valid (i.e. it contains only letters, digits, and 2628 * underscores, and does not start with a digit). 2629 */ validateSymbolName(final GenericDescriptor descriptor)2630 static void validateSymbolName(final GenericDescriptor descriptor) 2631 throws DescriptorValidationException { 2632 final String name = descriptor.getName(); 2633 if (name.length() == 0) { 2634 throw new DescriptorValidationException(descriptor, "Missing name."); 2635 } 2636 2637 // Non-ASCII characters are not valid in protobuf identifiers, even 2638 // if they are letters or digits. 2639 // The first character must be a letter or '_'. 2640 // Subsequent characters may be letters, numbers, or digits. 2641 for (int i = 0; i < name.length(); i++) { 2642 final char c = name.charAt(i); 2643 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 2644 || (c == '_') 2645 || ('0' <= c && c <= '9' && i > 0)) { 2646 // Valid 2647 continue; 2648 } 2649 throw new DescriptorValidationException( 2650 descriptor, '\"' + name + "\" is not a valid identifier."); 2651 } 2652 } 2653 } 2654 2655 /** Describes an oneof of a message type. */ 2656 public static final class OneofDescriptor extends GenericDescriptor { 2657 /** Get the index of this descriptor within its parent. */ getIndex()2658 public int getIndex() { 2659 return index; 2660 } 2661 2662 @Override getName()2663 public String getName() { 2664 return proto.getName(); 2665 } 2666 2667 @Override getFile()2668 public FileDescriptor getFile() { 2669 return file; 2670 } 2671 2672 @Override getFullName()2673 public String getFullName() { 2674 return fullName; 2675 } 2676 getContainingType()2677 public Descriptor getContainingType() { 2678 return containingType; 2679 } 2680 getFieldCount()2681 public int getFieldCount() { 2682 return fieldCount; 2683 } 2684 getOptions()2685 public OneofOptions getOptions() { 2686 return proto.getOptions(); 2687 } 2688 isSynthetic()2689 public boolean isSynthetic() { 2690 return fields.length == 1 && fields[0].isProto3Optional; 2691 } 2692 2693 /** Get a list of this message type's fields. */ getFields()2694 public List<FieldDescriptor> getFields() { 2695 return Collections.unmodifiableList(Arrays.asList(fields)); 2696 } 2697 getField(int index)2698 public FieldDescriptor getField(int index) { 2699 return fields[index]; 2700 } 2701 2702 @Override toProto()2703 public OneofDescriptorProto toProto() { 2704 return proto; 2705 } 2706 setProto(final OneofDescriptorProto proto)2707 private void setProto(final OneofDescriptorProto proto) { 2708 this.proto = proto; 2709 } 2710 OneofDescriptor( final OneofDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)2711 private OneofDescriptor( 2712 final OneofDescriptorProto proto, 2713 final FileDescriptor file, 2714 final Descriptor parent, 2715 final int index) 2716 throws DescriptorValidationException { 2717 this.proto = proto; 2718 fullName = computeFullName(file, parent, proto.getName()); 2719 this.file = file; 2720 this.index = index; 2721 2722 containingType = parent; 2723 fieldCount = 0; 2724 } 2725 2726 private final int index; 2727 private OneofDescriptorProto proto; 2728 private final String fullName; 2729 private final FileDescriptor file; 2730 2731 private Descriptor containingType; 2732 private int fieldCount; 2733 private FieldDescriptor[] fields; 2734 } 2735 } 2736