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