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.Internal.EnumVerifier; 13 import java.lang.reflect.Field; 14 15 /** Information for a single field in a protobuf message class. */ 16 @CheckReturnValue 17 @ExperimentalApi 18 final class FieldInfo implements Comparable<FieldInfo> { 19 private final Field field; 20 private final FieldType type; 21 private final Class<?> messageClass; // The message type for repeated message fields. 22 private final int fieldNumber; 23 private final Field presenceField; 24 private final int presenceMask; 25 private final boolean required; 26 private final boolean enforceUtf8; 27 private final OneofInfo oneof; 28 private final Field cachedSizeField; 29 /** 30 * The actual type stored in the oneof value for this field. Since the oneof value is an {@link 31 * Object}, primitives will store their boxed type. Only valid in conjunction with {@link #oneof} 32 * (both must be either null or non-null. 33 */ 34 private final Class<?> oneofStoredType; 35 36 // TODO: make map default entry lazy? 37 private final Object mapDefaultEntry; 38 39 private final EnumVerifier enumVerifier; 40 41 /** Constructs a new descriptor for a field. */ forField( Field field, int fieldNumber, FieldType fieldType, boolean enforceUtf8)42 public static FieldInfo forField( 43 Field field, int fieldNumber, FieldType fieldType, boolean enforceUtf8) { 44 checkFieldNumber(fieldNumber); 45 checkNotNull(field, "field"); 46 checkNotNull(fieldType, "fieldType"); 47 if (fieldType == FieldType.MESSAGE_LIST || fieldType == FieldType.GROUP_LIST) { 48 throw new IllegalStateException("Shouldn't be called for repeated message fields."); 49 } 50 return new FieldInfo( 51 field, 52 fieldNumber, 53 fieldType, 54 /* messageClass= */ null, 55 /* presenceField= */ null, 56 /* presenceMask= */ 0, 57 /* required= */ false, 58 enforceUtf8, 59 /* oneof= */ null, 60 /* oneofStoredType= */ null, 61 /* mapDefaultEntry= */ null, 62 /* enumVerifier= */ null, 63 /* cachedSizeField= */ null); 64 } 65 66 /** Constructs a new descriptor for a packed field. */ forPackedField( Field field, int fieldNumber, FieldType fieldType, Field cachedSizeField)67 public static FieldInfo forPackedField( 68 Field field, int fieldNumber, FieldType fieldType, Field cachedSizeField) { 69 checkFieldNumber(fieldNumber); 70 checkNotNull(field, "field"); 71 checkNotNull(fieldType, "fieldType"); 72 if (fieldType == FieldType.MESSAGE_LIST || fieldType == FieldType.GROUP_LIST) { 73 throw new IllegalStateException("Shouldn't be called for repeated message fields."); 74 } 75 return new FieldInfo( 76 field, 77 fieldNumber, 78 fieldType, 79 /* messageClass= */ null, 80 /* presenceField= */ null, 81 /* presenceMask= */ 0, 82 /* required= */ false, 83 /* enforceUtf8= */ false, 84 /* oneof= */ null, 85 /* oneofStoredType= */ null, 86 /* mapDefaultEntry= */ null, 87 /* enumVerifier= */ null, 88 cachedSizeField); 89 } 90 91 /** Constructs a new descriptor for a repeated message field. */ forRepeatedMessageField( Field field, int fieldNumber, FieldType fieldType, Class<?> messageClass)92 public static FieldInfo forRepeatedMessageField( 93 Field field, int fieldNumber, FieldType fieldType, Class<?> messageClass) { 94 checkFieldNumber(fieldNumber); 95 checkNotNull(field, "field"); 96 checkNotNull(fieldType, "fieldType"); 97 checkNotNull(messageClass, "messageClass"); 98 return new FieldInfo( 99 field, 100 fieldNumber, 101 fieldType, 102 messageClass, 103 /* presenceField= */ null, 104 /* presenceMask= */ 0, 105 /* required= */ false, 106 /* enforceUtf8= */ false, 107 /* oneof= */ null, 108 /* oneofStoredType= */ null, 109 /* mapDefaultEntry= */ null, 110 /* enumVerifier= */ null, 111 /* cachedSizeField= */ null); 112 } 113 forFieldWithEnumVerifier( Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier)114 public static FieldInfo forFieldWithEnumVerifier( 115 Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier) { 116 checkFieldNumber(fieldNumber); 117 checkNotNull(field, "field"); 118 return new FieldInfo( 119 field, 120 fieldNumber, 121 fieldType, 122 /* messageClass= */ null, 123 /* presenceField= */ null, 124 /* presenceMask= */ 0, 125 /* required= */ false, 126 /* enforceUtf8= */ false, 127 /* oneof= */ null, 128 /* oneofStoredType= */ null, 129 /* mapDefaultEntry= */ null, 130 enumVerifier, 131 /* cachedSizeField= */ null); 132 } 133 forPackedFieldWithEnumVerifier( Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier, Field cachedSizeField)134 public static FieldInfo forPackedFieldWithEnumVerifier( 135 Field field, 136 int fieldNumber, 137 FieldType fieldType, 138 EnumVerifier enumVerifier, 139 Field cachedSizeField) { 140 checkFieldNumber(fieldNumber); 141 checkNotNull(field, "field"); 142 return new FieldInfo( 143 field, 144 fieldNumber, 145 fieldType, 146 /* messageClass= */ null, 147 /* presenceField= */ null, 148 /* presenceMask= */ 0, 149 /* required= */ false, 150 /* enforceUtf8= */ false, 151 /* oneof= */ null, 152 /* oneofStoredType= */ null, 153 /* mapDefaultEntry= */ null, 154 enumVerifier, 155 cachedSizeField); 156 } 157 158 /** Constructor for a field with explicit presence (e.g. proto2). */ forExplicitPresenceField( Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask, boolean enforceUtf8, EnumVerifier enumVerifier)159 public static FieldInfo forExplicitPresenceField( 160 Field field, 161 int fieldNumber, 162 FieldType fieldType, 163 Field presenceField, 164 int presenceMask, 165 boolean enforceUtf8, 166 EnumVerifier enumVerifier) { 167 checkFieldNumber(fieldNumber); 168 checkNotNull(field, "field"); 169 checkNotNull(fieldType, "fieldType"); 170 checkNotNull(presenceField, "presenceField"); 171 if (presenceField != null && !isExactlyOneBitSet(presenceMask)) { 172 throw new IllegalArgumentException( 173 "presenceMask must have exactly one bit set: " + presenceMask); 174 } 175 return new FieldInfo( 176 field, 177 fieldNumber, 178 fieldType, 179 /* messageClass= */ null, 180 presenceField, 181 presenceMask, 182 /* required= */ false, 183 enforceUtf8, 184 /* oneof= */ null, 185 /* oneofStoredType= */ null, 186 /* mapDefaultEntry= */ null, 187 enumVerifier, 188 /* cachedSizeField= */ null); 189 } 190 191 /** 192 * Constructor for a field that is part of a oneof. 193 * 194 * @param fieldNumber the unique field number for this field within the message. 195 * @param fieldType the type of the field (must be non-null). 196 * @param oneof the oneof for which this field is associated (must be non-null). 197 * @param oneofStoredType the actual type stored in the oneof value for this field. Since the 198 * oneof value is an {@link Object}, primitives will store their boxed type. Must be non-null. 199 * @param enforceUtf8 Only used for string fields. If {@code true}, will enforce UTF-8 on a string 200 * field. 201 * @return the {@link FieldInfo} describing this field. 202 */ forOneofMemberField( int fieldNumber, FieldType fieldType, OneofInfo oneof, Class<?> oneofStoredType, boolean enforceUtf8, EnumVerifier enumVerifier)203 public static FieldInfo forOneofMemberField( 204 int fieldNumber, 205 FieldType fieldType, 206 OneofInfo oneof, 207 Class<?> oneofStoredType, 208 boolean enforceUtf8, 209 EnumVerifier enumVerifier) { 210 checkFieldNumber(fieldNumber); 211 checkNotNull(fieldType, "fieldType"); 212 checkNotNull(oneof, "oneof"); 213 checkNotNull(oneofStoredType, "oneofStoredType"); 214 if (!fieldType.isScalar()) { 215 throw new IllegalArgumentException( 216 "Oneof is only supported for scalar fields. Field " 217 + fieldNumber 218 + " is of type " 219 + fieldType); 220 } 221 return new FieldInfo( 222 /* field= */ null, 223 fieldNumber, 224 fieldType, 225 /* messageClass= */ null, 226 /* presenceField= */ null, 227 /* presenceMask= */ 0, 228 /* required= */ false, 229 enforceUtf8, 230 oneof, 231 oneofStoredType, 232 /* mapDefaultEntry= */ null, 233 enumVerifier, 234 /* cachedSizeField= */ null); 235 } 236 checkFieldNumber(int fieldNumber)237 private static void checkFieldNumber(int fieldNumber) { 238 if (fieldNumber <= 0) { 239 throw new IllegalArgumentException("fieldNumber must be positive: " + fieldNumber); 240 } 241 } 242 243 /** Constructor for a legacy required field. */ forLegacyRequiredField( Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask, boolean enforceUtf8, EnumVerifier enumVerifier)244 public static FieldInfo forLegacyRequiredField( 245 Field field, 246 int fieldNumber, 247 FieldType fieldType, 248 Field presenceField, 249 int presenceMask, 250 boolean enforceUtf8, 251 EnumVerifier enumVerifier) { 252 checkFieldNumber(fieldNumber); 253 checkNotNull(field, "field"); 254 checkNotNull(fieldType, "fieldType"); 255 checkNotNull(presenceField, "presenceField"); 256 if (presenceField != null && !isExactlyOneBitSet(presenceMask)) { 257 throw new IllegalArgumentException( 258 "presenceMask must have exactly one bit set: " + presenceMask); 259 } 260 return new FieldInfo( 261 field, 262 fieldNumber, 263 fieldType, 264 /* messageClass= */ null, 265 presenceField, 266 presenceMask, 267 /* required= */ true, 268 enforceUtf8, 269 /* oneof= */ null, 270 /* oneofStoredType= */ null, 271 /* mapDefaultEntry= */ null, 272 /* enumVerifier= */ enumVerifier, 273 /* cachedSizeField= */ null); 274 } 275 forMapField( Field field, int fieldNumber, Object mapDefaultEntry, EnumVerifier enumVerifier)276 public static FieldInfo forMapField( 277 Field field, int fieldNumber, Object mapDefaultEntry, EnumVerifier enumVerifier) { 278 checkNotNull(mapDefaultEntry, "mapDefaultEntry"); 279 checkFieldNumber(fieldNumber); 280 checkNotNull(field, "field"); 281 return new FieldInfo( 282 field, 283 fieldNumber, 284 FieldType.MAP, 285 /* messageClass= */ null, 286 /* presenceField= */ null, 287 /* presenceMask= */ 0, 288 /* required= */ false, 289 /* enforceUtf8= */ true, 290 /* oneof= */ null, 291 /* oneofStoredType= */ null, 292 mapDefaultEntry, 293 enumVerifier, 294 /* cachedSizeField= */ null); 295 } 296 FieldInfo( Field field, int fieldNumber, FieldType type, Class<?> messageClass, Field presenceField, int presenceMask, boolean required, boolean enforceUtf8, OneofInfo oneof, Class<?> oneofStoredType, Object mapDefaultEntry, EnumVerifier enumVerifier, Field cachedSizeField)297 private FieldInfo( 298 Field field, 299 int fieldNumber, 300 FieldType type, 301 Class<?> messageClass, 302 Field presenceField, 303 int presenceMask, 304 boolean required, 305 boolean enforceUtf8, 306 OneofInfo oneof, 307 Class<?> oneofStoredType, 308 Object mapDefaultEntry, 309 EnumVerifier enumVerifier, 310 Field cachedSizeField) { 311 this.field = field; 312 this.type = type; 313 this.messageClass = messageClass; 314 this.fieldNumber = fieldNumber; 315 this.presenceField = presenceField; 316 this.presenceMask = presenceMask; 317 this.required = required; 318 this.enforceUtf8 = enforceUtf8; 319 this.oneof = oneof; 320 this.oneofStoredType = oneofStoredType; 321 this.mapDefaultEntry = mapDefaultEntry; 322 this.enumVerifier = enumVerifier; 323 this.cachedSizeField = cachedSizeField; 324 } 325 326 /** Gets the field number for the field. */ getFieldNumber()327 public int getFieldNumber() { 328 return fieldNumber; 329 } 330 331 /** Gets the subject {@link Field} of this descriptor. */ getField()332 public Field getField() { 333 return field; 334 } 335 336 /** Gets the type information for the field. */ getType()337 public FieldType getType() { 338 return type; 339 } 340 341 /** Gets the oneof for which this field is a member, or {@code null} if not part of a oneof. */ getOneof()342 public OneofInfo getOneof() { 343 return oneof; 344 } 345 346 /** 347 * Gets the actual type stored in the oneof value by this field. Since the oneof value is an 348 * {@link Object}, primitives will store their boxed type. For non-oneof fields, this will always 349 * be {@code null}. 350 */ getOneofStoredType()351 public Class<?> getOneofStoredType() { 352 return oneofStoredType; 353 } 354 355 /** Gets the {@code EnumVerifier} if the field is an enum field. */ getEnumVerifier()356 public EnumVerifier getEnumVerifier() { 357 return enumVerifier; 358 } 359 360 @Override compareTo(FieldInfo o)361 public int compareTo(FieldInfo o) { 362 return fieldNumber - o.fieldNumber; 363 } 364 365 /** 366 * For repeated message fields, returns the message type of the field. For other fields, returns 367 * {@code null}. 368 */ getListElementType()369 public Class<?> getListElementType() { 370 return messageClass; 371 } 372 373 /** Gets the presence bit field. Only valid for unary fields. For lists, returns {@code null}. */ getPresenceField()374 public Field getPresenceField() { 375 return presenceField; 376 } 377 getMapDefaultEntry()378 public Object getMapDefaultEntry() { 379 return mapDefaultEntry; 380 } 381 382 /** 383 * If {@link #getPresenceField()} is non-{@code null}, returns the mask used to identify the 384 * presence bit for this field in the message. 385 */ getPresenceMask()386 public int getPresenceMask() { 387 return presenceMask; 388 } 389 390 /** Whether this is a required field. */ isRequired()391 public boolean isRequired() { 392 return required; 393 } 394 395 /** 396 * Whether a UTF-8 should be enforced on string fields. Only applies to strings and string lists. 397 */ isEnforceUtf8()398 public boolean isEnforceUtf8() { 399 return enforceUtf8; 400 } 401 getCachedSizeField()402 public Field getCachedSizeField() { 403 return cachedSizeField; 404 } 405 406 /** 407 * For singular or repeated message fields, returns the message type. For other fields, returns 408 * {@code null}. 409 */ getMessageFieldClass()410 public Class<?> getMessageFieldClass() { 411 switch (type) { 412 case MESSAGE: 413 case GROUP: 414 return field != null ? field.getType() : oneofStoredType; 415 case MESSAGE_LIST: 416 case GROUP_LIST: 417 return messageClass; 418 default: 419 return null; 420 } 421 } 422 newBuilder()423 public static Builder newBuilder() { 424 return new Builder(); 425 } 426 427 /** A builder for {@link FieldInfo} instances. */ 428 public static final class Builder { 429 private Field field; 430 private FieldType type; 431 private int fieldNumber; 432 private Field presenceField; 433 private int presenceMask; 434 private boolean required; 435 private boolean enforceUtf8; 436 private OneofInfo oneof; 437 private Class<?> oneofStoredType; 438 private Object mapDefaultEntry; 439 private EnumVerifier enumVerifier; 440 private Field cachedSizeField; 441 Builder()442 private Builder() {} 443 444 /** 445 * Specifies the actual field on the message represented by this field. This should not be 446 * called for oneof member fields. 447 */ withField(Field field)448 public Builder withField(Field field) { 449 if (oneof != null) { 450 throw new IllegalStateException("Cannot set field when building a oneof."); 451 } 452 this.field = field; 453 return this; 454 } 455 456 /** Specifies the type of this field. */ withType(FieldType type)457 public Builder withType(FieldType type) { 458 this.type = type; 459 return this; 460 } 461 462 /** Specifies the unique field number for this field within the message. */ withFieldNumber(int fieldNumber)463 public Builder withFieldNumber(int fieldNumber) { 464 this.fieldNumber = fieldNumber; 465 return this; 466 } 467 468 /** Specifies proto2 presence information. This should not be called for oneof fields. */ withPresence(Field presenceField, int presenceMask)469 public Builder withPresence(Field presenceField, int presenceMask) { 470 this.presenceField = checkNotNull(presenceField, "presenceField"); 471 this.presenceMask = presenceMask; 472 return this; 473 } 474 475 /** 476 * Sets the information for building a oneof member field. This is incompatible with {@link 477 * #withField(Field)} and {@link #withPresence(Field, int)}. 478 * 479 * @param oneof the oneof for which this field is associated. 480 * @param oneofStoredType the actual type stored in the oneof value for this field. Since the 481 * oneof value is an {@link Object}, primitives will store their boxed type. 482 */ withOneof(OneofInfo oneof, Class<?> oneofStoredType)483 public Builder withOneof(OneofInfo oneof, Class<?> oneofStoredType) { 484 if (field != null || presenceField != null) { 485 throw new IllegalStateException( 486 "Cannot set oneof when field or presenceField have been provided"); 487 } 488 this.oneof = oneof; 489 this.oneofStoredType = oneofStoredType; 490 return this; 491 } 492 withRequired(boolean required)493 public Builder withRequired(boolean required) { 494 this.required = required; 495 return this; 496 } 497 withMapDefaultEntry(Object mapDefaultEntry)498 public Builder withMapDefaultEntry(Object mapDefaultEntry) { 499 this.mapDefaultEntry = mapDefaultEntry; 500 return this; 501 } 502 withEnforceUtf8(boolean enforceUtf8)503 public Builder withEnforceUtf8(boolean enforceUtf8) { 504 this.enforceUtf8 = enforceUtf8; 505 return this; 506 } 507 withEnumVerifier(EnumVerifier enumVerifier)508 public Builder withEnumVerifier(EnumVerifier enumVerifier) { 509 this.enumVerifier = enumVerifier; 510 return this; 511 } 512 withCachedSizeField(Field cachedSizeField)513 public Builder withCachedSizeField(Field cachedSizeField) { 514 this.cachedSizeField = cachedSizeField; 515 return this; 516 } 517 build()518 public FieldInfo build() { 519 if (oneof != null) { 520 return forOneofMemberField( 521 fieldNumber, type, oneof, oneofStoredType, enforceUtf8, enumVerifier); 522 } 523 if (mapDefaultEntry != null) { 524 return forMapField(field, fieldNumber, mapDefaultEntry, enumVerifier); 525 } 526 if (presenceField != null) { 527 if (required) { 528 return forLegacyRequiredField( 529 field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier); 530 } else { 531 return forExplicitPresenceField( 532 field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier); 533 } 534 } 535 if (enumVerifier != null) { 536 if (cachedSizeField == null) { 537 return forFieldWithEnumVerifier(field, fieldNumber, type, enumVerifier); 538 } else { 539 return forPackedFieldWithEnumVerifier( 540 field, fieldNumber, type, enumVerifier, cachedSizeField); 541 } 542 } else { 543 if (cachedSizeField == null) { 544 return forField(field, fieldNumber, type, enforceUtf8); 545 } else { 546 return forPackedField(field, fieldNumber, type, cachedSizeField); 547 } 548 } 549 } 550 } 551 isExactlyOneBitSet(int value)552 private static boolean isExactlyOneBitSet(int value) { 553 return value != 0 && (value & (value - 1)) == 0; 554 } 555 } 556