1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.protobuf.Internal.checkNotNull; 11 12 import com.google.protobuf.DescriptorProtos.DescriptorProto; 13 import com.google.protobuf.DescriptorProtos.Edition; 14 import com.google.protobuf.DescriptorProtos.EnumDescriptorProto; 15 import com.google.protobuf.DescriptorProtos.EnumOptions; 16 import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto; 17 import com.google.protobuf.DescriptorProtos.EnumValueOptions; 18 import com.google.protobuf.DescriptorProtos.FeatureSet; 19 import com.google.protobuf.DescriptorProtos.FeatureSetDefaults; 20 import com.google.protobuf.DescriptorProtos.FeatureSetDefaults.FeatureSetEditionDefault; 21 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; 22 import com.google.protobuf.DescriptorProtos.FieldOptions; 23 import com.google.protobuf.DescriptorProtos.FileDescriptorProto; 24 import com.google.protobuf.DescriptorProtos.FileOptions; 25 import com.google.protobuf.DescriptorProtos.MessageOptions; 26 import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; 27 import com.google.protobuf.DescriptorProtos.MethodOptions; 28 import com.google.protobuf.DescriptorProtos.OneofDescriptorProto; 29 import com.google.protobuf.DescriptorProtos.OneofOptions; 30 import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; 31 import com.google.protobuf.DescriptorProtos.ServiceOptions; 32 import com.google.protobuf.Descriptors.DescriptorValidationException; 33 import com.google.protobuf.JavaFeaturesProto.JavaFeatures; 34 import java.lang.ref.ReferenceQueue; 35 import java.lang.ref.WeakReference; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collections; 39 import java.util.Comparator; 40 import java.util.HashMap; 41 import java.util.IdentityHashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.concurrent.ConcurrentHashMap; 46 import java.util.logging.Logger; 47 48 /** 49 * Contains a collection of classes which describe protocol message types. 50 * 51 * <p>Every message type has a {@link Descriptor}, which lists all its fields and other information 52 * about a type. You can get a message type's descriptor by calling {@code 53 * MessageType.getDescriptor()}, or (given a message object of the type) {@code 54 * message.getDescriptorForType()}. Furthermore, each message is associated with a {@link 55 * FileDescriptor} for a relevant {@code .proto} file. You can obtain it by calling {@code 56 * Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors for all the messages defined 57 * in that file, and file descriptors for all the imported {@code .proto} files. 58 * 59 * <p>Descriptors are built from DescriptorProtos, as defined in {@code 60 * google/protobuf/descriptor.proto}. 61 * 62 * @author kenton@google.com Kenton Varda 63 */ 64 @CheckReturnValue 65 public final class Descriptors { 66 private static final Logger logger = Logger.getLogger(Descriptors.class.getName()); 67 private static final int[] EMPTY_INT_ARRAY = new int[0]; 68 private static final Descriptor[] EMPTY_DESCRIPTORS = new Descriptor[0]; 69 private static final FieldDescriptor[] EMPTY_FIELD_DESCRIPTORS = new FieldDescriptor[0]; 70 private static final EnumDescriptor[] EMPTY_ENUM_DESCRIPTORS = new EnumDescriptor[0]; 71 private static final ServiceDescriptor[] EMPTY_SERVICE_DESCRIPTORS = new ServiceDescriptor[0]; 72 private static final OneofDescriptor[] EMPTY_ONEOF_DESCRIPTORS = new OneofDescriptor[0]; 73 private static final ConcurrentHashMap<Integer, FeatureSet> FEATURE_CACHE = 74 new ConcurrentHashMap<>(); 75 76 @SuppressWarnings("NonFinalStaticField") 77 private static volatile FeatureSetDefaults javaEditionDefaults = null; 78 79 /** Sets the default feature mappings used during the build. Exposed for tests. */ setTestJavaEditionDefaults(FeatureSetDefaults defaults)80 static void setTestJavaEditionDefaults(FeatureSetDefaults defaults) { 81 javaEditionDefaults = defaults; 82 } 83 84 /** Gets the default feature mappings used during the build. */ getJavaEditionDefaults()85 static FeatureSetDefaults getJavaEditionDefaults() { 86 // Force explicit initialization before synchronized block which can trigger initialization in 87 // `JavaFeaturesProto.registerAllExtensions()` and `FeatureSetdefaults.parseFrom()` calls. 88 // Otherwise, this can result in deadlock if another threads holds the static init block's 89 // implicit lock. This operation should be cheap if initialization has already occurred. 90 Descriptor unused1 = FeatureSetDefaults.getDescriptor(); 91 FileDescriptor unused2 = JavaFeaturesProto.getDescriptor(); 92 if (javaEditionDefaults == null) { 93 synchronized (Descriptors.class) { 94 if (javaEditionDefaults == null) { 95 try { 96 ExtensionRegistry registry = ExtensionRegistry.newInstance(); 97 registry.add(JavaFeaturesProto.java_); 98 setTestJavaEditionDefaults( 99 FeatureSetDefaults.parseFrom( 100 JavaEditionDefaults.PROTOBUF_INTERNAL_JAVA_EDITION_DEFAULTS.getBytes( 101 Internal.ISO_8859_1), 102 registry)); 103 } catch (Exception e) { 104 throw new AssertionError(e); 105 } 106 } 107 } 108 } 109 return javaEditionDefaults; 110 } 111 getEditionDefaults(Edition edition)112 static FeatureSet getEditionDefaults(Edition edition) { 113 FeatureSetDefaults javaEditionDefaults = getJavaEditionDefaults(); 114 if (edition.getNumber() < javaEditionDefaults.getMinimumEdition().getNumber()) { 115 throw new IllegalArgumentException( 116 "Edition " 117 + edition 118 + " is lower than the minimum supported edition " 119 + javaEditionDefaults.getMinimumEdition() 120 + "!"); 121 } 122 if (edition.getNumber() > javaEditionDefaults.getMaximumEdition().getNumber()) { 123 throw new IllegalArgumentException( 124 "Edition " 125 + edition 126 + " is greater than the maximum supported edition " 127 + javaEditionDefaults.getMaximumEdition() 128 + "!"); 129 } 130 FeatureSetEditionDefault found = null; 131 for (FeatureSetEditionDefault editionDefault : javaEditionDefaults.getDefaultsList()) { 132 if (editionDefault.getEdition().getNumber() > edition.getNumber()) { 133 break; 134 } 135 found = editionDefault; 136 } 137 if (found == null) { 138 throw new IllegalArgumentException( 139 "Edition " + edition + " does not have a valid default FeatureSet!"); 140 } 141 return found.getFixedFeatures().toBuilder().mergeFrom(found.getOverridableFeatures()).build(); 142 } 143 internFeatures(FeatureSet features)144 private static FeatureSet internFeatures(FeatureSet features) { 145 FeatureSet cached = FEATURE_CACHE.putIfAbsent(features.hashCode(), features); 146 if (cached == null) { 147 return features; 148 } 149 return cached; 150 } 151 152 /** 153 * Describes a {@code .proto} file, including everything defined within. That includes, in 154 * particular, descriptors for all the messages and file descriptors for all other imported {@code 155 * .proto} files (dependencies). 156 */ 157 public static final class FileDescriptor extends GenericDescriptor { 158 /** Convert the descriptor to its protocol message representation. */ 159 @Override toProto()160 public FileDescriptorProto toProto() { 161 return proto; 162 } 163 164 /** Get the file name. */ 165 @Override getName()166 public String getName() { 167 return proto.getName(); 168 } 169 170 /** Returns this object. */ 171 @Override getFile()172 public FileDescriptor getFile() { 173 return this; 174 } 175 176 /** Returns the same as getName(). */ 177 @Override getFullName()178 public String getFullName() { 179 return proto.getName(); 180 } 181 182 /** 183 * Get the proto package name. This is the package name given by the {@code package} statement 184 * in the {@code .proto} file, which differs from the Java package. 185 */ getPackage()186 public String getPackage() { 187 return proto.getPackage(); 188 } 189 190 /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */ getOptions()191 public FileOptions getOptions() { 192 if (this.options == null) { 193 FileOptions strippedOptions = this.proto.getOptions(); 194 if (strippedOptions.hasFeatures()) { 195 // Clients should be using feature accessor methods, not accessing features on the 196 // options 197 // proto. 198 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 199 } 200 synchronized (this) { 201 if (this.options == null) { 202 this.options = strippedOptions; 203 } 204 } 205 } 206 return this.options; 207 } 208 209 /** Get a list of top-level message types declared in this file. */ getMessageTypes()210 public List<Descriptor> getMessageTypes() { 211 return Collections.unmodifiableList(Arrays.asList(messageTypes)); 212 } 213 214 /** Get a list of top-level enum types declared in this file. */ getEnumTypes()215 public List<EnumDescriptor> getEnumTypes() { 216 return Collections.unmodifiableList(Arrays.asList(enumTypes)); 217 } 218 219 /** Get a list of top-level services declared in this file. */ getServices()220 public List<ServiceDescriptor> getServices() { 221 return Collections.unmodifiableList(Arrays.asList(services)); 222 } 223 224 /** Get a list of top-level extensions declared in this file. */ getExtensions()225 public List<FieldDescriptor> getExtensions() { 226 return Collections.unmodifiableList(Arrays.asList(extensions)); 227 } 228 229 /** Get a list of this file's dependencies (imports). */ getDependencies()230 public List<FileDescriptor> getDependencies() { 231 return Collections.unmodifiableList(Arrays.asList(dependencies)); 232 } 233 234 /** Get a list of this file's public dependencies (public imports). */ getPublicDependencies()235 public List<FileDescriptor> getPublicDependencies() { 236 return Collections.unmodifiableList(Arrays.asList(publicDependencies)); 237 } 238 239 /** Get the edition of the .proto file. */ getEdition()240 Edition getEdition() { 241 switch (proto.getSyntax()) { 242 case "editions": 243 return proto.getEdition(); 244 case "proto3": 245 return Edition.EDITION_PROTO3; 246 default: 247 return Edition.EDITION_PROTO2; 248 } 249 } 250 copyHeadingTo(FileDescriptorProto.Builder protoBuilder)251 public void copyHeadingTo(FileDescriptorProto.Builder protoBuilder) { 252 protoBuilder.setName(getName()).setSyntax(proto.getSyntax()); 253 if (!getPackage().isEmpty()) { 254 protoBuilder.setPackage(getPackage()); 255 } 256 if (proto.getSyntax().equals("editions")) { 257 protoBuilder.setEdition(proto.getEdition()); 258 } 259 if (proto.hasOptions() && !proto.getOptions().equals(FileOptions.getDefaultInstance())) { 260 protoBuilder.setOptions(proto.getOptions()); 261 } 262 } 263 264 /** 265 * Find a message type in the file by name. Does not find nested types. 266 * 267 * @param name The unqualified type name to look for. 268 * @return The message type's descriptor, or {@code null} if not found. 269 */ findMessageTypeByName(String name)270 public Descriptor findMessageTypeByName(String name) { 271 // Don't allow looking up nested types. This will make optimization 272 // easier later. 273 if (name.indexOf('.') != -1) { 274 return null; 275 } 276 final String packageName = getPackage(); 277 if (!packageName.isEmpty()) { 278 name = packageName + '.' + name; 279 } 280 final GenericDescriptor result = pool.findSymbol(name); 281 if (result instanceof Descriptor && result.getFile() == this) { 282 return (Descriptor) result; 283 } else { 284 return null; 285 } 286 } 287 288 /** 289 * Find an enum type in the file by name. Does not find nested types. 290 * 291 * @param name The unqualified type name to look for. 292 * @return The enum type's descriptor, or {@code null} if not found. 293 */ findEnumTypeByName(String name)294 public EnumDescriptor findEnumTypeByName(String name) { 295 // Don't allow looking up nested types. This will make optimization 296 // easier later. 297 if (name.indexOf('.') != -1) { 298 return null; 299 } 300 final String packageName = getPackage(); 301 if (!packageName.isEmpty()) { 302 name = packageName + '.' + name; 303 } 304 final GenericDescriptor result = pool.findSymbol(name); 305 if (result instanceof EnumDescriptor && result.getFile() == this) { 306 return (EnumDescriptor) result; 307 } else { 308 return null; 309 } 310 } 311 312 /** 313 * Find a service type in the file by name. 314 * 315 * @param name The unqualified type name to look for. 316 * @return The service type's descriptor, or {@code null} if not found. 317 */ findServiceByName(String name)318 public ServiceDescriptor findServiceByName(String name) { 319 // Don't allow looking up nested types. This will make optimization 320 // easier later. 321 if (name.indexOf('.') != -1) { 322 return null; 323 } 324 final String packageName = getPackage(); 325 if (!packageName.isEmpty()) { 326 name = packageName + '.' + name; 327 } 328 final GenericDescriptor result = pool.findSymbol(name); 329 if (result instanceof ServiceDescriptor && result.getFile() == this) { 330 return (ServiceDescriptor) result; 331 } else { 332 return null; 333 } 334 } 335 336 /** 337 * Find an extension in the file by name. Does not find extensions nested inside message types. 338 * 339 * @param name The unqualified extension name to look for. 340 * @return The extension's descriptor, or {@code null} if not found. 341 */ findExtensionByName(String name)342 public FieldDescriptor findExtensionByName(String name) { 343 if (name.indexOf('.') != -1) { 344 return null; 345 } 346 final String packageName = getPackage(); 347 if (!packageName.isEmpty()) { 348 name = packageName + '.' + name; 349 } 350 final GenericDescriptor result = pool.findSymbol(name); 351 if (result instanceof FieldDescriptor && result.getFile() == this) { 352 return (FieldDescriptor) result; 353 } else { 354 return null; 355 } 356 } 357 358 /** 359 * Construct a {@code FileDescriptor}. 360 * 361 * @param proto the protocol message form of the FileDescriptort 362 * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies. 363 * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur 364 * for a number of reasons; for instance, because a field has an undefined type or because 365 * two messages were defined with the same name. 366 */ buildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)367 public static FileDescriptor buildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies) 368 throws DescriptorValidationException { 369 return buildFrom(proto, dependencies, false); 370 } 371 372 /** 373 * Construct a {@code FileDescriptor}. 374 * 375 * @param proto the protocol message form of the FileDescriptor 376 * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies 377 * @param allowUnknownDependencies if true, non-existing dependencies will be ignored and 378 * undefined message types will be replaced with a placeholder type. Undefined enum types 379 * still cause a DescriptorValidationException. 380 * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur 381 * for a number of reasons; for instance, because a field has an undefined type or because 382 * two messages were defined with the same name. 383 */ buildFrom( FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies)384 public static FileDescriptor buildFrom( 385 FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies) 386 throws DescriptorValidationException { 387 return buildFrom(proto, dependencies, allowUnknownDependencies, false); 388 } 389 buildFrom( FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies, boolean allowUnresolvedFeatures)390 private static FileDescriptor buildFrom( 391 FileDescriptorProto proto, 392 FileDescriptor[] dependencies, 393 boolean allowUnknownDependencies, 394 boolean allowUnresolvedFeatures) 395 throws DescriptorValidationException { 396 // Building descriptors involves two steps: translating and linking. 397 // In the translation step (implemented by FileDescriptor's 398 // constructor), we build an object tree mirroring the 399 // FileDescriptorProto's tree and put all of the descriptors into the 400 // DescriptorPool's lookup tables. In the linking step, we look up all 401 // type references in the DescriptorPool, so that, for example, a 402 // FieldDescriptor for an embedded message contains a pointer directly 403 // to the Descriptor for that message's type. We also detect undefined 404 // types in the linking step. 405 DescriptorPool pool = new DescriptorPool(dependencies, allowUnknownDependencies); 406 FileDescriptor result = 407 new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies); 408 result.crossLink(); 409 // Skip feature resolution until later for calls from gencode. 410 if (!allowUnresolvedFeatures) { 411 // We do not need to force feature resolution for proto1 dependencies 412 // since dependencies from non-gencode should already be fully feature resolved. 413 result.resolveAllFeaturesInternal(); 414 } 415 return result; 416 } 417 latin1Cat(final String[] strings)418 private static byte[] latin1Cat(final String[] strings) { 419 // Hack: We can't embed a raw byte array inside generated Java code 420 // (at least, not efficiently), but we can embed Strings. So, the 421 // protocol compiler embeds the FileDescriptorProto as a giant 422 // string literal which is passed to this function to construct the 423 // file's FileDescriptor. The string literal contains only 8-bit 424 // characters, each one representing a byte of the FileDescriptorProto's 425 // serialized form. So, if we convert it to bytes in ISO-8859-1, we 426 // should get the original bytes that we want. 427 // Literal strings are limited to 64k, so it may be split into multiple strings. 428 if (strings.length == 1) { 429 return strings[0].getBytes(Internal.ISO_8859_1); 430 } 431 StringBuilder descriptorData = new StringBuilder(); 432 for (String part : strings) { 433 descriptorData.append(part); 434 } 435 return descriptorData.toString().getBytes(Internal.ISO_8859_1); 436 } 437 findDescriptors( final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)438 private static FileDescriptor[] findDescriptors( 439 final Class<?> descriptorOuterClass, 440 final String[] dependencyClassNames, 441 final String[] dependencyFileNames) { 442 List<FileDescriptor> descriptors = new ArrayList<>(); 443 for (int i = 0; i < dependencyClassNames.length; i++) { 444 try { 445 Class<?> clazz = descriptorOuterClass.getClassLoader().loadClass(dependencyClassNames[i]); 446 descriptors.add((FileDescriptor) clazz.getField("descriptor").get(null)); 447 } catch (Exception e) { 448 // We allow unknown dependencies by default. If a dependency cannot 449 // be found we only generate a warning. 450 logger.warning("Descriptors for \"" + dependencyFileNames[i] + "\" can not be found."); 451 } 452 } 453 return descriptors.toArray(new FileDescriptor[0]); 454 } 455 456 /** 457 * This method is to be called by generated code only. It is equivalent to {@code buildFrom} 458 * except that the {@code FileDescriptorProto} is encoded in protocol buffer wire format. 459 */ internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final FileDescriptor[] dependencies)460 public static FileDescriptor internalBuildGeneratedFileFrom( 461 final String[] descriptorDataParts, final FileDescriptor[] dependencies) { 462 final byte[] descriptorBytes = latin1Cat(descriptorDataParts); 463 464 FileDescriptorProto proto; 465 try { 466 proto = FileDescriptorProto.parseFrom(descriptorBytes); 467 } catch (InvalidProtocolBufferException e) { 468 throw new IllegalArgumentException( 469 "Failed to parse protocol buffer descriptor for generated code.", e); 470 } 471 472 try { 473 // When building descriptors for generated code, we allow unknown 474 // dependencies by default and delay feature resolution until later. 475 return buildFrom(proto, dependencies, true, true); 476 } catch (DescriptorValidationException e) { 477 throw new IllegalArgumentException( 478 "Invalid embedded descriptor for \"" + proto.getName() + "\".", e); 479 } 480 } 481 482 /** 483 * This method is to be called by generated code only. It uses Java reflection to load the 484 * dependencies' descriptors. 485 */ internalBuildGeneratedFileFrom( final String[] descriptorDataParts, final Class<?> descriptorOuterClass, final String[] dependencyClassNames, final String[] dependencyFileNames)486 public static FileDescriptor internalBuildGeneratedFileFrom( 487 final String[] descriptorDataParts, 488 final Class<?> descriptorOuterClass, 489 final String[] dependencyClassNames, 490 final String[] dependencyFileNames) { 491 FileDescriptor[] dependencies = 492 findDescriptors(descriptorOuterClass, dependencyClassNames, dependencyFileNames); 493 return internalBuildGeneratedFileFrom(descriptorDataParts, dependencies); 494 } 495 496 /** 497 * This method is to be called by generated code only. It updates the FileDescriptorProto 498 * associated with the descriptor by parsing it again with the given ExtensionRegistry. This is 499 * needed to recognize custom options. 500 */ internalUpdateFileDescriptor( FileDescriptor descriptor, ExtensionRegistry registry)501 public static void internalUpdateFileDescriptor( 502 FileDescriptor descriptor, ExtensionRegistry registry) { 503 ByteString bytes = descriptor.proto.toByteString(); 504 try { 505 FileDescriptorProto proto = FileDescriptorProto.parseFrom(bytes, registry); 506 descriptor.setProto(proto); 507 } catch (InvalidProtocolBufferException e) { 508 throw new IllegalArgumentException( 509 "Failed to parse protocol buffer descriptor for generated code.", e); 510 } 511 } 512 513 /** 514 * This class should be used by generated code only. When calling {@link 515 * FileDescriptor#internalBuildGeneratedFileFrom}, the caller provides a callback implementing 516 * this interface. The callback is called after the FileDescriptor has been constructed, in 517 * order to assign all the global variables defined in the generated code which point at parts 518 * of the FileDescriptor. The callback returns an ExtensionRegistry which contains any 519 * extensions which might be used in the descriptor -- that is, extensions of the various 520 * "Options" messages defined in descriptor.proto. The callback may also return null to indicate 521 * that no extensions are used in the descriptor. 522 * 523 * <p>This interface is deprecated. Use the return value of internalBuildGeneratedFrom() 524 * instead. 525 */ 526 @Deprecated 527 public interface InternalDescriptorAssigner { assignDescriptors(FileDescriptor root)528 ExtensionRegistry assignDescriptors(FileDescriptor root); 529 } 530 531 private FileDescriptorProto proto; 532 private volatile FileOptions options; 533 private final Descriptor[] messageTypes; 534 private final EnumDescriptor[] enumTypes; 535 private final ServiceDescriptor[] services; 536 private final FieldDescriptor[] extensions; 537 private final FileDescriptor[] dependencies; 538 private final FileDescriptor[] publicDependencies; 539 private final DescriptorPool pool; 540 private boolean featuresResolved; 541 FileDescriptor( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final DescriptorPool pool, boolean allowUnknownDependencies)542 private FileDescriptor( 543 final FileDescriptorProto proto, 544 final FileDescriptor[] dependencies, 545 final DescriptorPool pool, 546 boolean allowUnknownDependencies) 547 throws DescriptorValidationException { 548 this.pool = pool; 549 this.proto = proto; 550 this.dependencies = dependencies.clone(); 551 this.featuresResolved = false; 552 HashMap<String, FileDescriptor> nameToFileMap = new HashMap<>(); 553 for (FileDescriptor file : dependencies) { 554 nameToFileMap.put(file.getName(), file); 555 } 556 List<FileDescriptor> publicDependencies = new ArrayList<>(); 557 for (int i = 0; i < proto.getPublicDependencyCount(); i++) { 558 int index = proto.getPublicDependency(i); 559 if (index < 0 || index >= proto.getDependencyCount()) { 560 throw new DescriptorValidationException(this, "Invalid public dependency index."); 561 } 562 String name = proto.getDependency(index); 563 FileDescriptor file = nameToFileMap.get(name); 564 if (file == null) { 565 if (!allowUnknownDependencies) { 566 throw new DescriptorValidationException(this, "Invalid public dependency: " + name); 567 } 568 // Ignore unknown dependencies. 569 } else { 570 publicDependencies.add(file); 571 } 572 } 573 this.publicDependencies = new FileDescriptor[publicDependencies.size()]; 574 publicDependencies.toArray(this.publicDependencies); 575 576 pool.addPackage(getPackage(), this); 577 578 messageTypes = 579 (proto.getMessageTypeCount() > 0) 580 ? new Descriptor[proto.getMessageTypeCount()] 581 : EMPTY_DESCRIPTORS; 582 for (int i = 0; i < proto.getMessageTypeCount(); i++) { 583 messageTypes[i] = new Descriptor(proto.getMessageType(i), this, null, i); 584 } 585 586 enumTypes = 587 (proto.getEnumTypeCount() > 0) 588 ? new EnumDescriptor[proto.getEnumTypeCount()] 589 : EMPTY_ENUM_DESCRIPTORS; 590 for (int i = 0; i < proto.getEnumTypeCount(); i++) { 591 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i); 592 } 593 594 services = 595 (proto.getServiceCount() > 0) 596 ? new ServiceDescriptor[proto.getServiceCount()] 597 : EMPTY_SERVICE_DESCRIPTORS; 598 for (int i = 0; i < proto.getServiceCount(); i++) { 599 services[i] = new ServiceDescriptor(proto.getService(i), this, i); 600 } 601 602 extensions = 603 (proto.getExtensionCount() > 0) 604 ? new FieldDescriptor[proto.getExtensionCount()] 605 : EMPTY_FIELD_DESCRIPTORS; 606 for (int i = 0; i < proto.getExtensionCount(); i++) { 607 extensions[i] = new FieldDescriptor(proto.getExtension(i), this, null, i, true); 608 } 609 } 610 611 /** Create a placeholder FileDescriptor for a message Descriptor. */ FileDescriptor(String packageName, Descriptor message)612 FileDescriptor(String packageName, Descriptor message) throws DescriptorValidationException { 613 this.parent = null; 614 this.pool = new DescriptorPool(new FileDescriptor[0], true); 615 this.proto = 616 FileDescriptorProto.newBuilder() 617 .setName(message.getFullName() + ".placeholder.proto") 618 .setPackage(packageName) 619 .addMessageType(message.toProto()) 620 .build(); 621 this.dependencies = new FileDescriptor[0]; 622 this.publicDependencies = new FileDescriptor[0]; 623 this.featuresResolved = false; 624 625 messageTypes = new Descriptor[] {message}; 626 enumTypes = EMPTY_ENUM_DESCRIPTORS; 627 services = EMPTY_SERVICE_DESCRIPTORS; 628 extensions = EMPTY_FIELD_DESCRIPTORS; 629 630 pool.addPackage(packageName, this); 631 pool.addSymbol(message); 632 } 633 resolveAllFeaturesImmutable()634 public void resolveAllFeaturesImmutable() { 635 try { 636 resolveAllFeaturesInternal(); 637 } catch (DescriptorValidationException e) { 638 throw new IllegalArgumentException("Invalid features for \"" + proto.getName() + "\".", e); 639 } 640 } 641 642 /** 643 * This method is to be called by generated code only. It resolves features for the descriptor 644 * and all of its children. 645 */ resolveAllFeaturesInternal()646 private void resolveAllFeaturesInternal() throws DescriptorValidationException { 647 if (this.featuresResolved) { 648 return; 649 } 650 651 synchronized (this) { 652 if (this.featuresResolved) { 653 return; 654 } 655 resolveFeatures(proto.getOptions().getFeatures()); 656 657 for (Descriptor messageType : messageTypes) { 658 messageType.resolveAllFeatures(); 659 } 660 661 for (EnumDescriptor enumType : enumTypes) { 662 enumType.resolveAllFeatures(); 663 } 664 665 for (ServiceDescriptor service : services) { 666 service.resolveAllFeatures(); 667 } 668 669 for (FieldDescriptor extension : extensions) { 670 extension.resolveAllFeatures(); 671 } 672 this.featuresResolved = true; 673 } 674 } 675 676 @Override inferLegacyProtoFeatures()677 FeatureSet inferLegacyProtoFeatures() { 678 FeatureSet.Builder features = FeatureSet.newBuilder(); 679 if (getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { 680 return features.build(); 681 } 682 683 if (getEdition() == Edition.EDITION_PROTO2) { 684 if (proto.getOptions().getJavaStringCheckUtf8()) { 685 features.setExtension( 686 JavaFeaturesProto.java_, 687 JavaFeatures.newBuilder() 688 .setUtf8Validation(JavaFeatures.Utf8Validation.VERIFY) 689 .build()); 690 } 691 } 692 return features.build(); 693 } 694 695 @Override hasInferredLegacyProtoFeatures()696 boolean hasInferredLegacyProtoFeatures() { 697 if (getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { 698 return false; 699 } 700 if (getEdition() == Edition.EDITION_PROTO2) { 701 if (proto.getOptions().getJavaStringCheckUtf8()) { 702 return true; 703 } 704 } 705 return false; 706 } 707 708 /** Look up and cross-link all field types, etc. */ crossLink()709 private void crossLink() throws DescriptorValidationException { 710 for (final Descriptor messageType : messageTypes) { 711 messageType.crossLink(); 712 } 713 714 for (final ServiceDescriptor service : services) { 715 service.crossLink(); 716 } 717 718 for (final FieldDescriptor extension : extensions) { 719 extension.crossLink(); 720 } 721 } 722 723 /** 724 * Replace our {@link FileDescriptorProto} with the given one, which is identical except that it 725 * might contain extensions that weren't present in the original. This method is needed for 726 * bootstrapping when a file defines custom options. The options may be defined in the file 727 * itself, so we can't actually parse them until we've constructed the descriptors, but to 728 * construct the descriptors we have to have parsed the descriptor protos. So, we have to parse 729 * the descriptor protos a second time after constructing the descriptors. 730 */ setProto(final FileDescriptorProto proto)731 private synchronized void setProto(final FileDescriptorProto proto) { 732 this.proto = proto; 733 this.options = null; 734 try { 735 resolveFeatures(proto.getOptions().getFeatures()); 736 737 for (int i = 0; i < messageTypes.length; i++) { 738 messageTypes[i].setProto(proto.getMessageType(i)); 739 } 740 741 for (int i = 0; i < enumTypes.length; i++) { 742 enumTypes[i].setProto(proto.getEnumType(i)); 743 } 744 745 for (int i = 0; i < services.length; i++) { 746 services[i].setProto(proto.getService(i)); 747 } 748 749 for (int i = 0; i < extensions.length; i++) { 750 extensions[i].setProto(proto.getExtension(i)); 751 } 752 } catch (DescriptorValidationException e) { 753 throw new IllegalArgumentException("Invalid features for \"" + proto.getName() + "\".", e); 754 } 755 } 756 } 757 758 // ================================================================= 759 760 /** Describes a message type. */ 761 public static final class Descriptor extends GenericDescriptor { 762 /** 763 * Get the index of this descriptor within its parent. In other words, given a {@link 764 * FileDescriptor} {@code file}, the following is true: 765 * 766 * <pre> 767 * for all i in [0, file.getMessageTypeCount()): 768 * file.getMessageType(i).getIndex() == i 769 * </pre> 770 * 771 * Similarly, for a {@link Descriptor} {@code messageType}: 772 * 773 * <pre> 774 * for all i in [0, messageType.getNestedTypeCount()): 775 * messageType.getNestedType(i).getIndex() == i 776 * </pre> 777 */ getIndex()778 public int getIndex() { 779 return index; 780 } 781 782 /** Convert the descriptor to its protocol message representation. */ 783 @Override toProto()784 public DescriptorProto toProto() { 785 return proto; 786 } 787 788 /** Get the type's unqualified name. */ 789 @Override getName()790 public String getName() { 791 return proto.getName(); 792 } 793 794 /** 795 * Get the type's fully-qualified name, within the proto language's namespace. This differs from 796 * the Java name. For example, given this {@code .proto}: 797 * 798 * <pre> 799 * package foo.bar; 800 * option java_package = "com.example.protos" 801 * message Baz {} 802 * </pre> 803 * 804 * {@code Baz}'s full name is "foo.bar.Baz". 805 */ 806 @Override getFullName()807 public String getFullName() { 808 return fullName; 809 } 810 811 /** Get the {@link FileDescriptor} containing this descriptor. */ 812 @Override getFile()813 public FileDescriptor getFile() { 814 return file; 815 } 816 817 /** If this is a nested type, get the outer descriptor, otherwise null. */ getContainingType()818 public Descriptor getContainingType() { 819 return containingType; 820 } 821 822 /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */ getOptions()823 public MessageOptions getOptions() { 824 if (this.options == null) { 825 MessageOptions strippedOptions = this.proto.getOptions(); 826 if (strippedOptions.hasFeatures()) { 827 // Clients should be using feature accessor methods, not accessing features on the 828 // options 829 // proto. 830 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 831 } 832 synchronized (this) { 833 if (this.options == null) { 834 this.options = strippedOptions; 835 } 836 } 837 } 838 return this.options; 839 } 840 841 /** Get a list of this message type's fields. */ getFields()842 public List<FieldDescriptor> getFields() { 843 return Collections.unmodifiableList(Arrays.asList(fields)); 844 } 845 846 /** Get a list of this message type's oneofs. */ getOneofs()847 public List<OneofDescriptor> getOneofs() { 848 return Collections.unmodifiableList(Arrays.asList(oneofs)); 849 } 850 851 /** Get a list of this message type's real oneofs. */ getRealOneofs()852 public List<OneofDescriptor> getRealOneofs() { 853 return Collections.unmodifiableList(Arrays.asList(oneofs).subList(0, realOneofCount)); 854 } 855 856 /** Get a list of this message type's extensions. */ getExtensions()857 public List<FieldDescriptor> getExtensions() { 858 return Collections.unmodifiableList(Arrays.asList(extensions)); 859 } 860 861 /** Get a list of message types nested within this one. */ getNestedTypes()862 public List<Descriptor> getNestedTypes() { 863 return Collections.unmodifiableList(Arrays.asList(nestedTypes)); 864 } 865 866 /** Get a list of enum types nested within this one. */ getEnumTypes()867 public List<EnumDescriptor> getEnumTypes() { 868 return Collections.unmodifiableList(Arrays.asList(enumTypes)); 869 } 870 871 /** Determines if the given field number is an extension. */ isExtensionNumber(final int number)872 public boolean isExtensionNumber(final int number) { 873 int index = Arrays.binarySearch(extensionRangeLowerBounds, number); 874 if (index < 0) { 875 index = ~index - 1; 876 } 877 // extensionRangeLowerBounds[index] is the biggest value <= number 878 return index >= 0 && number < extensionRangeUpperBounds[index]; 879 } 880 881 /** Determines if the given field number is reserved. */ isReservedNumber(final int number)882 public boolean isReservedNumber(final int number) { 883 for (final DescriptorProto.ReservedRange range : proto.getReservedRangeList()) { 884 if (range.getStart() <= number && number < range.getEnd()) { 885 return true; 886 } 887 } 888 return false; 889 } 890 891 /** Determines if the given field name is reserved. */ isReservedName(final String name)892 public boolean isReservedName(final String name) { 893 checkNotNull(name); 894 for (final String reservedName : proto.getReservedNameList()) { 895 if (reservedName.equals(name)) { 896 return true; 897 } 898 } 899 return false; 900 } 901 902 /** 903 * Indicates whether the message can be extended. That is, whether it has any "extensions x to 904 * y" ranges declared on it. 905 */ isExtendable()906 public boolean isExtendable() { 907 return !proto.getExtensionRangeList().isEmpty(); 908 } 909 910 /** 911 * Finds a field by name. 912 * 913 * @param name The unqualified name of the field (e.g. "foo"). For protocol buffer messages that 914 * follow <a 915 * href=https://developers.google.com/protocol-buffers/docs/style#message_and_field_names>Google's 916 * guidance on naming</a> this will be a snake case string, such as 917 * <pre>song_name</pre> 918 * . 919 * @return The field's descriptor, or {@code null} if not found. 920 */ findFieldByName(final String name)921 public FieldDescriptor findFieldByName(final String name) { 922 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 923 if (result instanceof FieldDescriptor) { 924 return (FieldDescriptor) result; 925 } else { 926 return null; 927 } 928 } 929 930 /** 931 * Finds a field by field number. 932 * 933 * @param number The field number within this message type. 934 * @return The field's descriptor, or {@code null} if not found. 935 */ findFieldByNumber(final int number)936 public FieldDescriptor findFieldByNumber(final int number) { 937 return binarySearch( 938 fieldsSortedByNumber, fieldsSortedByNumber.length, FieldDescriptor.NUMBER_GETTER, number); 939 } 940 941 /** 942 * Finds a nested message type by name. 943 * 944 * @param name The unqualified name of the nested type such as "Foo" 945 * @return The types's descriptor, or {@code null} if not found. 946 */ findNestedTypeByName(final String name)947 public Descriptor findNestedTypeByName(final String name) { 948 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 949 if (result instanceof Descriptor) { 950 return (Descriptor) result; 951 } else { 952 return null; 953 } 954 } 955 956 /** 957 * Finds a nested enum type by name. 958 * 959 * @param name The unqualified name of the nested type such as "Foo" 960 * @return The types's descriptor, or {@code null} if not found. 961 */ findEnumTypeByName(final String name)962 public EnumDescriptor findEnumTypeByName(final String name) { 963 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 964 if (result instanceof EnumDescriptor) { 965 return (EnumDescriptor) result; 966 } else { 967 return null; 968 } 969 } 970 971 private final int index; 972 private DescriptorProto proto; 973 private volatile MessageOptions options; 974 private final String fullName; 975 private final FileDescriptor file; 976 private final Descriptor containingType; 977 private final Descriptor[] nestedTypes; 978 private final EnumDescriptor[] enumTypes; 979 private final FieldDescriptor[] fields; 980 private final FieldDescriptor[] fieldsSortedByNumber; 981 private final FieldDescriptor[] extensions; 982 private final OneofDescriptor[] oneofs; 983 private final int realOneofCount; 984 985 private final int[] extensionRangeLowerBounds; 986 private final int[] extensionRangeUpperBounds; 987 988 // Used to create a placeholder when the type cannot be found. Descriptor(final String fullname)989 Descriptor(final String fullname) throws DescriptorValidationException { 990 String name = fullname; 991 String packageName = ""; 992 int pos = fullname.lastIndexOf('.'); 993 if (pos != -1) { 994 name = fullname.substring(pos + 1); 995 packageName = fullname.substring(0, pos); 996 } 997 this.index = 0; 998 this.proto = 999 DescriptorProto.newBuilder() 1000 .setName(name) 1001 .addExtensionRange( 1002 DescriptorProto.ExtensionRange.newBuilder().setStart(1).setEnd(536870912).build()) 1003 .build(); 1004 this.fullName = fullname; 1005 this.containingType = null; 1006 1007 this.nestedTypes = EMPTY_DESCRIPTORS; 1008 this.enumTypes = EMPTY_ENUM_DESCRIPTORS; 1009 this.fields = EMPTY_FIELD_DESCRIPTORS; 1010 this.fieldsSortedByNumber = EMPTY_FIELD_DESCRIPTORS; 1011 this.extensions = EMPTY_FIELD_DESCRIPTORS; 1012 this.oneofs = EMPTY_ONEOF_DESCRIPTORS; 1013 this.realOneofCount = 0; 1014 1015 // Create a placeholder FileDescriptor to hold this message. 1016 this.file = new FileDescriptor(packageName, this); 1017 this.parent = this.file; 1018 1019 extensionRangeLowerBounds = new int[] {1}; 1020 extensionRangeUpperBounds = new int[] {536870912}; 1021 } 1022 Descriptor( final DescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)1023 private Descriptor( 1024 final DescriptorProto proto, 1025 final FileDescriptor file, 1026 final Descriptor parent, 1027 final int index) 1028 throws DescriptorValidationException { 1029 if (parent == null) { 1030 this.parent = file; 1031 } else { 1032 this.parent = parent; 1033 } 1034 this.index = index; 1035 this.proto = proto; 1036 fullName = computeFullName(file, parent, proto.getName()); 1037 this.file = file; 1038 containingType = parent; 1039 1040 oneofs = 1041 (proto.getOneofDeclCount() > 0) 1042 ? new OneofDescriptor[proto.getOneofDeclCount()] 1043 : EMPTY_ONEOF_DESCRIPTORS; 1044 for (int i = 0; i < proto.getOneofDeclCount(); i++) { 1045 oneofs[i] = new OneofDescriptor(proto.getOneofDecl(i), file, this, i); 1046 } 1047 1048 nestedTypes = 1049 (proto.getNestedTypeCount() > 0) 1050 ? new Descriptor[proto.getNestedTypeCount()] 1051 : EMPTY_DESCRIPTORS; 1052 for (int i = 0; i < proto.getNestedTypeCount(); i++) { 1053 nestedTypes[i] = new Descriptor(proto.getNestedType(i), file, this, i); 1054 } 1055 1056 enumTypes = 1057 (proto.getEnumTypeCount() > 0) 1058 ? new EnumDescriptor[proto.getEnumTypeCount()] 1059 : EMPTY_ENUM_DESCRIPTORS; 1060 for (int i = 0; i < proto.getEnumTypeCount(); i++) { 1061 enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), file, this, i); 1062 } 1063 1064 fields = 1065 (proto.getFieldCount() > 0) 1066 ? new FieldDescriptor[proto.getFieldCount()] 1067 : EMPTY_FIELD_DESCRIPTORS; 1068 for (int i = 0; i < proto.getFieldCount(); i++) { 1069 fields[i] = new FieldDescriptor(proto.getField(i), file, this, i, false); 1070 } 1071 this.fieldsSortedByNumber = 1072 (proto.getFieldCount() > 0) ? fields.clone() : EMPTY_FIELD_DESCRIPTORS; 1073 1074 extensions = 1075 (proto.getExtensionCount() > 0) 1076 ? new FieldDescriptor[proto.getExtensionCount()] 1077 : EMPTY_FIELD_DESCRIPTORS; 1078 for (int i = 0; i < proto.getExtensionCount(); i++) { 1079 extensions[i] = new FieldDescriptor(proto.getExtension(i), file, this, i, true); 1080 } 1081 1082 for (int i = 0; i < proto.getOneofDeclCount(); i++) { 1083 oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()]; 1084 oneofs[i].fieldCount = 0; 1085 } 1086 for (int i = 0; i < proto.getFieldCount(); i++) { 1087 OneofDescriptor oneofDescriptor = fields[i].getContainingOneof(); 1088 if (oneofDescriptor != null) { 1089 oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i]; 1090 } 1091 } 1092 1093 int syntheticOneofCount = 0; 1094 for (OneofDescriptor oneof : this.oneofs) { 1095 if (oneof.isSynthetic()) { 1096 syntheticOneofCount++; 1097 } else { 1098 if (syntheticOneofCount > 0) { 1099 throw new DescriptorValidationException(this, "Synthetic oneofs must come last."); 1100 } 1101 } 1102 } 1103 this.realOneofCount = this.oneofs.length - syntheticOneofCount; 1104 1105 file.pool.addSymbol(this); 1106 1107 // NOTE: The defined extension ranges are guaranteed to be disjoint. 1108 if (proto.getExtensionRangeCount() > 0) { 1109 extensionRangeLowerBounds = new int[proto.getExtensionRangeCount()]; 1110 extensionRangeUpperBounds = new int[proto.getExtensionRangeCount()]; 1111 int i = 0; 1112 for (final DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) { 1113 extensionRangeLowerBounds[i] = range.getStart(); 1114 extensionRangeUpperBounds[i] = range.getEnd(); 1115 i++; 1116 } 1117 // Since the ranges are disjoint, sorting these independently must still produce the correct 1118 // order. 1119 Arrays.sort(extensionRangeLowerBounds); 1120 Arrays.sort(extensionRangeUpperBounds); 1121 } else { 1122 extensionRangeLowerBounds = EMPTY_INT_ARRAY; 1123 extensionRangeUpperBounds = EMPTY_INT_ARRAY; 1124 } 1125 } 1126 1127 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()1128 private void resolveAllFeatures() throws DescriptorValidationException { 1129 resolveFeatures(proto.getOptions().getFeatures()); 1130 1131 for (Descriptor nestedType : nestedTypes) { 1132 nestedType.resolveAllFeatures(); 1133 } 1134 1135 for (EnumDescriptor enumType : enumTypes) { 1136 enumType.resolveAllFeatures(); 1137 } 1138 1139 // Oneofs must be resolved before any children oneof fields. 1140 for (OneofDescriptor oneof : oneofs) { 1141 oneof.resolveAllFeatures(); 1142 } 1143 1144 for (FieldDescriptor field : fields) { 1145 field.resolveAllFeatures(); 1146 } 1147 1148 for (FieldDescriptor extension : extensions) { 1149 extension.resolveAllFeatures(); 1150 } 1151 } 1152 1153 /** Look up and cross-link all field types, etc. */ crossLink()1154 private void crossLink() throws DescriptorValidationException { 1155 for (final Descriptor nestedType : nestedTypes) { 1156 nestedType.crossLink(); 1157 } 1158 1159 for (final FieldDescriptor field : fields) { 1160 field.crossLink(); 1161 } 1162 Arrays.sort(fieldsSortedByNumber); 1163 validateNoDuplicateFieldNumbers(); 1164 1165 for (final FieldDescriptor extension : extensions) { 1166 extension.crossLink(); 1167 } 1168 } 1169 validateNoDuplicateFieldNumbers()1170 private void validateNoDuplicateFieldNumbers() throws DescriptorValidationException { 1171 for (int i = 0; i + 1 < fieldsSortedByNumber.length; i++) { 1172 FieldDescriptor old = fieldsSortedByNumber[i]; 1173 FieldDescriptor field = fieldsSortedByNumber[i + 1]; 1174 if (old.getNumber() == field.getNumber()) { 1175 throw new DescriptorValidationException( 1176 field, 1177 "Field number " 1178 + field.getNumber() 1179 + " has already been used in \"" 1180 + field.getContainingType().getFullName() 1181 + "\" by field \"" 1182 + old.getName() 1183 + "\"."); 1184 } 1185 } 1186 } 1187 1188 /** See {@link FileDescriptor#setProto}. */ setProto(final DescriptorProto proto)1189 private void setProto(final DescriptorProto proto) throws DescriptorValidationException { 1190 this.proto = proto; 1191 this.options = null; 1192 resolveFeatures(proto.getOptions().getFeatures()); 1193 1194 for (int i = 0; i < nestedTypes.length; i++) { 1195 nestedTypes[i].setProto(proto.getNestedType(i)); 1196 } 1197 1198 for (int i = 0; i < oneofs.length; i++) { 1199 oneofs[i].setProto(proto.getOneofDecl(i)); 1200 } 1201 1202 for (int i = 0; i < enumTypes.length; i++) { 1203 enumTypes[i].setProto(proto.getEnumType(i)); 1204 } 1205 1206 for (int i = 0; i < fields.length; i++) { 1207 fields[i].setProto(proto.getField(i)); 1208 } 1209 1210 for (int i = 0; i < extensions.length; i++) { 1211 extensions[i].setProto(proto.getExtension(i)); 1212 } 1213 } 1214 } 1215 1216 // ================================================================= 1217 1218 /** Describes a field of a message type. */ 1219 public static final class FieldDescriptor extends GenericDescriptor 1220 implements Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> { 1221 private static final NumberGetter<FieldDescriptor> NUMBER_GETTER = 1222 new NumberGetter<FieldDescriptor>() { 1223 @Override 1224 public int getNumber(FieldDescriptor fieldDescriptor) { 1225 return fieldDescriptor.getNumber(); 1226 } 1227 }; 1228 1229 /** 1230 * Get the index of this descriptor within its parent. 1231 * 1232 * @see Descriptors.Descriptor#getIndex() 1233 */ getIndex()1234 public int getIndex() { 1235 return index; 1236 } 1237 1238 /** Convert the descriptor to its protocol message representation. */ 1239 @Override toProto()1240 public FieldDescriptorProto toProto() { 1241 return proto; 1242 } 1243 1244 /** Get the field's unqualified name. */ 1245 @Override getName()1246 public String getName() { 1247 return proto.getName(); 1248 } 1249 1250 /** Get the field's number. */ 1251 @Override getNumber()1252 public int getNumber() { 1253 return proto.getNumber(); 1254 } 1255 1256 /** 1257 * Get the field's fully-qualified name. 1258 * 1259 * @see Descriptors.Descriptor#getFullName() 1260 */ 1261 @Override getFullName()1262 public String getFullName() { 1263 return fullName; 1264 } 1265 1266 /** Get the JSON name of this field. */ getJsonName()1267 public String getJsonName() { 1268 String result = jsonName; 1269 if (result != null) { 1270 return result; 1271 } else if (proto.hasJsonName()) { 1272 return jsonName = proto.getJsonName(); 1273 } else { 1274 return jsonName = fieldNameToJsonName(proto.getName()); 1275 } 1276 } 1277 1278 /** 1279 * Get the field's java type. This is just for convenience. Every {@code 1280 * FieldDescriptorProto.Type} maps to exactly one Java type. 1281 */ getJavaType()1282 public JavaType getJavaType() { 1283 return getType().getJavaType(); 1284 } 1285 1286 /** For internal use only. */ 1287 @Override getLiteJavaType()1288 public WireFormat.JavaType getLiteJavaType() { 1289 return getLiteType().getJavaType(); 1290 } 1291 1292 /** Get the {@code FileDescriptor} containing this descriptor. */ 1293 @Override getFile()1294 public FileDescriptor getFile() { 1295 return file; 1296 } 1297 1298 /** Get the field's declared type. */ getType()1299 public Type getType() { 1300 // Override delimited messages as legacy group type. Leaves unresolved messages as-is 1301 // since these are used before feature resolution when parsing java feature set defaults 1302 // (custom options) into unknown fields. 1303 if (type == Type.MESSAGE 1304 && !(messageType != null && messageType.toProto().getOptions().getMapEntry()) 1305 && !(containingType != null && containingType.toProto().getOptions().getMapEntry()) 1306 && this.features != null 1307 && getFeatures().getMessageEncoding() == FeatureSet.MessageEncoding.DELIMITED) { 1308 return Type.GROUP; 1309 } 1310 return type; 1311 } 1312 1313 /** For internal use only. */ 1314 @Override getLiteType()1315 public WireFormat.FieldType getLiteType() { 1316 return table[getType().ordinal()]; 1317 } 1318 1319 /** For internal use only. */ needsUtf8Check()1320 public boolean needsUtf8Check() { 1321 if (getType() != Type.STRING) { 1322 return false; 1323 } 1324 if (getContainingType().toProto().getOptions().getMapEntry()) { 1325 // Always enforce strict UTF-8 checking for map fields. 1326 return true; 1327 } 1328 if (getFeatures() 1329 .getExtension(JavaFeaturesProto.java_) 1330 .getUtf8Validation() 1331 .equals(JavaFeatures.Utf8Validation.VERIFY)) { 1332 return true; 1333 } 1334 return getFeatures().getUtf8Validation().equals(FeatureSet.Utf8Validation.VERIFY); 1335 } 1336 isMapField()1337 public boolean isMapField() { 1338 return getType() == Type.MESSAGE 1339 && isRepeated() 1340 && getMessageType().toProto().getOptions().getMapEntry(); 1341 } 1342 1343 // I'm pretty sure values() constructs a new array every time, since there 1344 // is nothing stopping the caller from mutating the array. Therefore we 1345 // make a static copy here. 1346 private static final WireFormat.FieldType[] table = WireFormat.FieldType.values(); 1347 1348 /** Is this field declared required? */ isRequired()1349 public boolean isRequired() { 1350 return getFeatures().getFieldPresence() 1351 == DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED; 1352 } 1353 1354 /** Is this field declared optional? */ isOptional()1355 public boolean isOptional() { 1356 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL 1357 && getFeatures().getFieldPresence() 1358 != DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED; 1359 } 1360 1361 /** Is this field declared repeated? */ 1362 @Override isRepeated()1363 public boolean isRepeated() { 1364 return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; 1365 } 1366 1367 /** 1368 * Does this field have the {@code [packed = true]} option or is this field packable in proto3 1369 * and not explicitly set to unpacked? 1370 */ 1371 @Override isPacked()1372 public boolean isPacked() { 1373 if (!isPackable()) { 1374 return false; 1375 } 1376 return getFeatures() 1377 .getRepeatedFieldEncoding() 1378 .equals(FeatureSet.RepeatedFieldEncoding.PACKED); 1379 } 1380 1381 /** Can this field be packed? That is, is it a repeated primitive field? */ isPackable()1382 public boolean isPackable() { 1383 return isRepeated() && getLiteType().isPackable(); 1384 } 1385 1386 /** Returns true if the field had an explicitly-defined default value. */ hasDefaultValue()1387 public boolean hasDefaultValue() { 1388 return proto.hasDefaultValue(); 1389 } 1390 1391 /** 1392 * Returns the field's default value. Valid for all types except for messages and groups. For 1393 * all other types, the object returned is of the same class that would returned by 1394 * Message.getField(this). 1395 */ getDefaultValue()1396 public Object getDefaultValue() { 1397 if (getJavaType() == JavaType.MESSAGE) { 1398 throw new UnsupportedOperationException( 1399 "FieldDescriptor.getDefaultValue() called on an embedded message field."); 1400 } 1401 return defaultValue; 1402 } 1403 1404 /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ getOptions()1405 public FieldOptions getOptions() { 1406 if (this.options == null) { 1407 FieldOptions strippedOptions = this.proto.getOptions(); 1408 if (strippedOptions.hasFeatures()) { 1409 // Clients should be using feature accessor methods, not accessing features on the 1410 // options 1411 // proto. 1412 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 1413 } 1414 synchronized (this) { 1415 if (this.options == null) { 1416 this.options = strippedOptions; 1417 } 1418 } 1419 } 1420 return this.options; 1421 } 1422 1423 /** Is this field an extension? */ isExtension()1424 public boolean isExtension() { 1425 return proto.hasExtendee(); 1426 } 1427 1428 /** 1429 * Get the field's containing type. For extensions, this is the type being extended, not the 1430 * location where the extension was defined. See {@link #getExtensionScope()}. 1431 */ getContainingType()1432 public Descriptor getContainingType() { 1433 return containingType; 1434 } 1435 1436 /** Get the field's containing oneof. */ getContainingOneof()1437 public OneofDescriptor getContainingOneof() { 1438 return containingOneof; 1439 } 1440 1441 /** Get the field's containing oneof, only if non-synthetic. */ getRealContainingOneof()1442 public OneofDescriptor getRealContainingOneof() { 1443 return containingOneof != null && !containingOneof.isSynthetic() ? containingOneof : null; 1444 } 1445 1446 /** 1447 * Returns true if this field was syntactically written with "optional" in the .proto file. 1448 * Excludes singular proto3 fields that do not have a label. 1449 */ hasOptionalKeyword()1450 boolean hasOptionalKeyword() { 1451 return isProto3Optional 1452 || (file.getEdition() == Edition.EDITION_PROTO2 1453 && isOptional() 1454 && getContainingOneof() == null); 1455 } 1456 1457 /** 1458 * Returns true if this field tracks presence, ie. does the field distinguish between "unset" 1459 * and "present with default value." 1460 * 1461 * <p>This includes required, optional, and oneof fields. It excludes maps, repeated fields, and 1462 * singular proto3 fields without "optional". 1463 * 1464 * <p>For fields where hasPresence() == true, the return value of msg.hasField() is semantically 1465 * meaningful. 1466 */ hasPresence()1467 public boolean hasPresence() { 1468 if (isRepeated()) { 1469 return false; 1470 } 1471 return isProto3Optional 1472 || getType() == Type.MESSAGE 1473 || getType() == Type.GROUP 1474 || isExtension() 1475 || getContainingOneof() != null 1476 || getFeatures().getFieldPresence() != DescriptorProtos.FeatureSet.FieldPresence.IMPLICIT; 1477 } 1478 1479 /** 1480 * Returns true if this field is structured like the synthetic field of a proto2 group. This 1481 * allows us to expand our treatment of delimited fields without breaking proto2 files that have 1482 * been upgraded to editions. 1483 */ isGroupLike()1484 boolean isGroupLike() { 1485 if (getType() != Type.GROUP) { 1486 // Groups are always tag-delimited. 1487 return false; 1488 } 1489 1490 if (!getMessageType().getName().toLowerCase().equals(getName())) { 1491 // Group fields always are always the lowercase type name. 1492 return false; 1493 } 1494 1495 if (getMessageType().getFile() != getFile()) { 1496 // Groups could only be defined in the same file they're used. 1497 return false; 1498 } 1499 1500 // Group messages are always defined in the same scope as the field. File level extensions 1501 // will compare NULL == NULL here, which is why the file comparison above is necessary to 1502 // ensure both come from the same file. 1503 return isExtension() 1504 ? getMessageType().getContainingType() == getExtensionScope() 1505 : getMessageType().getContainingType() == getContainingType(); 1506 } 1507 1508 /** 1509 * For extensions defined nested within message types, gets the outer type. Not valid for 1510 * non-extension fields. For example, consider this {@code .proto} file: 1511 * 1512 * <pre> 1513 * message Foo { 1514 * extensions 1000 to max; 1515 * } 1516 * extend Foo { 1517 * optional int32 baz = 1234; 1518 * } 1519 * message Bar { 1520 * extend Foo { 1521 * optional int32 moo = 4321; 1522 * } 1523 * } 1524 * </pre> 1525 * 1526 * Both {@code baz}'s and {@code moo}'s containing type is {@code Foo}. However, {@code baz}'s 1527 * extension scope is {@code null} while {@code moo}'s extension scope is {@code Bar}. 1528 */ getExtensionScope()1529 public Descriptor getExtensionScope() { 1530 if (!isExtension()) { 1531 throw new UnsupportedOperationException( 1532 String.format("This field is not an extension. (%s)", fullName)); 1533 } 1534 return extensionScope; 1535 } 1536 1537 /** For embedded message and group fields, gets the field's type. */ getMessageType()1538 public Descriptor getMessageType() { 1539 if (getJavaType() != JavaType.MESSAGE) { 1540 throw new UnsupportedOperationException( 1541 String.format("This field is not of message type. (%s)", fullName)); 1542 } 1543 return messageType; 1544 } 1545 1546 /** For enum fields, gets the field's type. */ 1547 @Override getEnumType()1548 public EnumDescriptor getEnumType() { 1549 if (getJavaType() != JavaType.ENUM) { 1550 throw new UnsupportedOperationException( 1551 String.format("This field is not of enum type. (%s)", fullName)); 1552 } 1553 return enumType; 1554 } 1555 1556 /** 1557 * Determines if the given enum field is treated as closed based on legacy non-conformant 1558 * behavior. 1559 * 1560 * <p>Conformant behavior determines closedness based on the enum and can be queried using 1561 * {@code EnumDescriptor.isClosed()}. 1562 * 1563 * <p>Some runtimes currently have a quirk where non-closed enums are treated as closed when 1564 * used as the type of fields defined in a `syntax = proto2;` file. This quirk is not present in 1565 * all runtimes; as of writing, we know that: 1566 * 1567 * <ul> 1568 * <li>C++, Java, and C++-based Python share this quirk. 1569 * <li>UPB and UPB-based Python do not. 1570 * <li>PHP and Ruby treat all enums as open regardless of declaration. 1571 * </ul> 1572 * 1573 * <p>Care should be taken when using this function to respect the target runtime's enum 1574 * handling quirks. 1575 */ legacyEnumFieldTreatedAsClosed()1576 public boolean legacyEnumFieldTreatedAsClosed() { 1577 // Don't check JavaFeaturesProto extension for files without dependencies. 1578 // This is especially important for descriptor.proto since getting the JavaFeaturesProto 1579 // extension itself involves calling legacyEnumFieldTreatedAsClosed() which would otherwise 1580 // infinite loop. 1581 if (getFile().getDependencies().isEmpty()) { 1582 return getType() == Type.ENUM && enumType.isClosed(); 1583 } 1584 1585 return getType() == Type.ENUM 1586 && (getFeatures().getExtension(JavaFeaturesProto.java_).getLegacyClosedEnum() 1587 || enumType.isClosed()); 1588 } 1589 1590 /** 1591 * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which 1592 * simply means ascending order by field number. {@code other} must be a field of the same type. 1593 * That is, {@code getContainingType()} must return the same {@code Descriptor} for both fields. 1594 * 1595 * @return negative, zero, or positive if {@code this} is less than, equal to, or greater than 1596 * {@code other}, respectively 1597 */ 1598 @Override compareTo(final FieldDescriptor other)1599 public int compareTo(final FieldDescriptor other) { 1600 if (other.containingType != containingType) { 1601 throw new IllegalArgumentException( 1602 "FieldDescriptors can only be compared to other FieldDescriptors " 1603 + "for fields of the same message type."); 1604 } 1605 return getNumber() - other.getNumber(); 1606 } 1607 1608 @Override toString()1609 public String toString() { 1610 return getFullName(); 1611 } 1612 1613 private final int index; 1614 1615 private FieldDescriptorProto proto; 1616 private volatile FieldOptions options; 1617 private final String fullName; 1618 private String jsonName; 1619 private final FileDescriptor file; 1620 private final Descriptor extensionScope; 1621 private final boolean isProto3Optional; 1622 1623 // Possibly initialized during cross-linking. 1624 private Type type; 1625 private Descriptor containingType; 1626 private Descriptor messageType; 1627 private OneofDescriptor containingOneof; 1628 private EnumDescriptor enumType; 1629 private Object defaultValue; 1630 1631 public enum Type { 1632 DOUBLE(JavaType.DOUBLE), 1633 FLOAT(JavaType.FLOAT), 1634 INT64(JavaType.LONG), 1635 UINT64(JavaType.LONG), 1636 INT32(JavaType.INT), 1637 FIXED64(JavaType.LONG), 1638 FIXED32(JavaType.INT), 1639 BOOL(JavaType.BOOLEAN), 1640 STRING(JavaType.STRING), 1641 GROUP(JavaType.MESSAGE), 1642 MESSAGE(JavaType.MESSAGE), 1643 BYTES(JavaType.BYTE_STRING), 1644 UINT32(JavaType.INT), 1645 ENUM(JavaType.ENUM), 1646 SFIXED32(JavaType.INT), 1647 SFIXED64(JavaType.LONG), 1648 SINT32(JavaType.INT), 1649 SINT64(JavaType.LONG); 1650 1651 // Private copy to avoid repeated allocations from calls to values() in valueOf(). 1652 private static final Type[] types = values(); 1653 Type(JavaType javaType)1654 Type(JavaType javaType) { 1655 this.javaType = javaType; 1656 } 1657 1658 private final JavaType javaType; 1659 toProto()1660 public FieldDescriptorProto.Type toProto() { 1661 return FieldDescriptorProto.Type.forNumber(ordinal() + 1); 1662 } 1663 getJavaType()1664 public JavaType getJavaType() { 1665 return javaType; 1666 } 1667 valueOf(final FieldDescriptorProto.Type type)1668 public static Type valueOf(final FieldDescriptorProto.Type type) { 1669 return types[type.getNumber() - 1]; 1670 } 1671 } 1672 1673 static { 1674 // Refuse to init if someone added a new declared type. 1675 if (Type.types.length != FieldDescriptorProto.Type.values().length) { 1676 throw new RuntimeException( 1677 "descriptor.proto has a new declared type but Descriptors.java wasn't updated."); 1678 } 1679 } 1680 1681 public enum JavaType { 1682 INT(0), 1683 LONG(0L), 1684 FLOAT(0F), 1685 DOUBLE(0D), 1686 BOOLEAN(false), 1687 STRING(""), 1688 BYTE_STRING(ByteString.EMPTY), 1689 ENUM(null), 1690 MESSAGE(null); 1691 JavaType(final Object defaultDefault)1692 JavaType(final Object defaultDefault) { 1693 this.defaultDefault = defaultDefault; 1694 } 1695 1696 /** 1697 * The default default value for fields of this type, if it's a primitive type. This is meant 1698 * for use inside this file only, hence is private. 1699 */ 1700 private final Object defaultDefault; 1701 } 1702 1703 // This method should match exactly with the ToJsonName() function in C++ 1704 // descriptor.cc. fieldNameToJsonName(String name)1705 private static String fieldNameToJsonName(String name) { 1706 final int length = name.length(); 1707 StringBuilder result = new StringBuilder(length); 1708 boolean isNextUpperCase = false; 1709 for (int i = 0; i < length; i++) { 1710 char ch = name.charAt(i); 1711 if (ch == '_') { 1712 isNextUpperCase = true; 1713 } else if (isNextUpperCase) { 1714 // This closely matches the logic for ASCII characters in: 1715 // http://google3/google/protobuf/descriptor.cc?l=249-251&rcl=228891689 1716 if ('a' <= ch && ch <= 'z') { 1717 ch = (char) (ch - 'a' + 'A'); 1718 } 1719 result.append(ch); 1720 isNextUpperCase = false; 1721 } else { 1722 result.append(ch); 1723 } 1724 } 1725 return result.toString(); 1726 } 1727 FieldDescriptor( final FieldDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index, final boolean isExtension)1728 private FieldDescriptor( 1729 final FieldDescriptorProto proto, 1730 final FileDescriptor file, 1731 final Descriptor parent, 1732 final int index, 1733 final boolean isExtension) 1734 throws DescriptorValidationException { 1735 this.parent = parent; 1736 this.index = index; 1737 this.proto = proto; 1738 fullName = computeFullName(file, parent, proto.getName()); 1739 this.file = file; 1740 1741 if (proto.hasType()) { 1742 type = Type.valueOf(proto.getType()); 1743 } 1744 1745 isProto3Optional = proto.getProto3Optional(); 1746 1747 if (getNumber() <= 0) { 1748 throw new DescriptorValidationException(this, "Field numbers must be positive integers."); 1749 } 1750 1751 if (isExtension) { 1752 if (!proto.hasExtendee()) { 1753 throw new DescriptorValidationException( 1754 this, "FieldDescriptorProto.extendee not set for extension field."); 1755 } 1756 containingType = null; // Will be filled in when cross-linking 1757 if (parent != null) { 1758 extensionScope = parent; 1759 } else { 1760 extensionScope = null; 1761 this.parent = file; 1762 } 1763 1764 if (proto.hasOneofIndex()) { 1765 throw new DescriptorValidationException( 1766 this, "FieldDescriptorProto.oneof_index set for extension field."); 1767 } 1768 containingOneof = null; 1769 } else { 1770 if (proto.hasExtendee()) { 1771 throw new DescriptorValidationException( 1772 this, "FieldDescriptorProto.extendee set for non-extension field."); 1773 } 1774 containingType = parent; 1775 1776 if (proto.hasOneofIndex()) { 1777 if (proto.getOneofIndex() < 0 1778 || proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) { 1779 throw new DescriptorValidationException( 1780 this, 1781 "FieldDescriptorProto.oneof_index is out of range for type " + parent.getName()); 1782 } 1783 containingOneof = parent.getOneofs().get(proto.getOneofIndex()); 1784 containingOneof.fieldCount++; 1785 this.parent = containingOneof; 1786 } else { 1787 containingOneof = null; 1788 } 1789 extensionScope = null; 1790 } 1791 1792 file.pool.addSymbol(this); 1793 } 1794 1795 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()1796 private void resolveAllFeatures() throws DescriptorValidationException { 1797 resolveFeatures(proto.getOptions().getFeatures()); 1798 } 1799 1800 @Override inferLegacyProtoFeatures()1801 FeatureSet inferLegacyProtoFeatures() { 1802 FeatureSet.Builder features = FeatureSet.newBuilder(); 1803 if (getFile().getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { 1804 return features.build(); 1805 } 1806 1807 if (proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED) { 1808 features.setFieldPresence(FeatureSet.FieldPresence.LEGACY_REQUIRED); 1809 } 1810 1811 if (proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP) { 1812 features.setMessageEncoding(FeatureSet.MessageEncoding.DELIMITED); 1813 } 1814 1815 if (getFile().getEdition() == Edition.EDITION_PROTO2 && proto.getOptions().getPacked()) { 1816 features.setRepeatedFieldEncoding(FeatureSet.RepeatedFieldEncoding.PACKED); 1817 } 1818 1819 if (getFile().getEdition() == Edition.EDITION_PROTO3) { 1820 if (proto.getOptions().hasPacked() && !proto.getOptions().getPacked()) { 1821 features.setRepeatedFieldEncoding(FeatureSet.RepeatedFieldEncoding.EXPANDED); 1822 } 1823 1824 } 1825 return features.build(); 1826 } 1827 1828 @Override hasInferredLegacyProtoFeatures()1829 boolean hasInferredLegacyProtoFeatures() { 1830 if (getFile().getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { 1831 return false; 1832 } 1833 1834 if (proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED) { 1835 return true; 1836 } 1837 1838 if (proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP) { 1839 return true; 1840 } 1841 1842 if (proto.getOptions().getPacked()) { 1843 return true; 1844 } 1845 1846 if (getFile().getEdition() == Edition.EDITION_PROTO3) { 1847 if (proto.getOptions().hasPacked() && !proto.getOptions().getPacked()) { 1848 return true; 1849 } 1850 1851 } 1852 return false; 1853 } 1854 1855 @Override validateFeatures()1856 void validateFeatures() throws DescriptorValidationException { 1857 if (containingType != null 1858 && containingType.toProto().getOptions().getMessageSetWireFormat()) { 1859 if (isExtension()) { 1860 if (!isOptional() || getType() != Type.MESSAGE) { 1861 throw new DescriptorValidationException( 1862 this, "Extensions of MessageSets must be optional messages."); 1863 } 1864 } 1865 } 1866 } 1867 1868 /** Look up and cross-link all field types, etc. */ crossLink()1869 private void crossLink() throws DescriptorValidationException { 1870 if (proto.hasExtendee()) { 1871 final GenericDescriptor extendee = 1872 file.pool.lookupSymbol( 1873 proto.getExtendee(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 1874 if (!(extendee instanceof Descriptor)) { 1875 throw new DescriptorValidationException( 1876 this, '\"' + proto.getExtendee() + "\" is not a message type."); 1877 } 1878 containingType = (Descriptor) extendee; 1879 1880 if (!getContainingType().isExtensionNumber(getNumber())) { 1881 throw new DescriptorValidationException( 1882 this, 1883 '\"' 1884 + getContainingType().getFullName() 1885 + "\" does not declare " 1886 + getNumber() 1887 + " as an extension number."); 1888 } 1889 } 1890 1891 if (proto.hasTypeName()) { 1892 final GenericDescriptor typeDescriptor = 1893 file.pool.lookupSymbol( 1894 proto.getTypeName(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 1895 1896 if (!proto.hasType()) { 1897 // Choose field type based on symbol. 1898 if (typeDescriptor instanceof Descriptor) { 1899 type = Type.MESSAGE; 1900 } else if (typeDescriptor instanceof EnumDescriptor) { 1901 type = Type.ENUM; 1902 } else { 1903 throw new DescriptorValidationException( 1904 this, '\"' + proto.getTypeName() + "\" is not a type."); 1905 } 1906 } 1907 1908 // Use raw type since inferred type considers messageType which may not be fully cross 1909 // linked yet. 1910 if (type.getJavaType() == JavaType.MESSAGE) { 1911 if (!(typeDescriptor instanceof Descriptor)) { 1912 throw new DescriptorValidationException( 1913 this, '\"' + proto.getTypeName() + "\" is not a message type."); 1914 } 1915 messageType = (Descriptor) typeDescriptor; 1916 1917 if (proto.hasDefaultValue()) { 1918 throw new DescriptorValidationException(this, "Messages can't have default values."); 1919 } 1920 } else if (type.getJavaType() == JavaType.ENUM) { 1921 if (!(typeDescriptor instanceof EnumDescriptor)) { 1922 throw new DescriptorValidationException( 1923 this, '\"' + proto.getTypeName() + "\" is not an enum type."); 1924 } 1925 enumType = (EnumDescriptor) typeDescriptor; 1926 } else { 1927 throw new DescriptorValidationException(this, "Field with primitive type has type_name."); 1928 } 1929 } else { 1930 if (type.getJavaType() == JavaType.MESSAGE || type.getJavaType() == JavaType.ENUM) { 1931 throw new DescriptorValidationException( 1932 this, "Field with message or enum type missing type_name."); 1933 } 1934 } 1935 1936 // Only repeated primitive fields may be packed. 1937 if (proto.getOptions().getPacked() && !isPackable()) { 1938 throw new DescriptorValidationException( 1939 this, "[packed = true] can only be specified for repeated primitive fields."); 1940 } 1941 1942 // We don't attempt to parse the default value until here because for 1943 // enums we need the enum type's descriptor. 1944 if (proto.hasDefaultValue()) { 1945 if (isRepeated()) { 1946 throw new DescriptorValidationException( 1947 this, "Repeated fields cannot have default values."); 1948 } 1949 1950 try { 1951 switch (type) { 1952 case INT32: 1953 case SINT32: 1954 case SFIXED32: 1955 defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); 1956 break; 1957 case UINT32: 1958 case FIXED32: 1959 defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); 1960 break; 1961 case INT64: 1962 case SINT64: 1963 case SFIXED64: 1964 defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); 1965 break; 1966 case UINT64: 1967 case FIXED64: 1968 defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); 1969 break; 1970 case FLOAT: 1971 if (proto.getDefaultValue().equals("inf")) { 1972 defaultValue = Float.POSITIVE_INFINITY; 1973 } else if (proto.getDefaultValue().equals("-inf")) { 1974 defaultValue = Float.NEGATIVE_INFINITY; 1975 } else if (proto.getDefaultValue().equals("nan")) { 1976 defaultValue = Float.NaN; 1977 } else { 1978 defaultValue = Float.valueOf(proto.getDefaultValue()); 1979 } 1980 break; 1981 case DOUBLE: 1982 if (proto.getDefaultValue().equals("inf")) { 1983 defaultValue = Double.POSITIVE_INFINITY; 1984 } else if (proto.getDefaultValue().equals("-inf")) { 1985 defaultValue = Double.NEGATIVE_INFINITY; 1986 } else if (proto.getDefaultValue().equals("nan")) { 1987 defaultValue = Double.NaN; 1988 } else { 1989 defaultValue = Double.valueOf(proto.getDefaultValue()); 1990 } 1991 break; 1992 case BOOL: 1993 defaultValue = Boolean.valueOf(proto.getDefaultValue()); 1994 break; 1995 case STRING: 1996 defaultValue = proto.getDefaultValue(); 1997 break; 1998 case BYTES: 1999 try { 2000 defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue()); 2001 } catch (TextFormat.InvalidEscapeSequenceException e) { 2002 throw new DescriptorValidationException( 2003 this, "Couldn't parse default value: " + e.getMessage(), e); 2004 } 2005 break; 2006 case ENUM: 2007 defaultValue = enumType.findValueByName(proto.getDefaultValue()); 2008 if (defaultValue == null) { 2009 throw new DescriptorValidationException( 2010 this, "Unknown enum default value: \"" + proto.getDefaultValue() + '\"'); 2011 } 2012 break; 2013 case MESSAGE: 2014 case GROUP: 2015 throw new DescriptorValidationException(this, "Message type had default value."); 2016 } 2017 } catch (NumberFormatException e) { 2018 throw new DescriptorValidationException( 2019 this, "Could not parse default value: \"" + proto.getDefaultValue() + '\"', e); 2020 } 2021 } else { 2022 // Determine the default default for this field. 2023 if (isRepeated()) { 2024 defaultValue = Collections.emptyList(); 2025 } else { 2026 switch (type.getJavaType()) { 2027 case ENUM: 2028 // We guarantee elsewhere that an enum type always has at least 2029 // one possible value. 2030 defaultValue = enumType.getValues().get(0); 2031 break; 2032 case MESSAGE: 2033 defaultValue = null; 2034 break; 2035 default: 2036 defaultValue = type.getJavaType().defaultDefault; 2037 break; 2038 } 2039 } 2040 } 2041 } 2042 2043 /** See {@link FileDescriptor#setProto}. */ setProto(final FieldDescriptorProto proto)2044 private void setProto(final FieldDescriptorProto proto) throws DescriptorValidationException { 2045 this.proto = proto; 2046 this.options = null; 2047 resolveFeatures(proto.getOptions().getFeatures()); 2048 } 2049 2050 /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */ 2051 @Override internalMergeFrom(MessageLite.Builder to, MessageLite from)2052 public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) { 2053 // FieldDescriptors are only used with non-lite messages so we can just 2054 // down-cast and call mergeFrom directly. 2055 return ((Message.Builder) to).mergeFrom((Message) from); 2056 } 2057 } 2058 2059 // ================================================================= 2060 2061 /** Describes an enum type. */ 2062 public static final class EnumDescriptor extends GenericDescriptor 2063 implements Internal.EnumLiteMap<EnumValueDescriptor> { 2064 /** 2065 * Get the index of this descriptor within its parent. 2066 * 2067 * @see Descriptors.Descriptor#getIndex() 2068 */ getIndex()2069 public int getIndex() { 2070 return index; 2071 } 2072 2073 /** Convert the descriptor to its protocol message representation. */ 2074 @Override toProto()2075 public EnumDescriptorProto toProto() { 2076 return proto; 2077 } 2078 2079 /** Get the type's unqualified name. */ 2080 @Override getName()2081 public String getName() { 2082 return proto.getName(); 2083 } 2084 2085 /** 2086 * Get the type's fully-qualified name. 2087 * 2088 * @see Descriptors.Descriptor#getFullName() 2089 */ 2090 @Override getFullName()2091 public String getFullName() { 2092 return fullName; 2093 } 2094 2095 /** Get the {@link FileDescriptor} containing this descriptor. */ 2096 @Override getFile()2097 public FileDescriptor getFile() { 2098 return file; 2099 } 2100 2101 /** 2102 * Determines if the given enum is closed. 2103 * 2104 * <p>Closed enum means that it: 2105 * 2106 * <ul> 2107 * <li>Has a fixed set of values, rather than being equivalent to an int32. 2108 * <li>Encountering values not in this set causes them to be treated as unknown fields. 2109 * <li>The first value (i.e., the default) may be nonzero. 2110 * </ul> 2111 * 2112 * <p>WARNING: Some runtimes currently have a quirk where non-closed enums are treated as closed 2113 * when used as the type of fields defined in a `syntax = proto2;` file. This quirk is not 2114 * present in all runtimes; as of writing, we know that: 2115 * 2116 * <ul> 2117 * <li> C++, Java, and C++-based Python share this quirk. 2118 * <li> UPB and UPB-based Python do not. 2119 * <li> PHP and Ruby treat all enums as open regardless of declaration. 2120 * </ul> 2121 * 2122 * <p>Care should be taken when using this function to respect the target runtime's enum 2123 * handling quirks. 2124 */ isClosed()2125 public boolean isClosed() { 2126 return getFeatures().getEnumType() == DescriptorProtos.FeatureSet.EnumType.CLOSED; 2127 } 2128 2129 /** If this is a nested type, get the outer descriptor, otherwise null. */ getContainingType()2130 public Descriptor getContainingType() { 2131 return containingType; 2132 } 2133 2134 /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ getOptions()2135 public EnumOptions getOptions() { 2136 if (this.options == null) { 2137 EnumOptions strippedOptions = this.proto.getOptions(); 2138 if (strippedOptions.hasFeatures()) { 2139 // Clients should be using feature accessor methods, not accessing features on the 2140 // options 2141 // proto. 2142 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 2143 } 2144 synchronized (this) { 2145 if (this.options == null) { 2146 this.options = strippedOptions; 2147 } 2148 } 2149 } 2150 return this.options; 2151 } 2152 2153 /** Get a list of defined values for this enum. */ getValues()2154 public List<EnumValueDescriptor> getValues() { 2155 return Collections.unmodifiableList(Arrays.asList(values)); 2156 } 2157 2158 /** Determines if the given field number is reserved. */ isReservedNumber(final int number)2159 public boolean isReservedNumber(final int number) { 2160 for (final EnumDescriptorProto.EnumReservedRange range : proto.getReservedRangeList()) { 2161 if (range.getStart() <= number && number <= range.getEnd()) { 2162 return true; 2163 } 2164 } 2165 return false; 2166 } 2167 2168 /** Determines if the given field name is reserved. */ isReservedName(final String name)2169 public boolean isReservedName(final String name) { 2170 checkNotNull(name); 2171 for (final String reservedName : proto.getReservedNameList()) { 2172 if (reservedName.equals(name)) { 2173 return true; 2174 } 2175 } 2176 return false; 2177 } 2178 2179 /** 2180 * Find an enum value by name. 2181 * 2182 * @param name the unqualified name of the value such as "FOO" 2183 * @return the value's descriptor, or {@code null} if not found 2184 */ findValueByName(final String name)2185 public EnumValueDescriptor findValueByName(final String name) { 2186 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 2187 if (result instanceof EnumValueDescriptor) { 2188 return (EnumValueDescriptor) result; 2189 } else { 2190 return null; 2191 } 2192 } 2193 2194 /** 2195 * Find an enum value by number. If multiple enum values have the same number, this returns the 2196 * first defined value with that number. 2197 * 2198 * @param number The value's number. 2199 * @return the value's descriptor, or {@code null} if not found. 2200 */ 2201 @Override findValueByNumber(final int number)2202 public EnumValueDescriptor findValueByNumber(final int number) { 2203 return binarySearch( 2204 valuesSortedByNumber, distinctNumbers, EnumValueDescriptor.NUMBER_GETTER, number); 2205 } 2206 2207 private static class UnknownEnumValueReference extends WeakReference<EnumValueDescriptor> { 2208 private final int number; 2209 UnknownEnumValueReference(int number, EnumValueDescriptor descriptor)2210 private UnknownEnumValueReference(int number, EnumValueDescriptor descriptor) { 2211 super(descriptor); 2212 this.number = number; 2213 } 2214 } 2215 2216 /** 2217 * Get the enum value for a number. If no enum value has this number, construct an 2218 * EnumValueDescriptor for it. 2219 */ findValueByNumberCreatingIfUnknown(final int number)2220 public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) { 2221 EnumValueDescriptor result = findValueByNumber(number); 2222 if (result != null) { 2223 return result; 2224 } 2225 // The number represents an unknown enum value. 2226 synchronized (this) { 2227 if (cleanupQueue == null) { 2228 cleanupQueue = new ReferenceQueue<>(); 2229 unknownValues = new HashMap<>(); 2230 } else { 2231 while (true) { 2232 UnknownEnumValueReference toClean = (UnknownEnumValueReference) cleanupQueue.poll(); 2233 if (toClean == null) { 2234 break; 2235 } 2236 unknownValues.remove(toClean.number); 2237 } 2238 } 2239 2240 // There are two ways we can be missing a value: it wasn't in the map, or the reference 2241 // has been GC'd. (It may even have been GC'd since we cleaned up the references a few 2242 // lines of code ago.) So get out the reference, if it's still present... 2243 WeakReference<EnumValueDescriptor> reference = unknownValues.get(number); 2244 result = (reference == null) ? null : reference.get(); 2245 2246 if (result == null) { 2247 result = new EnumValueDescriptor(this, number); 2248 unknownValues.put(number, new UnknownEnumValueReference(number, result)); 2249 } 2250 } 2251 return result; 2252 } 2253 2254 // Used in tests only. getUnknownEnumValueDescriptorCount()2255 int getUnknownEnumValueDescriptorCount() { 2256 return unknownValues.size(); 2257 } 2258 2259 private final int index; 2260 private EnumDescriptorProto proto; 2261 private volatile EnumOptions options; 2262 private final String fullName; 2263 private final FileDescriptor file; 2264 private final Descriptor containingType; 2265 private final EnumValueDescriptor[] values; 2266 private final EnumValueDescriptor[] valuesSortedByNumber; 2267 private final int distinctNumbers; 2268 private Map<Integer, WeakReference<EnumValueDescriptor>> unknownValues = null; 2269 private ReferenceQueue<EnumValueDescriptor> cleanupQueue = null; 2270 EnumDescriptor( final EnumDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)2271 private EnumDescriptor( 2272 final EnumDescriptorProto proto, 2273 final FileDescriptor file, 2274 final Descriptor parent, 2275 final int index) 2276 throws DescriptorValidationException { 2277 if (parent == null) { 2278 this.parent = file; 2279 } else { 2280 this.parent = parent; 2281 } 2282 this.index = index; 2283 this.proto = proto; 2284 fullName = computeFullName(file, parent, proto.getName()); 2285 this.file = file; 2286 containingType = parent; 2287 2288 if (proto.getValueCount() == 0) { 2289 // We cannot allow enums with no values because this would mean there 2290 // would be no valid default value for fields of this type. 2291 throw new DescriptorValidationException(this, "Enums must contain at least one value."); 2292 } 2293 2294 values = new EnumValueDescriptor[proto.getValueCount()]; 2295 for (int i = 0; i < proto.getValueCount(); i++) { 2296 values[i] = new EnumValueDescriptor(proto.getValue(i), file, this, i); 2297 } 2298 valuesSortedByNumber = values.clone(); 2299 Arrays.sort(valuesSortedByNumber, EnumValueDescriptor.BY_NUMBER); 2300 // deduplicate 2301 int j = 0; 2302 for (int i = 1; i < proto.getValueCount(); i++) { 2303 EnumValueDescriptor oldValue = valuesSortedByNumber[j]; 2304 EnumValueDescriptor newValue = valuesSortedByNumber[i]; 2305 if (oldValue.getNumber() != newValue.getNumber()) { 2306 valuesSortedByNumber[++j] = newValue; 2307 } 2308 } 2309 this.distinctNumbers = j + 1; 2310 Arrays.fill(valuesSortedByNumber, distinctNumbers, proto.getValueCount(), null); 2311 2312 file.pool.addSymbol(this); 2313 } 2314 2315 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()2316 private void resolveAllFeatures() throws DescriptorValidationException { 2317 resolveFeatures(proto.getOptions().getFeatures()); 2318 2319 for (EnumValueDescriptor value : values) { 2320 value.resolveAllFeatures(); 2321 } 2322 } 2323 2324 /** See {@link FileDescriptor#setProto}. */ setProto(final EnumDescriptorProto proto)2325 private void setProto(final EnumDescriptorProto proto) throws DescriptorValidationException { 2326 this.proto = proto; 2327 this.options = null; 2328 resolveFeatures(proto.getOptions().getFeatures()); 2329 2330 for (int i = 0; i < values.length; i++) { 2331 values[i].setProto(proto.getValue(i)); 2332 } 2333 } 2334 } 2335 2336 // ================================================================= 2337 2338 /** 2339 * Describes one value within an enum type. Note that multiple defined values may have the same 2340 * number. In generated Java code, all values with the same number after the first become aliases 2341 * of the first. However, they still have independent EnumValueDescriptors. 2342 */ 2343 @SuppressWarnings("ShouldNotSubclass") 2344 public static final class EnumValueDescriptor extends GenericDescriptor 2345 implements Internal.EnumLite { 2346 static final Comparator<EnumValueDescriptor> BY_NUMBER = 2347 new Comparator<EnumValueDescriptor>() { 2348 @Override 2349 public int compare(EnumValueDescriptor o1, EnumValueDescriptor o2) { 2350 return Integer.compare(o1.getNumber(), o2.getNumber()); 2351 } 2352 }; 2353 2354 static final NumberGetter<EnumValueDescriptor> NUMBER_GETTER = 2355 new NumberGetter<EnumValueDescriptor>() { 2356 @Override 2357 public int getNumber(EnumValueDescriptor enumValueDescriptor) { 2358 return enumValueDescriptor.getNumber(); 2359 } 2360 }; 2361 2362 /** 2363 * Get the index of this descriptor within its parent. 2364 * 2365 * @see Descriptors.Descriptor#getIndex() 2366 */ getIndex()2367 public int getIndex() { 2368 return index; 2369 } 2370 2371 /** Convert the descriptor to its protocol message representation. */ 2372 @Override toProto()2373 public EnumValueDescriptorProto toProto() { 2374 return proto; 2375 } 2376 2377 /** Get the value's unqualified name. */ 2378 @Override getName()2379 public String getName() { 2380 return proto.getName(); 2381 } 2382 2383 /** Get the value's number. */ 2384 @Override getNumber()2385 public int getNumber() { 2386 return proto.getNumber(); 2387 } 2388 2389 @Override toString()2390 public String toString() { 2391 return proto.getName(); 2392 } 2393 2394 /** 2395 * Get the value's fully-qualified name. 2396 * 2397 * @see Descriptors.Descriptor#getFullName() 2398 */ 2399 @Override getFullName()2400 public String getFullName() { 2401 return fullName; 2402 } 2403 2404 /** Get the {@link FileDescriptor} containing this descriptor. */ 2405 @Override getFile()2406 public FileDescriptor getFile() { 2407 return type.file; 2408 } 2409 2410 /** Get the value's enum type. */ getType()2411 public EnumDescriptor getType() { 2412 return type; 2413 } 2414 2415 /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */ getOptions()2416 public EnumValueOptions getOptions() { 2417 if (this.options == null) { 2418 EnumValueOptions strippedOptions = this.proto.getOptions(); 2419 if (strippedOptions.hasFeatures()) { 2420 // Clients should be using feature accessor methods, not accessing features on the 2421 // options 2422 // proto. 2423 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 2424 } 2425 synchronized (this) { 2426 if (this.options == null) { 2427 this.options = strippedOptions; 2428 } 2429 } 2430 } 2431 return this.options; 2432 } 2433 2434 private final int index; 2435 private EnumValueDescriptorProto proto; 2436 private volatile EnumValueOptions options; 2437 private final String fullName; 2438 private final EnumDescriptor type; 2439 EnumValueDescriptor( final EnumValueDescriptorProto proto, final FileDescriptor file, final EnumDescriptor parent, final int index)2440 private EnumValueDescriptor( 2441 final EnumValueDescriptorProto proto, 2442 final FileDescriptor file, 2443 final EnumDescriptor parent, 2444 final int index) 2445 throws DescriptorValidationException { 2446 this.parent = parent; 2447 this.index = index; 2448 this.proto = proto; 2449 this.type = parent; 2450 this.fullName = parent.getFullName() + '.' + proto.getName(); 2451 file.pool.addSymbol(this); 2452 } 2453 2454 // Create an unknown enum value. EnumValueDescriptor(final EnumDescriptor parent, final Integer number)2455 private EnumValueDescriptor(final EnumDescriptor parent, final Integer number) { 2456 String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number; 2457 EnumValueDescriptorProto proto = 2458 EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build(); 2459 this.parent = parent; 2460 this.index = -1; 2461 this.proto = proto; 2462 this.type = parent; 2463 this.fullName = parent.getFullName() + '.' + proto.getName(); 2464 2465 // Don't add this descriptor into pool. 2466 } 2467 2468 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()2469 private void resolveAllFeatures() throws DescriptorValidationException { 2470 resolveFeatures(proto.getOptions().getFeatures()); 2471 } 2472 2473 /** See {@link FileDescriptor#setProto}. */ setProto(final EnumValueDescriptorProto proto)2474 private void setProto(final EnumValueDescriptorProto proto) 2475 throws DescriptorValidationException { 2476 this.proto = proto; 2477 this.options = null; 2478 resolveFeatures(proto.getOptions().getFeatures()); 2479 } 2480 } 2481 2482 // ================================================================= 2483 2484 /** Describes a service type. */ 2485 public static final class ServiceDescriptor extends GenericDescriptor { 2486 /** 2487 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex() 2488 */ getIndex()2489 public int getIndex() { 2490 return index; 2491 } 2492 2493 /** Convert the descriptor to its protocol message representation. */ 2494 @Override toProto()2495 public ServiceDescriptorProto toProto() { 2496 return proto; 2497 } 2498 2499 /** Get the type's unqualified name. */ 2500 @Override getName()2501 public String getName() { 2502 return proto.getName(); 2503 } 2504 2505 /** 2506 * Get the type's fully-qualified name. 2507 * 2508 * @see Descriptors.Descriptor#getFullName() 2509 */ 2510 @Override getFullName()2511 public String getFullName() { 2512 return fullName; 2513 } 2514 2515 /** Get the {@link FileDescriptor} containing this descriptor. */ 2516 @Override getFile()2517 public FileDescriptor getFile() { 2518 return file; 2519 } 2520 2521 /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ getOptions()2522 public ServiceOptions getOptions() { 2523 if (this.options == null) { 2524 ServiceOptions strippedOptions = this.proto.getOptions(); 2525 if (strippedOptions.hasFeatures()) { 2526 // Clients should be using feature accessor methods, not accessing features on the 2527 // options 2528 // proto. 2529 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 2530 } 2531 synchronized (this) { 2532 if (this.options == null) { 2533 this.options = strippedOptions; 2534 } 2535 } 2536 } 2537 return this.options; 2538 } 2539 2540 /** Get a list of methods for this service. */ getMethods()2541 public List<MethodDescriptor> getMethods() { 2542 return Collections.unmodifiableList(Arrays.asList(methods)); 2543 } 2544 2545 /** 2546 * Find a method by name. 2547 * 2548 * @param name the unqualified name of the method such as "Foo" 2549 * @return the method's descriptor, or {@code null} if not found 2550 */ findMethodByName(final String name)2551 public MethodDescriptor findMethodByName(final String name) { 2552 final GenericDescriptor result = file.pool.findSymbol(fullName + '.' + name); 2553 if (result instanceof MethodDescriptor) { 2554 return (MethodDescriptor) result; 2555 } else { 2556 return null; 2557 } 2558 } 2559 2560 private final int index; 2561 private ServiceDescriptorProto proto; 2562 private volatile ServiceOptions options; 2563 private final String fullName; 2564 private final FileDescriptor file; 2565 private MethodDescriptor[] methods; 2566 ServiceDescriptor( final ServiceDescriptorProto proto, final FileDescriptor file, final int index)2567 private ServiceDescriptor( 2568 final ServiceDescriptorProto proto, final FileDescriptor file, final int index) 2569 throws DescriptorValidationException { 2570 this.parent = file; 2571 this.index = index; 2572 this.proto = proto; 2573 fullName = computeFullName(file, null, proto.getName()); 2574 this.file = file; 2575 2576 methods = new MethodDescriptor[proto.getMethodCount()]; 2577 for (int i = 0; i < proto.getMethodCount(); i++) { 2578 methods[i] = new MethodDescriptor(proto.getMethod(i), file, this, i); 2579 } 2580 2581 file.pool.addSymbol(this); 2582 } 2583 2584 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()2585 private void resolveAllFeatures() throws DescriptorValidationException { 2586 resolveFeatures(proto.getOptions().getFeatures()); 2587 2588 for (MethodDescriptor method : methods) { 2589 method.resolveAllFeatures(); 2590 } 2591 } 2592 crossLink()2593 private void crossLink() throws DescriptorValidationException { 2594 for (final MethodDescriptor method : methods) { 2595 method.crossLink(); 2596 } 2597 } 2598 2599 /** See {@link FileDescriptor#setProto}. */ setProto(final ServiceDescriptorProto proto)2600 private void setProto(final ServiceDescriptorProto proto) throws DescriptorValidationException { 2601 this.proto = proto; 2602 this.options = null; 2603 resolveFeatures(proto.getOptions().getFeatures()); 2604 2605 for (int i = 0; i < methods.length; i++) { 2606 methods[i].setProto(proto.getMethod(i)); 2607 } 2608 } 2609 } 2610 2611 // ================================================================= 2612 2613 /** Describes one method within a service type. */ 2614 public static final class MethodDescriptor extends GenericDescriptor { 2615 /** 2616 * Get the index of this descriptor within its parent. * @see Descriptors.Descriptor#getIndex() 2617 */ getIndex()2618 public int getIndex() { 2619 return index; 2620 } 2621 2622 /** Convert the descriptor to its protocol message representation. */ 2623 @Override toProto()2624 public MethodDescriptorProto toProto() { 2625 return proto; 2626 } 2627 2628 /** Get the method's unqualified name. */ 2629 @Override getName()2630 public String getName() { 2631 return proto.getName(); 2632 } 2633 2634 /** 2635 * Get the method's fully-qualified name. 2636 * 2637 * @see Descriptors.Descriptor#getFullName() 2638 */ 2639 @Override getFullName()2640 public String getFullName() { 2641 return fullName; 2642 } 2643 2644 /** Get the {@link FileDescriptor} containing this descriptor. */ 2645 @Override getFile()2646 public FileDescriptor getFile() { 2647 return file; 2648 } 2649 2650 /** Get the method's service type. */ getService()2651 public ServiceDescriptor getService() { 2652 return service; 2653 } 2654 2655 /** Get the method's input type. */ getInputType()2656 public Descriptor getInputType() { 2657 return inputType; 2658 } 2659 2660 /** Get the method's output type. */ getOutputType()2661 public Descriptor getOutputType() { 2662 return outputType; 2663 } 2664 2665 /** Get whether or not the inputs are streaming. */ isClientStreaming()2666 public boolean isClientStreaming() { 2667 return proto.getClientStreaming(); 2668 } 2669 2670 /** Get whether or not the outputs are streaming. */ isServerStreaming()2671 public boolean isServerStreaming() { 2672 return proto.getServerStreaming(); 2673 } 2674 2675 /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */ getOptions()2676 public MethodOptions getOptions() { 2677 if (this.options == null) { 2678 MethodOptions strippedOptions = this.proto.getOptions(); 2679 if (strippedOptions.hasFeatures()) { 2680 // Clients should be using feature accessor methods, not accessing features on the 2681 // options 2682 // proto. 2683 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 2684 } 2685 synchronized (this) { 2686 if (this.options == null) { 2687 this.options = strippedOptions; 2688 } 2689 } 2690 } 2691 return this.options; 2692 } 2693 2694 private final int index; 2695 private MethodDescriptorProto proto; 2696 private volatile MethodOptions options; 2697 private final String fullName; 2698 private final FileDescriptor file; 2699 private final ServiceDescriptor service; 2700 2701 // Initialized during cross-linking. 2702 private Descriptor inputType; 2703 private Descriptor outputType; 2704 MethodDescriptor( final MethodDescriptorProto proto, final FileDescriptor file, final ServiceDescriptor parent, final int index)2705 private MethodDescriptor( 2706 final MethodDescriptorProto proto, 2707 final FileDescriptor file, 2708 final ServiceDescriptor parent, 2709 final int index) 2710 throws DescriptorValidationException { 2711 this.parent = parent; 2712 this.index = index; 2713 this.proto = proto; 2714 this.file = file; 2715 service = parent; 2716 2717 fullName = parent.getFullName() + '.' + proto.getName(); 2718 2719 file.pool.addSymbol(this); 2720 } 2721 2722 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()2723 private void resolveAllFeatures() throws DescriptorValidationException { 2724 resolveFeatures(proto.getOptions().getFeatures()); 2725 } 2726 crossLink()2727 private void crossLink() throws DescriptorValidationException { 2728 final GenericDescriptor input = 2729 getFile() 2730 .pool 2731 .lookupSymbol(proto.getInputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 2732 if (!(input instanceof Descriptor)) { 2733 throw new DescriptorValidationException( 2734 this, '\"' + proto.getInputType() + "\" is not a message type."); 2735 } 2736 inputType = (Descriptor) input; 2737 2738 final GenericDescriptor output = 2739 getFile() 2740 .pool 2741 .lookupSymbol(proto.getOutputType(), this, DescriptorPool.SearchFilter.TYPES_ONLY); 2742 if (!(output instanceof Descriptor)) { 2743 throw new DescriptorValidationException( 2744 this, '\"' + proto.getOutputType() + "\" is not a message type."); 2745 } 2746 outputType = (Descriptor) output; 2747 } 2748 2749 /** See {@link FileDescriptor#setProto}. */ setProto(final MethodDescriptorProto proto)2750 private void setProto(final MethodDescriptorProto proto) throws DescriptorValidationException { 2751 this.proto = proto; 2752 this.options = null; 2753 resolveFeatures(proto.getOptions().getFeatures()); 2754 } 2755 } 2756 2757 // ================================================================= 2758 computeFullName( final FileDescriptor file, final Descriptor parent, final String name)2759 private static String computeFullName( 2760 final FileDescriptor file, final Descriptor parent, final String name) { 2761 if (parent != null) { 2762 return parent.getFullName() + '.' + name; 2763 } 2764 2765 final String packageName = file.getPackage(); 2766 if (!packageName.isEmpty()) { 2767 return packageName + '.' + name; 2768 } 2769 2770 return name; 2771 } 2772 2773 // ================================================================= 2774 2775 /** 2776 * All descriptors implement this to make it easier to implement tools like {@code 2777 * DescriptorPool}. 2778 */ 2779 public abstract static class GenericDescriptor { 2780 // Private constructor to prevent subclasses outside of com.google.protobuf.Descriptors GenericDescriptor()2781 private GenericDescriptor() {} 2782 toProto()2783 public abstract Message toProto(); 2784 getName()2785 public abstract String getName(); 2786 getFullName()2787 public abstract String getFullName(); 2788 getFile()2789 public abstract FileDescriptor getFile(); 2790 resolveFeatures(FeatureSet unresolvedFeatures)2791 void resolveFeatures(FeatureSet unresolvedFeatures) throws DescriptorValidationException { 2792 if (this.parent != null 2793 && unresolvedFeatures.equals(FeatureSet.getDefaultInstance()) 2794 && !hasInferredLegacyProtoFeatures()) { 2795 this.features = this.parent.features; 2796 validateFeatures(); 2797 return; 2798 } 2799 2800 // Java features from a custom pool (i.e. buildFrom) may end up in unknown fields or 2801 // use a different descriptor from the generated pool used by the Java runtime. 2802 boolean hasPossibleCustomJavaFeature = false; 2803 for (FieldDescriptor f : unresolvedFeatures.getExtensionFields().keySet()) { 2804 if (f.getNumber() == JavaFeaturesProto.java_.getNumber() 2805 && f != JavaFeaturesProto.java_.getDescriptor()) { 2806 hasPossibleCustomJavaFeature = true; 2807 continue; 2808 } 2809 } 2810 boolean hasPossibleUnknownJavaFeature = 2811 !unresolvedFeatures.getUnknownFields().isEmpty() 2812 && (unresolvedFeatures 2813 .getUnknownFields() 2814 .hasField(JavaFeaturesProto.java_.getNumber()) 2815 ); 2816 if (hasPossibleCustomJavaFeature || hasPossibleUnknownJavaFeature) { 2817 ExtensionRegistry registry = ExtensionRegistry.newInstance(); 2818 registry.add(JavaFeaturesProto.java_); 2819 ByteString bytes = unresolvedFeatures.toByteString(); 2820 try { 2821 unresolvedFeatures = FeatureSet.parseFrom(bytes, registry); 2822 } catch (InvalidProtocolBufferException e) { 2823 throw new DescriptorValidationException( 2824 this, "Failed to parse features with Java feature extension registry.", e); 2825 } 2826 } 2827 2828 FeatureSet.Builder features; 2829 if (this.parent == null) { 2830 Edition edition = getFile().getEdition(); 2831 features = getEditionDefaults(edition).toBuilder(); 2832 } else { 2833 features = this.parent.features.toBuilder(); 2834 } 2835 features.mergeFrom(inferLegacyProtoFeatures()); 2836 features.mergeFrom(unresolvedFeatures); 2837 this.features = internFeatures(features.build()); 2838 validateFeatures(); 2839 } 2840 inferLegacyProtoFeatures()2841 FeatureSet inferLegacyProtoFeatures() { 2842 return FeatureSet.getDefaultInstance(); 2843 } 2844 hasInferredLegacyProtoFeatures()2845 boolean hasInferredLegacyProtoFeatures() { 2846 return false; 2847 } 2848 validateFeatures()2849 void validateFeatures() throws DescriptorValidationException {} 2850 getFeatures()2851 FeatureSet getFeatures() { 2852 // TODO: Remove lazy resolution of unresolved features for legacy syntax for 2853 // compatibility with older <4.26.x gencode in the next breaking release. 2854 if (this.features == null 2855 && (getFile().getEdition() == Edition.EDITION_PROTO2 2856 || getFile().getEdition() == Edition.EDITION_PROTO3)) { 2857 getFile().resolveAllFeaturesImmutable(); 2858 } 2859 if (this.features == null) { 2860 throw new NullPointerException( 2861 String.format("Features not yet loaded for %s.", getFullName())); 2862 } 2863 return this.features; 2864 } 2865 2866 GenericDescriptor parent; 2867 volatile FeatureSet features; 2868 } 2869 2870 /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */ 2871 public static class DescriptorValidationException extends Exception { 2872 private static final long serialVersionUID = 5750205775490483148L; 2873 2874 /** Gets the full name of the descriptor where the error occurred. */ getProblemSymbolName()2875 public String getProblemSymbolName() { 2876 return name; 2877 } 2878 2879 /** Gets the protocol message representation of the invalid descriptor. */ getProblemProto()2880 public Message getProblemProto() { 2881 return proto; 2882 } 2883 2884 /** Gets a human-readable description of the error. */ getDescription()2885 public String getDescription() { 2886 return description; 2887 } 2888 2889 private final String name; 2890 private final Message proto; 2891 private final String description; 2892 DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description)2893 private DescriptorValidationException( 2894 final GenericDescriptor problemDescriptor, final String description) { 2895 super(problemDescriptor.getFullName() + ": " + description); 2896 2897 // Note that problemDescriptor may be partially uninitialized, so we 2898 // don't want to expose it directly to the user. So, we only provide 2899 // the name and the original proto. 2900 name = problemDescriptor.getFullName(); 2901 proto = problemDescriptor.toProto(); 2902 this.description = description; 2903 } 2904 DescriptorValidationException( final GenericDescriptor problemDescriptor, final String description, final Throwable cause)2905 private DescriptorValidationException( 2906 final GenericDescriptor problemDescriptor, 2907 final String description, 2908 final Throwable cause) { 2909 this(problemDescriptor, description); 2910 initCause(cause); 2911 } 2912 DescriptorValidationException( final FileDescriptor problemDescriptor, final String description)2913 private DescriptorValidationException( 2914 final FileDescriptor problemDescriptor, final String description) { 2915 super(problemDescriptor.getName() + ": " + description); 2916 2917 // Note that problemDescriptor may be partially uninitialized, so we 2918 // don't want to expose it directly to the user. So, we only provide 2919 // the name and the original proto. 2920 name = problemDescriptor.getName(); 2921 proto = problemDescriptor.toProto(); 2922 this.description = description; 2923 } 2924 } 2925 2926 // ================================================================= 2927 2928 /** 2929 * A private helper class which contains lookup tables containing all the descriptors defined in a 2930 * particular file. 2931 */ 2932 private static final class DescriptorPool { 2933 2934 /** Defines what subclass of descriptors to search in the descriptor pool. */ 2935 enum SearchFilter { 2936 TYPES_ONLY, 2937 AGGREGATES_ONLY, 2938 ALL_SYMBOLS 2939 } 2940 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies)2941 DescriptorPool(final FileDescriptor[] dependencies, boolean allowUnknownDependencies) { 2942 this.dependencies = 2943 Collections.newSetFromMap( 2944 new IdentityHashMap<FileDescriptor, Boolean>(dependencies.length)); 2945 this.allowUnknownDependencies = allowUnknownDependencies; 2946 2947 for (Descriptors.FileDescriptor dependency : dependencies) { 2948 this.dependencies.add(dependency); 2949 importPublicDependencies(dependency); 2950 } 2951 2952 for (final FileDescriptor dependency : this.dependencies) { 2953 try { 2954 addPackage(dependency.getPackage(), dependency); 2955 } catch (DescriptorValidationException e) { 2956 // Can't happen, because addPackage() only fails when the name 2957 // conflicts with a non-package, but we have not yet added any 2958 // non-packages at this point. 2959 throw new AssertionError(e); 2960 } 2961 } 2962 } 2963 2964 /** Find and put public dependencies of the file into dependencies set. */ importPublicDependencies(final FileDescriptor file)2965 private void importPublicDependencies(final FileDescriptor file) { 2966 for (FileDescriptor dependency : file.getPublicDependencies()) { 2967 if (dependencies.add(dependency)) { 2968 importPublicDependencies(dependency); 2969 } 2970 } 2971 } 2972 2973 private final Set<FileDescriptor> dependencies; 2974 private final boolean allowUnknownDependencies; 2975 2976 private final Map<String, GenericDescriptor> descriptorsByName = new HashMap<>(); 2977 2978 /** Find a generic descriptor by fully-qualified name. */ findSymbol(final String fullName)2979 GenericDescriptor findSymbol(final String fullName) { 2980 return findSymbol(fullName, SearchFilter.ALL_SYMBOLS); 2981 } 2982 2983 /** 2984 * Find a descriptor by fully-qualified name and given option to only search valid field type 2985 * descriptors. 2986 */ findSymbol(final String fullName, final SearchFilter filter)2987 GenericDescriptor findSymbol(final String fullName, final SearchFilter filter) { 2988 GenericDescriptor result = descriptorsByName.get(fullName); 2989 if (result != null) { 2990 if ((filter == SearchFilter.ALL_SYMBOLS) 2991 || ((filter == SearchFilter.TYPES_ONLY) && isType(result)) 2992 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { 2993 return result; 2994 } 2995 } 2996 2997 for (final FileDescriptor dependency : dependencies) { 2998 result = dependency.pool.descriptorsByName.get(fullName); 2999 if (result != null) { 3000 if ((filter == SearchFilter.ALL_SYMBOLS) 3001 || ((filter == SearchFilter.TYPES_ONLY) && isType(result)) 3002 || ((filter == SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { 3003 return result; 3004 } 3005 } 3006 } 3007 3008 return null; 3009 } 3010 3011 /** Checks if the descriptor is a valid type for a message field. */ isType(GenericDescriptor descriptor)3012 boolean isType(GenericDescriptor descriptor) { 3013 return (descriptor instanceof Descriptor) || (descriptor instanceof EnumDescriptor); 3014 } 3015 3016 /** Checks if the descriptor is a valid namespace type. */ isAggregate(GenericDescriptor descriptor)3017 boolean isAggregate(GenericDescriptor descriptor) { 3018 return (descriptor instanceof Descriptor) 3019 || (descriptor instanceof EnumDescriptor) 3020 || (descriptor instanceof PackageDescriptor) 3021 || (descriptor instanceof ServiceDescriptor); 3022 } 3023 3024 /** 3025 * Look up a type descriptor by name, relative to some other descriptor. The name may be 3026 * fully-qualified (with a leading '.'), partially-qualified, or unqualified. C++-like name 3027 * lookup semantics are used to search for the matching descriptor. 3028 */ lookupSymbol( final String name, final GenericDescriptor relativeTo, final DescriptorPool.SearchFilter filter)3029 GenericDescriptor lookupSymbol( 3030 final String name, 3031 final GenericDescriptor relativeTo, 3032 final DescriptorPool.SearchFilter filter) 3033 throws DescriptorValidationException { 3034 3035 GenericDescriptor result; 3036 String fullname; 3037 if (name.startsWith(".")) { 3038 // Fully-qualified name. 3039 fullname = name.substring(1); 3040 result = findSymbol(fullname, filter); 3041 } else { 3042 // If "name" is a compound identifier, we want to search for the 3043 // first component of it, then search within it for the rest. 3044 // If name is something like "Foo.Bar.baz", and symbols named "Foo" are 3045 // defined in multiple parent scopes, we only want to find "Bar.baz" in 3046 // the innermost one. E.g., the following should produce an error: 3047 // message Bar { message Baz {} } 3048 // message Foo { 3049 // message Bar { 3050 // } 3051 // optional Bar.Baz baz = 1; 3052 // } 3053 // So, we look for just "Foo" first, then look for "Bar.baz" within it 3054 // if found. 3055 final int firstPartLength = name.indexOf('.'); 3056 final String firstPart; 3057 if (firstPartLength == -1) { 3058 firstPart = name; 3059 } else { 3060 firstPart = name.substring(0, firstPartLength); 3061 } 3062 3063 // We will search each parent scope of "relativeTo" looking for the 3064 // symbol. 3065 final StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName()); 3066 3067 while (true) { 3068 // Chop off the last component of the scope. 3069 final int dotpos = scopeToTry.lastIndexOf("."); 3070 if (dotpos == -1) { 3071 fullname = name; 3072 result = findSymbol(name, filter); 3073 break; 3074 } else { 3075 scopeToTry.setLength(dotpos + 1); 3076 3077 // Append firstPart and try to find 3078 scopeToTry.append(firstPart); 3079 result = findSymbol(scopeToTry.toString(), DescriptorPool.SearchFilter.AGGREGATES_ONLY); 3080 3081 if (result != null) { 3082 if (firstPartLength != -1) { 3083 // We only found the first part of the symbol. Now look for 3084 // the whole thing. If this fails, we *don't* want to keep 3085 // searching parent scopes. 3086 scopeToTry.setLength(dotpos + 1); 3087 scopeToTry.append(name); 3088 result = findSymbol(scopeToTry.toString(), filter); 3089 } 3090 fullname = scopeToTry.toString(); 3091 break; 3092 } 3093 3094 // Not found. Remove the name so we can try again. 3095 scopeToTry.setLength(dotpos); 3096 } 3097 } 3098 } 3099 3100 if (result == null) { 3101 if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) { 3102 logger.warning( 3103 "The descriptor for message type \"" 3104 + name 3105 + "\" cannot be found and a placeholder is created for it"); 3106 // We create a dummy message descriptor here regardless of the 3107 // expected type. If the type should be message, this dummy 3108 // descriptor will work well and if the type should be enum, a 3109 // DescriptorValidationException will be thrown later. In either 3110 // case, the code works as expected: we allow unknown message types 3111 // but not unknown enum types. 3112 result = new Descriptor(fullname); 3113 // Add the placeholder file as a dependency so we can find the 3114 // placeholder symbol when resolving other references. 3115 this.dependencies.add(result.getFile()); 3116 return result; 3117 } else { 3118 throw new DescriptorValidationException(relativeTo, '\"' + name + "\" is not defined."); 3119 } 3120 } else { 3121 return result; 3122 } 3123 } 3124 3125 /** 3126 * Adds a symbol to the symbol table. If a symbol with the same name already exists, throws an 3127 * error. 3128 */ addSymbol(final GenericDescriptor descriptor)3129 void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException { 3130 validateSymbolName(descriptor); 3131 3132 final String fullName = descriptor.getFullName(); 3133 3134 final GenericDescriptor old = descriptorsByName.put(fullName, descriptor); 3135 if (old != null) { 3136 descriptorsByName.put(fullName, old); 3137 3138 if (descriptor.getFile() == old.getFile()) { 3139 final int dotpos = fullName.lastIndexOf('.'); 3140 if (dotpos == -1) { 3141 throw new DescriptorValidationException( 3142 descriptor, '\"' + fullName + "\" is already defined."); 3143 } else { 3144 throw new DescriptorValidationException( 3145 descriptor, 3146 '\"' 3147 + fullName.substring(dotpos + 1) 3148 + "\" is already defined in \"" 3149 + fullName.substring(0, dotpos) 3150 + "\"."); 3151 } 3152 } else { 3153 throw new DescriptorValidationException( 3154 descriptor, 3155 '\"' 3156 + fullName 3157 + "\" is already defined in file \"" 3158 + old.getFile().getName() 3159 + "\"."); 3160 } 3161 } 3162 } 3163 3164 /** 3165 * Represents a package in the symbol table. We use PackageDescriptors just as placeholders so 3166 * that someone cannot define, say, a message type that has the same name as an existing 3167 * package. 3168 */ 3169 private static final class PackageDescriptor extends GenericDescriptor { 3170 @Override toProto()3171 public Message toProto() { 3172 return file.toProto(); 3173 } 3174 3175 @Override getName()3176 public String getName() { 3177 return name; 3178 } 3179 3180 @Override getFullName()3181 public String getFullName() { 3182 return fullName; 3183 } 3184 3185 @Override getFile()3186 public FileDescriptor getFile() { 3187 return file; 3188 } 3189 PackageDescriptor(final String name, final String fullName, final FileDescriptor file)3190 PackageDescriptor(final String name, final String fullName, final FileDescriptor file) { 3191 this.file = file; 3192 this.fullName = fullName; 3193 this.name = name; 3194 } 3195 3196 private final String name; 3197 private final String fullName; 3198 private final FileDescriptor file; 3199 } 3200 3201 /** 3202 * Adds a package to the symbol tables. If a package by the same name already exists, that is 3203 * fine, but if some other kind of symbol exists under the same name, an exception is thrown. If 3204 * the package has multiple components, this also adds the parent package(s). 3205 */ addPackage(final String fullName, final FileDescriptor file)3206 void addPackage(final String fullName, final FileDescriptor file) 3207 throws DescriptorValidationException { 3208 final int dotpos = fullName.lastIndexOf('.'); 3209 final String name; 3210 if (dotpos == -1) { 3211 name = fullName; 3212 } else { 3213 addPackage(fullName.substring(0, dotpos), file); 3214 name = fullName.substring(dotpos + 1); 3215 } 3216 3217 final GenericDescriptor old = 3218 descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file)); 3219 if (old != null) { 3220 descriptorsByName.put(fullName, old); 3221 if (!(old instanceof PackageDescriptor)) { 3222 throw new DescriptorValidationException( 3223 file, 3224 '\"' 3225 + name 3226 + "\" is already defined (as something other than a " 3227 + "package) in file \"" 3228 + old.getFile().getName() 3229 + "\"."); 3230 } 3231 } 3232 } 3233 3234 /** 3235 * Verifies that the descriptor's name is valid. That is, it contains only letters, digits, and 3236 * underscores, and does not start with a digit. 3237 */ validateSymbolName(final GenericDescriptor descriptor)3238 static void validateSymbolName(final GenericDescriptor descriptor) 3239 throws DescriptorValidationException { 3240 final String name = descriptor.getName(); 3241 if (name.length() == 0) { 3242 throw new DescriptorValidationException(descriptor, "Missing name."); 3243 } 3244 3245 // Non-ASCII characters are not valid in protobuf identifiers, even 3246 // if they are letters or digits. 3247 // The first character must be a letter or '_'. 3248 // Subsequent characters may be letters, numbers, or digits. 3249 for (int i = 0; i < name.length(); i++) { 3250 final char c = name.charAt(i); 3251 if (('a' <= c && c <= 'z') 3252 || ('A' <= c && c <= 'Z') 3253 || (c == '_') 3254 || ('0' <= c && c <= '9' && i > 0)) { 3255 // Valid 3256 continue; 3257 } 3258 throw new DescriptorValidationException( 3259 descriptor, '\"' + name + "\" is not a valid identifier."); 3260 } 3261 } 3262 } 3263 3264 /** Describes a oneof of a message type. */ 3265 public static final class OneofDescriptor extends GenericDescriptor { 3266 /** Get the index of this descriptor within its parent. */ getIndex()3267 public int getIndex() { 3268 return index; 3269 } 3270 3271 @Override getName()3272 public String getName() { 3273 return proto.getName(); 3274 } 3275 3276 @Override getFile()3277 public FileDescriptor getFile() { 3278 return file; 3279 } 3280 3281 @Override getFullName()3282 public String getFullName() { 3283 return fullName; 3284 } 3285 getContainingType()3286 public Descriptor getContainingType() { 3287 return containingType; 3288 } 3289 getFieldCount()3290 public int getFieldCount() { 3291 return fieldCount; 3292 } 3293 getOptions()3294 public OneofOptions getOptions() { 3295 if (this.options == null) { 3296 OneofOptions strippedOptions = this.proto.getOptions(); 3297 if (strippedOptions.hasFeatures()) { 3298 // Clients should be using feature accessor methods, not accessing features on the 3299 // options 3300 // proto. 3301 strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); 3302 } 3303 synchronized (this) { 3304 if (this.options == null) { 3305 this.options = strippedOptions; 3306 } 3307 } 3308 } 3309 return this.options; 3310 } 3311 3312 /** Get a list of this message type's fields. */ getFields()3313 public List<FieldDescriptor> getFields() { 3314 return Collections.unmodifiableList(Arrays.asList(fields)); 3315 } 3316 getField(int index)3317 public FieldDescriptor getField(int index) { 3318 return fields[index]; 3319 } 3320 3321 @Override toProto()3322 public OneofDescriptorProto toProto() { 3323 return proto; 3324 } 3325 isSynthetic()3326 boolean isSynthetic() { 3327 return fields.length == 1 && fields[0].isProto3Optional; 3328 } 3329 3330 /** See {@link FileDescriptor#resolveAllFeatures}. */ resolveAllFeatures()3331 private void resolveAllFeatures() throws DescriptorValidationException { 3332 resolveFeatures(proto.getOptions().getFeatures()); 3333 } 3334 setProto(final OneofDescriptorProto proto)3335 private void setProto(final OneofDescriptorProto proto) throws DescriptorValidationException { 3336 this.proto = proto; 3337 this.options = null; 3338 resolveFeatures(proto.getOptions().getFeatures()); 3339 } 3340 OneofDescriptor( final OneofDescriptorProto proto, final FileDescriptor file, final Descriptor parent, final int index)3341 private OneofDescriptor( 3342 final OneofDescriptorProto proto, 3343 final FileDescriptor file, 3344 final Descriptor parent, 3345 final int index) { 3346 this.parent = parent; 3347 this.proto = proto; 3348 fullName = computeFullName(file, parent, proto.getName()); 3349 this.file = file; 3350 this.index = index; 3351 3352 containingType = parent; 3353 fieldCount = 0; 3354 } 3355 3356 private final int index; 3357 private OneofDescriptorProto proto; 3358 private volatile OneofOptions options; 3359 private final String fullName; 3360 private final FileDescriptor file; 3361 3362 private Descriptor containingType; 3363 private int fieldCount; 3364 private FieldDescriptor[] fields; 3365 } 3366 binarySearch(T[] array, int size, NumberGetter<T> getter, int number)3367 private static <T> T binarySearch(T[] array, int size, NumberGetter<T> getter, int number) { 3368 int left = 0; 3369 int right = size - 1; 3370 3371 while (left <= right) { 3372 int mid = (left + right) / 2; 3373 T midValue = array[mid]; 3374 int midValueNumber = getter.getNumber(midValue); 3375 if (number < midValueNumber) { 3376 right = mid - 1; 3377 } else if (number > midValueNumber) { 3378 left = mid + 1; 3379 } else { 3380 return midValue; 3381 } 3382 } 3383 return null; 3384 } 3385 3386 private interface NumberGetter<T> { getNumber(T t)3387 int getNumber(T t); 3388 } 3389 } 3390