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