31 package com.google.protobuf;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.HashMap;
52 import java.util.HashSet;
53 import java.util.List;
56 import java.util.Stack;
57 import java.util.concurrent.ConcurrentHashMap;
61 final class DescriptorMessageInfoFactory
implements MessageInfoFactory {
62 private static final String GET_DEFAULT_INSTANCE_METHOD_NAME =
"getDefaultInstance";
63 private static final DescriptorMessageInfoFactory instance =
new DescriptorMessageInfoFactory();
64 private static final Set<String> specialFieldNames =
65 new HashSet<>(Arrays.asList(
"cached_size",
"serialized_size",
"class"));
68 private DescriptorMessageInfoFactory() {}
70 public static DescriptorMessageInfoFactory getInstance() {
75 public boolean isSupported(Class<?> messageType) {
76 return GeneratedMessageV3.class.isAssignableFrom(messageType);
80 public MessageInfo messageInfoFor(Class<?> messageType) {
81 if (!GeneratedMessageV3.class.isAssignableFrom(messageType)) {
82 throw new IllegalArgumentException(
"Unsupported message type: " + messageType.getName());
85 return convert(messageType, descriptorForType(messageType));
88 private static Message getDefaultInstance(Class<?> messageType) {
90 Method method = messageType.getDeclaredMethod(GET_DEFAULT_INSTANCE_METHOD_NAME);
91 return (Message)
method.invoke(
null);
92 }
catch (Exception e) {
93 throw new IllegalArgumentException(
94 "Unable to get default instance for message class " + messageType.getName(), e);
98 private static Descriptor descriptorForType(Class<?> messageType) {
99 return getDefaultInstance(messageType).getDescriptorForType();
102 private static MessageInfo convert(Class<?> messageType,
Descriptor messageDescriptor) {
103 switch (messageDescriptor.getFile().getSyntax()) {
105 return convertProto2(messageType, messageDescriptor);
107 return convertProto3(messageType, messageDescriptor);
109 throw new IllegalArgumentException(
110 "Unsupported syntax: " + messageDescriptor.getFile().getSyntax());
128 static class IsInitializedCheckAnalyzer {
131 new ConcurrentHashMap<Descriptor, Boolean>();
135 private int index = 0;
136 private final Stack<Node> stack =
new Stack<Node>();
140 Boolean cachedValue = resultCache.get(
descriptor);
141 if (cachedValue !=
null) {
144 synchronized (
this) {
148 if (cachedValue !=
null) {
151 return dfs(
descriptor).component.needsIsInitializedCheck;
164 this.lowLink =
index;
165 this.component =
null;
170 final List<Descriptor> messages =
new ArrayList<Descriptor>();
171 boolean needsIsInitializedCheck =
false;
186 result.lowLink = Math.min(result.lowLink,
child.lowLink);
188 if (
child.component ==
null) {
190 result.lowLink = Math.min(result.lowLink,
child.lowLink);
196 if (result.index == result.lowLink) {
198 StronglyConnectedComponent component =
new StronglyConnectedComponent();
200 Node node = stack.pop();
201 node.component = component;
202 component.messages.add(node.descriptor);
203 if (node == result) {
215 private void analyze(StronglyConnectedComponent component) {
216 boolean needsIsInitializedCheck =
false;
220 needsIsInitializedCheck =
true;
225 if (
field.isRequired()) {
226 needsIsInitializedCheck =
true;
233 Node node = nodeCache.get(
field.getMessageType());
234 if (node.component != component) {
235 if (node.component.needsIsInitializedCheck) {
236 needsIsInitializedCheck =
true;
244 component.needsIsInitializedCheck = needsIsInitializedCheck;
247 resultCache.put(
descriptor, component.needsIsInitializedCheck);
252 private static IsInitializedCheckAnalyzer isInitializedCheckAnalyzer =
253 new IsInitializedCheckAnalyzer();
256 return isInitializedCheckAnalyzer.needsIsInitializedCheck(
descriptor);
259 private static StructuralMessageInfo convertProto2(
260 Class<?> messageType,
Descriptor messageDescriptor) {
261 List<FieldDescriptor> fieldDescriptors = messageDescriptor.getFields();
262 StructuralMessageInfo.Builder builder =
263 StructuralMessageInfo.newBuilder(fieldDescriptors.size());
264 builder.withDefaultInstance(getDefaultInstance(messageType));
265 builder.withSyntax(ProtoSyntax.PROTO2);
266 builder.withMessageSetWireFormat(messageDescriptor.getOptions().getMessageSetWireFormat());
268 OneofState oneofState =
new OneofState();
269 int bitFieldIndex = 0;
270 int presenceMask = 1;
271 Field bitField =
null;
277 for (
int i = 0;
i < fieldDescriptors.size(); ++
i) {
279 boolean enforceUtf8 = fd.getFile().getOptions().getJavaStringCheckUtf8();
280 Internal.EnumVerifier enumVerifier =
null;
281 if (fd.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) {
283 new Internal.EnumVerifier() {
285 public boolean isInRange(
int number) {
286 return fd.getEnumType().findValueByNumber(
number) !=
null;
290 if (fd.getContainingOneof() !=
null) {
292 builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, enumVerifier));
295 int number = fd.getNumber();
298 if (fd.isMapField()) {
304 final FieldDescriptor valueField = fd.getMessageType().findFieldByNumber(2);
305 if (valueField.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) {
307 new Internal.EnumVerifier() {
309 public boolean isInRange(
int number) {
310 return valueField.getEnumType().findValueByNumber(
number) !=
null;
318 SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
323 if (fd.isRepeated()) {
325 if (enumVerifier !=
null) {
328 forPackedFieldWithEnumVerifier(
331 builder.withField(forFieldWithEnumVerifier(
field,
number,
type, enumVerifier));
335 forRepeatedMessageField(
336 field,
number,
type, getTypeForRepeatedMessageField(messageType, fd)));
340 forPackedField(
field,
number,
type, cachedSizeField(messageType, fd)));
348 if (bitField ==
null) {
350 bitField = bitField(messageType, bitFieldIndex);
354 if (fd.isRequired()) {
356 forProto2RequiredField(
357 field,
number,
type, bitField, presenceMask, enforceUtf8, enumVerifier));
360 forProto2OptionalField(
361 field,
number,
type, bitField, presenceMask, enforceUtf8, enumVerifier));
368 if (presenceMask == 0) {
375 List<Integer> fieldsToCheckIsInitialized =
new ArrayList<Integer>();
376 for (
int i = 0;
i < fieldDescriptors.size(); ++
i) {
380 && needsIsInitializedCheck(fd.getMessageType()))) {
381 fieldsToCheckIsInitialized.add(fd.getNumber());
384 int[] numbers =
new int[fieldsToCheckIsInitialized.size()];
385 for (
int i = 0;
i < fieldsToCheckIsInitialized.size();
i++) {
386 numbers[
i] = fieldsToCheckIsInitialized.get(
i);
388 builder.withCheckInitialized(numbers);
390 return builder.build();
393 private static StructuralMessageInfo convertProto3(
394 Class<?> messageType,
Descriptor messageDescriptor) {
395 List<FieldDescriptor> fieldDescriptors = messageDescriptor.getFields();
396 StructuralMessageInfo.Builder builder =
397 StructuralMessageInfo.newBuilder(fieldDescriptors.size());
398 builder.withDefaultInstance(getDefaultInstance(messageType));
399 builder.withSyntax(ProtoSyntax.PROTO3);
401 OneofState oneofState =
new OneofState();
402 boolean enforceUtf8 =
true;
403 for (
int i = 0;
i < fieldDescriptors.size(); ++
i) {
405 if (fd.getContainingOneof() !=
null) {
407 builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8,
null));
410 if (fd.isMapField()) {
413 field(messageType, fd),
415 SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
419 if (fd.isRepeated() && fd.getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) {
421 forRepeatedMessageField(
422 field(messageType, fd),
425 getTypeForRepeatedMessageField(messageType, fd)));
431 field(messageType, fd),
434 cachedSizeField(messageType, fd)));
437 forField(
field(messageType, fd), fd.getNumber(), getFieldType(fd), enforceUtf8));
441 return builder.build();
445 private static FieldInfo buildOneofMember(
446 Class<?> messageType,
448 OneofState oneofState,
450 Internal.EnumVerifier enumVerifier) {
451 OneofInfo oneof = oneofState.getOneof(messageType, fd.getContainingOneof());
453 Class<?> oneofStoredType = getOneofStoredType(messageType, fd,
type);
454 return forOneofMemberField(
455 fd.getNumber(),
type, oneof, oneofStoredType, enforceUtf8, enumVerifier);
458 private static Class<?> getOneofStoredType(
460 switch (
type.getJavaType()) {
462 return Boolean.class;
464 return ByteString.class;
471 return Integer.class;
477 return getOneofStoredTypeForMessage(messageType, fd);
479 throw new IllegalArgumentException(
"Invalid type for oneof: " +
type);
484 switch (fd.getType()) {
486 if (!fd.isRepeated()) {
493 if (!fd.isRepeated()) {
498 if (!fd.isRepeated()) {
503 if (!fd.isRepeated()) {
508 if (!fd.isRepeated()) {
513 if (!fd.isRepeated()) {
520 if (!fd.isRepeated()) {
525 if (!fd.isRepeated()) {
530 if (fd.isMapField()) {
535 if (!fd.isRepeated()) {
540 if (!fd.isRepeated()) {
545 if (!fd.isRepeated()) {
550 if (!fd.isRepeated()) {
557 if (!fd.isRepeated()) {
562 if (!fd.isRepeated()) {
567 throw new IllegalArgumentException(
"Unsupported field type: " + fd.getType());
571 private static Field bitField(Class<?> messageType,
int index) {
572 return field(messageType,
"bitField" +
index +
"_");
576 return field(messageType, getFieldName(fd));
580 return field(messageType, getCachedSizeFieldName(fd));
583 private static Field field(Class<?> messageType, String fieldName) {
585 return messageType.getDeclaredField(fieldName);
586 }
catch (Exception e) {
587 throw new IllegalArgumentException(
588 "Unable to find field " + fieldName +
" in message class " + messageType.getName());
594 ? fd.getMessageType().getName()
596 String suffix = specialFieldNames.contains(
name) ?
"__" :
"_";
597 return snakeCaseToCamelCase(
name) + suffix;
601 return snakeCaseToCamelCase(fd.getName()) +
"MemoizedSerializedSize";
608 private static String snakeCaseToCamelCase(String snakeCase) {
609 StringBuilder
sb =
new StringBuilder(snakeCase.length() + 1);
610 boolean capNext =
false;
611 for (
int ctr = 0; ctr < snakeCase.length(); ctr++) {
612 char next = snakeCase.charAt(ctr);
615 }
else if (Character.isDigit(
next)) {
618 }
else if (capNext) {
619 sb.append(Character.toUpperCase(
next));
621 }
else if (ctr == 0) {
622 sb.append(Character.toLowerCase(
next));
627 return sb.toString();
633 private static Class<?> getOneofStoredTypeForMessage(Class<?> messageType,
FieldDescriptor fd) {
635 String
name = fd.getType() ==
Type.GROUP ? fd.getMessageType().getName() : fd.getName();
636 Method getter = messageType.getDeclaredMethod(getterForField(
name));
637 return getter.getReturnType();
638 }
catch (Exception e) {
639 throw new RuntimeException(e);
644 private static Class<?> getTypeForRepeatedMessageField(Class<?> messageType,
FieldDescriptor fd) {
646 String
name = fd.getType() ==
Type.GROUP ? fd.getMessageType().getName() : fd.getName();
647 Method getter = messageType.getDeclaredMethod(getterForField(
name),
int.
class);
648 return getter.getReturnType();
649 }
catch (Exception e) {
650 throw new RuntimeException(e);
655 private static String getterForField(String snakeCase) {
656 String camelCase = snakeCaseToCamelCase(snakeCase);
657 StringBuilder builder =
new StringBuilder(
"get");
659 builder.append(Character.toUpperCase(camelCase.charAt(0)));
660 builder.append(camelCase.substring(1, camelCase.length()));
661 return builder.toString();
665 private OneofInfo[]
oneofs =
new OneofInfo[2];
682 String camelCase = snakeCaseToCamelCase(
desc.getName());
683 String valueFieldName = camelCase +
"_";
684 String caseFieldName = camelCase +
"Case_";
686 return new OneofInfo(
687 desc.getIndex(),
field(messageType, caseFieldName),
field(messageType, valueFieldName));