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