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