use of org.infinispan.protostream.annotations.ProtoSchemaBuilderException in project protostream by infinispan.
the class AbstractMarshallerCodeGenerator method generateReadMethodBody.
/**
* Signature of generated method is:
* <code>
* public java.lang.Object read(org.infinispan.protostream.ProtoStreamMarshaller.ReadContext $1,
* java.lang.Object $2) throws java.io.IOException
* </code>
*/
protected String generateReadMethodBody(ProtoMessageTypeMetadata messageTypeMetadata) {
// todo [anistor] handle unknown fields for adapters also
String getUnknownFieldSetFieldStatement = null;
String setUnknownFieldSetFieldStatement = null;
if (messageTypeMetadata.getUnknownFieldSetField() != null) {
getUnknownFieldSetFieldStatement = "o." + messageTypeMetadata.getUnknownFieldSetField().getName();
setUnknownFieldSetFieldStatement = "o." + messageTypeMetadata.getUnknownFieldSetField().getName() + " = u";
} else if (messageTypeMetadata.getUnknownFieldSetGetter() != null) {
getUnknownFieldSetFieldStatement = "o." + messageTypeMetadata.getUnknownFieldSetGetter().getName() + "()";
setUnknownFieldSetFieldStatement = "o." + messageTypeMetadata.getUnknownFieldSetSetter().getName() + "(u)";
} else if (messageTypeMetadata.getJavaClass().isAssignableTo(Message.class)) {
getUnknownFieldSetFieldStatement = "o.getUnknownFieldSet()";
setUnknownFieldSetFieldStatement = "o.setUnknownFieldSet(u)";
}
IndentWriter iw = new IndentWriter();
iw.append("{\n");
iw.inc();
iw.append("final ").append(TagReader.class.getName()).append(" $in = $1.getReader();\n");
if (messageTypeMetadata.isContainer()) {
iw.append("Object __v$sizeParam = $1.getParam(\"" + WrappedMessage.CONTAINER_SIZE_CONTEXT_PARAM + "\");\n");
iw.append("int __v$size = ((java.lang.Integer) __v$sizeParam).intValue();\n");
}
// if there is no factory then the class must have setters or the fields should be directly accessible and not be final
final boolean noFactory = messageTypeMetadata.getFactory() == null;
if (noFactory) {
iw.append("final ").append(messageTypeMetadata.getJavaClassName()).append(" o = new ").append(messageTypeMetadata.getJavaClassName()).append("();\n");
}
// number of fields that are required and do not have a default value
int mandatoryFields = 0;
// fields that should be tracked for presence and be either initialized with defaults if missing at the end
// or an exception thrown if no default exists
Map<String, Integer> trackedFields = new LinkedHashMap<>();
// First pass over fields. Count how many are mandatory and how many need to be tracked for presence.
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
if (fieldMetadata.isRequired() && fieldMetadata.getDefaultValue() == null) {
mandatoryFields++;
}
if (fieldMetadata.isRequired() || fieldMetadata.getDefaultValue() != null && (noFactory || fieldMetadata.isRepeated() || fieldMetadata.getProtobufType() == Type.BYTES)) {
int trackedFieldsSize = trackedFields.size();
if (trackedFieldsSize % 64 == 0) {
// declare a long variable to emulate a bitset in multiple long variables
iw.append("long __bits$").append(String.valueOf(trackedFieldsSize >> 6)).append(" = 0;\n");
}
trackedFields.put(fieldMetadata.getName(), trackedFieldsSize);
}
}
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
if (fieldMetadata.isRepeated()) {
// a collection local variable
iw.append(fieldMetadata.getCollectionImplementation().getCanonicalName()).append(' ').append(makeCollectionLocalVar(fieldMetadata)).append(" = ");
if (noDefaults || fieldMetadata.isArray()) {
iw.append("null");
} else {
iw.append("new ").append(fieldMetadata.getCollectionImplementation().getCanonicalName()).append("()");
}
iw.append(";\n");
if (!noFactory && fieldMetadata.isArray()) {
// an array local variable
iw.append(fieldMetadata.getJavaTypeName()).append("[] ").append(makeArrayLocalVar(fieldMetadata)).append(" = ");
if (noDefaults) {
iw.append("null");
} else {
iw.append("new ").append(fieldMetadata.getJavaTypeName()).append("[0]");
}
iw.append(";\n");
}
} else if (!noFactory) {
// immutable messages need a per-field local variable initialized to default value if any
iw.append(fieldMetadata.getJavaTypeName()).append(' ').append(makeFieldLocalVar(fieldMetadata));
Object defaultValue = fieldMetadata.getDefaultValue();
if (defaultValue != null && fieldMetadata.getProtobufType() != Type.BYTES) {
// fields of type bytes get assigned default values only at the end to avoid a possibly useless byte[] allocation
String val = toJavaLiteral(defaultValue, fieldMetadata.getJavaType());
iw.append(" = ").append(box(val, fieldMetadata.getJavaType()));
} else {
if (fieldMetadata.isBoxedPrimitive() || fieldMetadata.getProtobufType() == Type.BYTES || fieldMetadata.getProtobufType().getJavaType() == JavaType.STRING || fieldMetadata.getProtobufType().getJavaType() == JavaType.BYTE_STRING || fieldMetadata.getProtobufType().getJavaType() == JavaType.ENUM || fieldMetadata.getProtobufType().getJavaType() == JavaType.MESSAGE || fieldMetadata.getJavaType().getCanonicalName().equals(Date.class.getCanonicalName()) || fieldMetadata.getJavaType().getCanonicalName().equals(Instant.class.getCanonicalName())) {
iw.append(" = null");
} else if (fieldMetadata.isPrimitive()) {
if (fieldMetadata.getProtobufType() == Type.BOOL) {
iw.append(" = false");
} else {
iw.append(" = 0");
}
}
}
iw.append(";\n");
}
}
iw.append("boolean done = false;\n");
iw.append("while (!done) {\n");
iw.inc();
iw.append("final int tag = $in.readTag();\n");
iw.append("switch (tag) {\n");
iw.inc();
iw.append("case 0: {\n");
iw.inc();
iw.append("done = true;\nbreak;\n");
iw.dec();
iw.append("}\n");
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
final String v = makeFieldLocalVar(fieldMetadata);
iw.append("case ").append(makeFieldTag(fieldMetadata.getNumber(), fieldMetadata.getProtobufType().getWireType())).append(": {\n");
iw.inc();
if (BaseProtoSchemaGenerator.generateMarshallerDebugComments) {
iw.append("// type = ").append(fieldMetadata.getProtobufType().toString()).append(", name = ").append(fieldMetadata.getName()).append('\n');
}
switch(fieldMetadata.getProtobufType()) {
case DOUBLE:
case FLOAT:
case INT64:
case UINT64:
case INT32:
case FIXED64:
case FIXED32:
case BOOL:
case STRING:
case BYTES:
case UINT32:
case SFIXED32:
case SFIXED64:
case SINT32:
case SINT64:
{
if (noFactory || fieldMetadata.isRepeated()) {
iw.append(fieldMetadata.getJavaTypeName()).append(' ');
}
iw.append(v).append(" = ").append(box(convert("$in." + makeStreamIOMethodName(fieldMetadata, false) + "()", fieldMetadata), fieldMetadata.getJavaType())).append(";\n");
genSetField(iw, fieldMetadata, trackedFields, messageTypeMetadata);
break;
}
case GROUP:
{
String mdField = initMarshallerDelegateField(iw, fieldMetadata);
if (noFactory || fieldMetadata.isRepeated()) {
iw.append(fieldMetadata.getJavaTypeName()).append(' ');
}
iw.append(v).append(" = (").append(fieldMetadata.getJavaTypeName()).append(") readMessage(").append(mdField).append(", $1);\n");
iw.append("$in.checkLastTagWas(").append(makeFieldTag(fieldMetadata.getNumber(), WireType.END_GROUP)).append(");\n");
genSetField(iw, fieldMetadata, trackedFields, messageTypeMetadata);
break;
}
case MESSAGE:
{
String mdField = initMarshallerDelegateField(iw, fieldMetadata);
iw.append("int length = $in.readUInt32();\n");
iw.append("int oldLimit = $in.pushLimit(length);\n");
if (noFactory || fieldMetadata.isRepeated()) {
iw.append(fieldMetadata.getJavaTypeName()).append(' ');
}
iw.append(v).append(" = (").append(fieldMetadata.getJavaTypeName()).append(") readMessage(").append(mdField).append(", $1);\n");
iw.append("$in.checkLastTagWas(0);\n");
iw.append("$in.popLimit(oldLimit);\n");
genSetField(iw, fieldMetadata, trackedFields, messageTypeMetadata);
break;
}
case ENUM:
{
String mdField = initMarshallerDelegateField(iw, fieldMetadata);
iw.append("int enumVal = $in.readEnum();\n");
if (noFactory || fieldMetadata.isRepeated()) {
iw.append(fieldMetadata.getJavaTypeName()).append(' ');
}
iw.append(v).append(" = (").append(fieldMetadata.getJavaTypeName()).append(") ").append(mdField).append(".getMarshaller().decode(enumVal);\n");
iw.append("if (").append(v).append(" == null) {\n");
if (getUnknownFieldSetFieldStatement != null) {
iw.inc();
iw.append(PROTOSTREAM_PACKAGE).append(".UnknownFieldSet u = ").append(getUnknownFieldSetFieldStatement).append(";\n");
iw.append("if (u == null) { u = new ").append(PROTOSTREAM_PACKAGE).append(".impl.UnknownFieldSetImpl(); ").append(setUnknownFieldSetFieldStatement).append("; }\n");
iw.append("u.putVarintField(").append(String.valueOf(fieldMetadata.getNumber())).append(", enumVal);\n");
iw.dec();
}
iw.append("} else {\n").inc();
genSetField(iw, fieldMetadata, trackedFields, messageTypeMetadata);
iw.dec().append("}\n");
break;
}
default:
throw new IllegalStateException("Unknown field type : " + fieldMetadata.getProtobufType());
}
iw.append("break;\n");
iw.dec();
iw.append("}\n");
}
iw.append("default: {\n");
iw.inc();
if (getUnknownFieldSetFieldStatement != null) {
iw.append(PROTOSTREAM_PACKAGE).append(".UnknownFieldSet u = ").append(getUnknownFieldSetFieldStatement).append(";\n");
iw.append("if (u == null) u = new ").append(PROTOSTREAM_PACKAGE).append(".impl.UnknownFieldSetImpl();\n");
iw.append("if (!u.readSingleField(tag, $in)) done = true;\n");
iw.append("if (!u.isEmpty()) ").append(setUnknownFieldSetFieldStatement).append(";\n");
} else {
iw.append("if (!$in.skipField(tag)) done = true;\n");
}
iw.dec().append("}\n");
iw.dec().append("}\n");
iw.dec().append("}\n");
// assign defaults to missing fields
if (BaseProtoSchemaGenerator.generateMarshallerDebugComments) {
iw.append("\n// default values\n\n");
}
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
Object defaultValue = fieldMetadata.getDefaultValue();
if (defaultValue != null && (noFactory || fieldMetadata.isRepeated() || fieldMetadata.getProtobufType() == Type.BYTES)) {
iw.append("if ").append(makeTestFieldWasNotSet(fieldMetadata, trackedFields)).append(" {\n");
iw.inc();
String val = toJavaLiteral(defaultValue, fieldMetadata.getJavaType());
if (fieldMetadata.isRepeated()) {
String c = makeCollectionLocalVar(fieldMetadata);
if (noDefaults || fieldMetadata.isArray()) {
iw.append("if (").append(c).append(" == null) ").append(c).append(" = new ").append(fieldMetadata.getCollectionImplementation().getCanonicalName()).append("();\n");
}
iw.append(c).append(".add(").append(box(val, typeFactory.fromClass(defaultValue.getClass()))).append(");\n");
} else {
if (noFactory) {
iw.append(createSetPropExpr(messageTypeMetadata, fieldMetadata, "o", box(val, fieldMetadata.getJavaType()))).append(";\n");
} else {
iw.append(makeFieldLocalVar(fieldMetadata)).append(" = ").append(box(val, fieldMetadata.getJavaType())).append(";\n");
}
}
iw.dec();
iw.append("}\n");
}
}
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
if (fieldMetadata.isRepeated()) {
String c = makeCollectionLocalVar(fieldMetadata);
if (fieldMetadata.isArray()) {
if (fieldMetadata.getDefaultValue() == null) {
iw.append("if (").append(c).append(" != null) ");
}
iw.append("{\n").inc();
String a = makeArrayLocalVar(fieldMetadata);
if (fieldMetadata.getJavaType().isPrimitive()) {
if (noFactory) {
iw.append(fieldMetadata.getJavaTypeName()).append("[] ");
}
iw.append(a).append(" = new ").append(fieldMetadata.getJavaTypeName()).append("[").append(c).append(".size()];\n");
XClass boxedType = box(fieldMetadata.getJavaType());
iw.append("int _j = 0;\nfor (java.util.Iterator _it = ").append(c).append(".iterator(); _it.hasNext();) ").append(a).append("[_j++] = ").append(unbox("((" + boxedType.getName() + ") _it.next())", boxedType)).append(";\n");
c = a;
} else {
c = "(" + fieldMetadata.getJavaTypeName() + "[])" + c + ".toArray(new " + fieldMetadata.getJavaTypeName() + "[0])";
}
}
if (noFactory) {
iw.append(createSetPropExpr(messageTypeMetadata, fieldMetadata, "o", c)).append(";\n");
} else if (fieldMetadata.isArray() && !fieldMetadata.getJavaType().isPrimitive()) {
iw.append(makeArrayLocalVar(fieldMetadata)).append(" = ").append(c).append(";\n");
}
if (fieldMetadata.isArray()) {
iw.dec().append('}');
if (!noDefaults && fieldMetadata.getDefaultValue() == null) {
c = "new " + fieldMetadata.getJavaTypeName() + "[0]";
iw.append(" else {\n").inc();
if (noFactory) {
iw.append(createSetPropExpr(messageTypeMetadata, fieldMetadata, "o", c)).append(";\n");
} else {
iw.append(makeArrayLocalVar(fieldMetadata)).append(" = ").append(c).append(";\n");
}
iw.dec().append("}\n");
}
}
iw.append('\n');
}
}
// complain about missing required fields
if (mandatoryFields > 0) {
List<ProtoFieldMetadata> mandatory = messageTypeMetadata.getFields().values().stream().filter(f -> f.isRequired() && f.getDefaultValue() == null).collect(Collectors.toList());
iw.append("if (").append(makeTestFieldWasNotSet(mandatory, trackedFields)).append(") {\n");
iw.inc();
iw.append("final StringBuilder missing = new StringBuilder();\n");
boolean first = true;
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
if (fieldMetadata.isRequired()) {
iw.append("if ").append(makeTestFieldWasNotSet(fieldMetadata, trackedFields)).append(" {\n");
iw.inc();
if (first) {
first = false;
} else {
iw.append("if (missing.length() > 0) missing.append(\", \");\n");
}
iw.append("missing.append(\"").append(fieldMetadata.getName()).append("\");\n");
iw.dec();
iw.append("}\n");
}
}
iw.append("throw new java.io.IOException(\"Required field(s) missing from input stream : \" + missing);\n");
iw.dec();
iw.append("}\n");
}
if (noFactory) {
// return the instance
iw.append("return o;\n");
} else {
// create and return the instance
iw.append("return ");
XExecutable factory = messageTypeMetadata.getFactory();
if (factory instanceof XConstructor) {
iw.append("new ").append(messageTypeMetadata.getJavaClassName());
} else {
if (factory.isStatic()) {
iw.append(messageTypeMetadata.getAnnotatedClassName()).append('.').append(factory.getName());
} else {
iw.append(ADAPTER_FIELD_NAME).append('.').append(factory.getName());
}
}
iw.append('(');
boolean first = true;
for (String paramName : factory.getParameterNames()) {
if (first) {
first = false;
if (messageTypeMetadata.isContainer()) {
iw.append("__v$size");
continue;
}
} else {
iw.append(", ");
}
boolean found = false;
for (ProtoFieldMetadata fieldMetadata : messageTypeMetadata.getFields().values()) {
if (fieldMetadata.getPropertyName().equals(paramName)) {
String var = fieldMetadata.isRepeated() ? (fieldMetadata.isArray() ? makeArrayLocalVar(fieldMetadata) : makeCollectionLocalVar(fieldMetadata)) : makeFieldLocalVar(fieldMetadata);
iw.append(var);
found = true;
break;
}
}
if (!found) {
throw new ProtoSchemaBuilderException("Parameter '" + paramName + "' of factory " + factory + " does not map to any Protobuf field");
}
}
iw.append(");\n");
}
iw.dec().append("}\n");
return iw.toString();
}
use of org.infinispan.protostream.annotations.ProtoSchemaBuilderException in project protostream by infinispan.
the class BaseProtoSchemaGenerator method findOuterType.
private ProtoMessageTypeMetadata findOuterType(XClass c) {
ProtoTypeMetadata outer = null;
XClass ec = c.getEnclosingClass();
while (ec != null) {
if (ec.isEnum()) {
throw new ProtoSchemaBuilderException("Classes defined inside an Enum are not allowed : " + c.getCanonicalName());
}
outer = metadataByClass.get(ec);
if (outer != null) {
break;
}
ec = ec.getEnclosingClass();
}
return (ProtoMessageTypeMetadata) outer;
}
use of org.infinispan.protostream.annotations.ProtoSchemaBuilderException in project protostream by infinispan.
the class ProtoMessageTypeMetadata method generateProto.
@Override
public void generateProto(IndentWriter iw) {
// todo [anistor] need to have a better place for this call
scanMemberAnnotations();
iw.append("\n\n");
appendDocumentation(iw, getDocumentation());
iw.append("message ").append(name);
if (BaseProtoSchemaGenerator.generateSchemaDebugComments) {
iw.append(" /* ").append(getJavaClassName()).append(" */");
}
iw.append(" {\n");
iw.inc();
ReservedProcessor reserved = new ReservedProcessor();
reserved.scan(annotatedClass);
for (int memberNumber : fieldsByNumber.keySet()) {
ProtoFieldMetadata field = fieldsByNumber.get(memberNumber);
XClass where = reserved.checkReserved(memberNumber);
if (where != null) {
throw new ProtoSchemaBuilderException("Protobuf field number " + memberNumber + " of field " + field.getLocation() + " conflicts with 'reserved' statement in " + where.getCanonicalName());
}
where = reserved.checkReserved(field.getName());
if (where != null) {
throw new ProtoSchemaBuilderException("Protobuf field number " + memberNumber + " of field " + field.getLocation() + " conflicts with 'reserved' statement in " + where.getCanonicalName());
}
}
reserved.generate(iw);
for (ProtoTypeMetadata t : innerTypes.values()) {
t.generateProto(iw);
}
for (ProtoFieldMetadata f : fieldsByNumber.values()) {
f.generateProto(iw);
}
iw.dec();
iw.append("}\n");
}
use of org.infinispan.protostream.annotations.ProtoSchemaBuilderException in project protostream by infinispan.
the class ProtoMessageTypeMetadata method getDefaultValue.
/**
* Parses the value from string form (coming from proto schema) to an actual Java instance value, according to its
* type.
*/
private Object getDefaultValue(XClass clazz, String fieldName, XClass fieldType, Type protobufType, String defaultValue) {
if (defaultValue == null || defaultValue.isEmpty()) {
return null;
}
if (fieldType == typeFactory.fromClass(String.class)) {
return defaultValue;
}
if (fieldType.isEnum()) {
ProtoTypeMetadata protoEnumTypeMetadata = protoSchemaGenerator.scanAnnotations(fieldType);
ProtoEnumValueMetadata enumVal = protoEnumTypeMetadata.getEnumMemberByName(defaultValue);
if (enumVal == null) {
throw new ProtoSchemaBuilderException("Invalid default value for field '" + fieldName + "' of Java type " + fieldType.getCanonicalName() + " from class " + clazz.getCanonicalName() + ": " + defaultValue + " is not a member of " + protoEnumTypeMetadata.getFullName() + " enum");
}
return enumVal;
}
if (fieldType == typeFactory.fromClass(Character.class) || fieldType == typeFactory.fromClass(char.class)) {
if (defaultValue.length() > 1) {
throw new ProtoSchemaBuilderException("Invalid default value for field '" + fieldName + "' of Java type " + fieldType.getCanonicalName() + " from class " + clazz.getCanonicalName() + ": " + defaultValue);
}
return defaultValue.charAt(0);
}
if (fieldType == typeFactory.fromClass(Boolean.class) || fieldType == typeFactory.fromClass(boolean.class)) {
return Boolean.valueOf(defaultValue);
}
try {
if (fieldType == typeFactory.fromClass(Integer.class) || fieldType == typeFactory.fromClass(int.class)) {
int v = parseInt(defaultValue);
if (v < 0 && protobufType.isUnsigned()) {
throw new ProtoSchemaBuilderException("Field '" + fieldName + "' of unsigned Protobuf type " + protobufType + " from class " + clazz.getCanonicalName() + " does not allow a negative default value : " + defaultValue);
}
return v;
}
if (fieldType == typeFactory.fromClass(Long.class) || fieldType == typeFactory.fromClass(long.class)) {
long v = parseLong(defaultValue);
if (v < 0 && protobufType.isUnsigned()) {
throw new ProtoSchemaBuilderException("Field '" + fieldName + "' of unsigned Protobuf type " + protobufType + " from class " + clazz.getCanonicalName() + " does not allow a negative default value : " + defaultValue);
}
return v;
}
if (fieldType == typeFactory.fromClass(Short.class) || fieldType == typeFactory.fromClass(short.class)) {
int v = parseInt(defaultValue);
if (v < 0 && protobufType.isUnsigned()) {
throw new ProtoSchemaBuilderException("Field '" + fieldName + "' of unsigned Protobuf type " + protobufType + " from class " + clazz.getCanonicalName() + " does not allow a negative default value : " + defaultValue);
}
if (v < Short.MIN_VALUE || v > Short.MAX_VALUE) {
throw new NumberFormatException("Value out of range for \"" + protobufType + "\": \"" + defaultValue);
}
return (short) v;
}
if (fieldType == typeFactory.fromClass(Double.class) || fieldType == typeFactory.fromClass(double.class)) {
return Double.valueOf(defaultValue);
}
if (fieldType == typeFactory.fromClass(Float.class) || fieldType == typeFactory.fromClass(float.class)) {
return Float.valueOf(defaultValue);
}
if (fieldType == typeFactory.fromClass(Byte.class) || fieldType == typeFactory.fromClass(byte.class)) {
int v = parseInt(defaultValue);
if (v < 0 && protobufType.isUnsigned()) {
throw new ProtoSchemaBuilderException("Field '" + fieldName + "' of unsigned Protobuf type " + protobufType + " from class " + clazz.getCanonicalName() + " does not allow a negative default value : " + defaultValue);
}
if (v < Byte.MIN_VALUE || v > Byte.MAX_VALUE) {
throw new NumberFormatException("Value out of range for \"" + protobufType + "\": \"" + defaultValue);
}
return (byte) v;
}
if (fieldType.isAssignableTo(Date.class)) {
return Long.parseUnsignedLong(defaultValue);
}
if (fieldType.isAssignableTo(Instant.class)) {
return Long.parseUnsignedLong(defaultValue);
}
} catch (NumberFormatException e) {
throw new ProtoSchemaBuilderException("Invalid default value for field '" + fieldName + "' of Java type " + fieldType.getCanonicalName() + " from class " + clazz.getCanonicalName() + ": " + defaultValue, e);
}
if (protobufType == Type.BYTES) {
if (fieldType == typeFactory.fromClass(byte[].class)) {
return cescape(defaultValue);
} else {
throw new ProtoSchemaBuilderException("Invalid default value for field '" + fieldName + "' of Java type " + fieldType.getCanonicalName() + " from class " + clazz.getCanonicalName() + ": " + defaultValue);
}
}
throw new ProtoSchemaBuilderException("No default value is allowed for field '" + fieldName + "' of Java type " + fieldType.getCanonicalName() + " from class " + clazz.getCanonicalName());
}
use of org.infinispan.protostream.annotations.ProtoSchemaBuilderException in project protostream by infinispan.
the class ProtoMessageTypeMetadata method scanMemberAnnotations.
@Override
public void scanMemberAnnotations() {
if (fieldsByNumber == null) {
// All the fields discovered in this class hierarchy, the key is their number.
// We use a TreeMap to ensure ascending order by field number.
fieldsByNumber = new TreeMap<>();
// all the fields discovered in this class hierarchy, by name
fieldsByName = new HashMap<>();
discoverFields(annotatedClass, new HashSet<>());
if (fieldsByNumber.isEmpty()) {
// todo avoid this warning in case where not necessary
// TODO [anistor] remove the "The class should be either annotated or it should have a custom marshaller" part after MessageMarshaller is removed in 5
log.warnf("Class %s does not have any @ProtoField annotated members. The class should be either annotated or it should have a custom marshaller.", getAnnotatedClassName());
}
// If we have a factory method / constructor, we must ensure its parameters match the declared fields
if (factory != null) {
String factoryKind = factory instanceof XConstructor ? "constructor" : (factory.isStatic() ? "static method" : "method");
XClass[] parameterTypes = factory.getParameterTypes();
int startPos = 0;
if (isIndexedContainer || isIterableContainer) {
if (parameterTypes.length == 0 || parameterTypes[0] != typeFactory.fromClass(int.class)) {
throw new ProtoSchemaBuilderException("@ProtoFactory annotated " + factoryKind + " signature mismatch. The first parameter is expected to be of type 'int' : " + factory.toGenericString());
}
startPos = 1;
}
String[] parameterNames = factory.getParameterNames();
if (parameterNames.length != fieldsByNumber.size() + startPos) {
throw new ProtoSchemaBuilderException("@ProtoFactory annotated " + factoryKind + " signature mismatch. Expected " + (fieldsByNumber.size() + startPos) + " parameters but found " + parameterNames.length + " : " + factory.toGenericString());
}
for (; startPos < parameterNames.length; startPos++) {
String parameterName = parameterNames[startPos];
ProtoFieldMetadata fieldMetadata = getFieldByPropertyName(parameterName);
if (fieldMetadata == null) {
throw new ProtoSchemaBuilderException("@ProtoFactory annotated " + factoryKind + " signature mismatch. The parameter '" + parameterName + "' does not match any field : " + factory.toGenericString());
}
XClass parameterType = parameterTypes[startPos];
boolean paramTypeMismatch = false;
if (fieldMetadata.isArray()) {
if (!parameterType.isArray() || parameterType.getComponentType() != fieldMetadata.getJavaType()) {
paramTypeMismatch = true;
}
} else if (fieldMetadata.isRepeated()) {
if (!fieldMetadata.getCollectionImplementation().isAssignableTo(parameterType)) {
paramTypeMismatch = true;
}
// todo [anistor] also check the collection's type parameter
} else if (fieldMetadata.getJavaType() != parameterType) {
paramTypeMismatch = true;
}
if (paramTypeMismatch) {
throw new ProtoSchemaBuilderException("@ProtoFactory annotated " + factoryKind + " signature mismatch: " + factory.toGenericString() + ". The parameter '" + parameterName + "' does not match the type from the field definition.");
}
}
}
}
}
Aggregations