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