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