Skip to content

Commit

Permalink
Allow type bindings for "types" members of "any" property to be set, re
Browse files Browse the repository at this point in the history
  • Loading branch information
safris committed May 13, 2024
1 parent b3dc402 commit 4a9a5da
Show file tree
Hide file tree
Showing 26 changed files with 207 additions and 164 deletions.
56 changes: 23 additions & 33 deletions generator/src/main/java/org/jsonx/AnyModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.jsonx.www.schema_0_5.xL0gluGCXAA.$ObjectMember;
import org.jsonx.www.schema_0_5.xL0gluGCXAA.$Reference;
import org.jsonx.www.schema_0_5.xL0gluGCXAA.$Reference.Name$;
import org.libj.lang.Classes;
import org.libj.lang.IllegalAnnotationException;
import org.libj.lang.Strings;
import org.openjax.json.JsonUtil;
Expand Down Expand Up @@ -129,7 +128,7 @@ static AnyModel reference(final Registry registry, final Referrer<?> referrer, f
}

static AnyModel reference(final Registry registry, final Referrer<?> referrer, final $Any xsb, final IdentityHashMap<$AnyType<?>,$FieldBinding> xsbToBinding) {
return registry.reference(new AnyModel(registry, referrer, xsb, xsbToBinding == null ? null : ($FieldBinding)xsbToBinding.get(xsb)), referrer);
return registry.reference(new AnyModel(registry, referrer, xsb, xsbToBinding), referrer);
}

private static final Name$ anonymousReferenceName = new Name$("");
Expand Down Expand Up @@ -160,9 +159,9 @@ static AnyModel reference(final Registry registry, final Referrer<?> referrer, f

private final Member[] types;

private AnyModel(final Registry registry, final Declarer declarer, final $Any xsb, final $FieldBinding binding) {
super(registry, declarer, xsb.getDoc$(), xsb.getNames$(), xsb.getNullable$(), xsb.getUse$(), null, binding == null ? null : binding.getField$(), null);
this.types = getTypes(nullable.get, use.get, xsb.getTypes$(), binding);
private AnyModel(final Registry registry, final Declarer declarer, final $Any xsb, final IdentityHashMap<$AnyType<?>,$FieldBinding> xsbToBinding) {
super(registry, declarer, xsb.getDoc$(), xsb.getNames$(), xsb.getNullable$(), xsb.getUse$(), null, getField(xsbToBinding, xsb), getType(registry, xsbToBinding, xsb));
this.types = getTypes(nullable.get, use.get, xsb.getTypes$(), xsbToBinding == null ? null : xsbToBinding.get(xsb));
validateTypeBinding();
}

Expand All @@ -173,15 +172,14 @@ private AnyModel(final Registry registry, final Declarer declarer, final $Array.
}

private AnyModel(final Registry registry, final Declarer declarer, final AnyProperty property, final Method getMethod, final String fieldName) {
super(registry, declarer, property.nullable(), property.use(), null, null, null);
super(registry, declarer, property.nullable(), property.use(), null, null, Bind.Type.from(registry, getMethod, property.nullable(), property.use(), null, null, Void.class));
final t[] types = property.types();
this.types = getMemberTypes(types);
final Class<?> requiredFieldType = types.length == 0 ? defaultClass() : getFieldType(types);
final boolean isRegex = isMultiRegex(property.name());
if (isRegex && !Map.class.isAssignableFrom(getMethod.getReturnType()))
throw new IllegalAnnotationException(property, getMethod.getDeclaringClass().getName() + "." + fieldName + ": @" + AnyProperty.class.getSimpleName() + " of type " + Bind.Type.getClassName(getMethod, property.nullable(), property.use()) + " with regex name=\"" + property.name() + "\" must be of type that extends " + Map.class.getName());

if (!isAssignable(getMethod, true, requiredFieldType, isRegex, property.nullable(), property.use()))
if (!isAssignable(getMethod, true, isRegex, property.nullable(), property.use(), false, types.length > 0 ? getFieldTypes(types) : new Class<?>[] { defaultClass() }))
throw new IllegalAnnotationException(property, getMethod.getDeclaringClass().getName() + "." + fieldName + ": @" + AnyProperty.class.getSimpleName() + " of type " + Bind.Type.getClassName(getMethod, property.nullable(), property.use()) + " is not assignable for the specified types attribute");

validateTypeBinding();
Expand Down Expand Up @@ -227,38 +225,27 @@ private Member addReference(final String idref, final Boolean nullable, final Us
});
}

private static Class<?> getFieldType(final t[] types) {
private static Class<?>[] getFieldTypes(final t[] types) {
final int len = types.length;
if (len == 0)
return null;

boolean isObjectModel = true;
final Class<?>[] members = new Class<?>[len];
for (int i = 0; i < len; ++i) { // [A]
final t type = types[i];
if (AnyType.isEnabled(type.arrays())) {
if (AnyType.isEnabled(type.arrays()))
members[i] = List.class;
isObjectModel = false;
}
else if (AnyType.isEnabled(type.booleans())) {
else if (AnyType.isEnabled(type.booleans()))
members[i] = Boolean.class;
isObjectModel = false;
}
else if (AnyType.isEnabled(type.numbers())) {
else if (AnyType.isEnabled(type.numbers()))
members[i] = type.numbers().type();
isObjectModel = false;
}
else if (AnyType.isEnabled(type.objects())) {
else if (AnyType.isEnabled(type.objects()))
members[i] = type.objects();
}
else if (AnyType.isEnabled(type.strings())) {
else if (AnyType.isEnabled(type.strings()))
members[i] = type.strings().type();
isObjectModel = false;
}
}

final Class<?> gcc = Classes.getGreatestCommonSuperclass(members);
return gcc != Object.class ? gcc : isObjectModel ? JxObject.class : Object.class;
return members;
}

private Member[] getMemberTypes(final t[] types) {
Expand Down Expand Up @@ -398,7 +385,7 @@ Registry.Type typeDefault(final boolean primitive) {

@Override
String isValid(final Bind.Type typeBinding) {
return typeBinding.type == null ? null : "Cannot override the type for \"any\"";
return null;
}

@Override
Expand Down Expand Up @@ -427,16 +414,18 @@ Class<? extends Annotation> elementAnnotation() {
}

@Override
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
final XmlElement element = super.toXml(owner, packageName, cursor, pathToBinding);
cursor.popName();
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
final XmlElement element = super.toXml(owner, packageName, cursor, pathToBinding, isFromReference);
if (!isFromReference)
cursor.popName();
return element;
}

@Override
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
final PropertyMap<Object> properties = super.toJson(owner, packageName, cursor, pathToBinding);
cursor.popName();
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
final PropertyMap<Object> properties = super.toJson(owner, packageName, cursor, pathToBinding, isFromReference);
if (!isFromReference)
cursor.popName();
return properties;
}

Expand Down Expand Up @@ -478,6 +467,7 @@ else if (type instanceof ObjectModel) {
}
else if (type instanceof BooleanModel || type instanceof NumberModel || type instanceof StringModel) {
final Model model = (Model)type;

final AttributeMap attrs = new AttributeMap();
model.toAnnotationAttributes(attrs, this);
if (b == null)
Expand Down
14 changes: 7 additions & 7 deletions generator/src/main/java/org/jsonx/ArrayModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ static Member referenceOrDeclare(final Registry registry, final Declarer declare

static Member referenceOrDeclare(final Registry registry, final Referrer<?> referrer, final ArrayProperty property, final Method getMethod, final String fieldName) {
final String declaringTypeName = getMethod.getDeclaringClass().getName() + "." + fieldName;
if (!isAssignable(getMethod, true, List.class, false, property.nullable(), property.use()))
if (!isAssignable(getMethod, true, false, property.nullable(), property.use(), true, List.class))
throw new IllegalAnnotationException(property, declaringTypeName + ": @" + ArrayProperty.class.getSimpleName() + " can only be applied to fields of List<?> type with use=\"required\" or nullable=false, or of Optional<? extends List<?>> type with use=\"optional\" and nullable=true");

return referenceOrDeclare(registry, referrer, property, fieldName, Classes.getAnnotations(getMethod), declaringTypeName);
Expand Down Expand Up @@ -506,15 +506,15 @@ void getDeclaredTypes(final Set<? super Registry.Type> types) {

@Override
@SuppressWarnings({"rawtypes", "unchecked"})
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
final XmlElement element = super.toXml(owner, packageName, cursor, pathToBinding);
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
final XmlElement element = super.toXml(owner, packageName, cursor, pathToBinding, isFromReference);
final int size = members.length;
if (size > 0) {
final Collection superElements = element.getElements();
final ArrayList<XmlElement> elements = new ArrayList<>(size + (superElements != null ? superElements.size() : 0));
cursor.inArray();
for (int i = 0; i < size; ++i) // [A]
elements.add(members[i].toXml(this, packageName, cursor, pathToBinding));
elements.add(members[i].toXml(this, packageName, cursor, pathToBinding, false));

if (superElements != null)
elements.addAll(superElements);
Expand All @@ -527,14 +527,14 @@ XmlElement toXml(final Element owner, final String packageName, final JsonPath.C
}

@Override
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
final PropertyMap<Object> element = super.toJson(owner, packageName, cursor, pathToBinding);
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
final PropertyMap<Object> element = super.toJson(owner, packageName, cursor, pathToBinding, isFromReference);
final int len = members.length;
if (len > 0) {
final ArrayList<Object> elements = new ArrayList<>();
cursor.inArray();
for (int i = 0; i < len; ++i) // [A]
elements.add(members[i].toJson(this, packageName, cursor, pathToBinding));
elements.add(members[i].toJson(this, packageName, cursor, pathToBinding, false));

element.put("@elements", elements);
}
Expand Down
27 changes: 22 additions & 5 deletions generator/src/main/java/org/jsonx/Bind.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

Expand All @@ -36,7 +38,7 @@ static Type from(final Registry registry, final $TypeFieldBinding.Type$ type, fi
}

static Type from(final Registry registry, final Method getMethod, final boolean nullable, final Use use, final String decode, final String encode, final Class<?> defaultClass) {
final String typeName = Model.isAssignable(getMethod, false, defaultClass, false, nullable, use) ? null : getClassName(getMethod, nullable, use);
final String typeName = Model.isAssignable(getMethod, false, false, nullable, use, true, defaultClass) ? null : getClassName(getMethod, nullable, use);
return typeName == null && (encode == null || encode.length() == 0) && (decode == null || decode.length() == 0) ? null : new Type(registry, typeName, decode, encode);
}

Expand All @@ -52,17 +54,32 @@ static String getClassName(final Method getMethod, final boolean nullable, final
return getClassName(returnType);

final java.lang.reflect.Type genericType = getMethod.getGenericReturnType();
if (!expectOptional)
return genericType.getTypeName();
if (!(genericType instanceof ParameterizedType)) {
if (!expectOptional)
return genericType.getTypeName();

if (!(genericType instanceof ParameterizedType))
throw new ValidationException("Expected " + Optional.class.getName() + " return type from method: " + getMethod.getName());
}

final java.lang.reflect.Type[] genericTypes = ((ParameterizedType)genericType).getActualTypeArguments();

if (Map.class.isAssignableFrom(returnType)) {
if (genericTypes.length != 2)
throw new ValidationException("Expected " + Map.class.getName() + " return type from method: " + getMethod.getName());

return getClassName((Class<?>)genericTypes[1]);
}

if (genericTypes.length != 1)
throw new ValidationException("Expected " + Optional.class.getName() + " return type from method: " + getMethod.getName());

return getClassName((Class<?>)genericTypes[0]);
if (genericTypes[0] instanceof Class)
return getClassName((Class<?>)genericTypes[0]);

if (List.class.equals(returnType))
return genericType.getTypeName();

return genericTypes[0].getTypeName();
}

static String getClassName(final Class<?> cls) {
Expand Down
10 changes: 5 additions & 5 deletions generator/src/main/java/org/jsonx/BooleanModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ private BooleanModel(final Registry registry, final Declarer declarer, final Boo
// If there is a "decode" spec, then skip the check to verify field<-->{CharSequence,char[]} compatibility
final boolean hasDecode = typeBinding != null && typeBinding.decode != null;
// TODO: Can this be parameterized and moved to Model#validateTypeBinding?
if (!isAssignable(getMethod, true, hasDecode ? null : defaultClass(), false, property.nullable(), property.use()) && !isAssignable(getMethod, true, CharSequence.class, false, property.nullable(), property.use()) || getMethod.getReturnType().isPrimitive() && (property.use() == Use.OPTIONAL || property.nullable()))
if (!isAssignable(getMethod, true, false, property.nullable(), property.use(), true, hasDecode ? null : defaultClass()) && !isAssignable(getMethod, true, false, property.nullable(), property.use(), true, CharSequence.class) || getMethod.getReturnType().isPrimitive() && (property.use() == Use.OPTIONAL || property.nullable()))
throw new IllegalAnnotationException(property, getMethod.getDeclaringClass().getName() + "." + getMethod.getName() + "(): @" + BooleanProperty.class.getSimpleName() + " can only be applied to fields of Object type with use=\"required\" or nullable=false, or of Optional<Object> type with use=\"optional\" and nullable=true");

validateTypeBinding();
Expand Down Expand Up @@ -223,15 +223,15 @@ Class<? extends Annotation> typeAnnotation() {
}

@Override
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
final XmlElement element = super.toXml(owner, packageName, cursor, pathToBinding);
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
final XmlElement element = super.toXml(owner, packageName, cursor, pathToBinding, isFromReference);
cursor.popName();
return element;
}

@Override
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
final PropertyMap<Object> properties = super.toJson(owner, packageName, cursor, pathToBinding);
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
final PropertyMap<Object> properties = super.toJson(owner, packageName, cursor, pathToBinding, isFromReference);
cursor.popName();
return properties;
}
Expand Down
4 changes: 2 additions & 2 deletions generator/src/main/java/org/jsonx/Deferred.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ Class<? extends Annotation> typeAnnotation() {
}

@Override
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
XmlElement toXml(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
throw new UnsupportedOperationException();
}

@Override
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding) {
PropertyMap<Object> toJson(final Element owner, final String packageName, final JsonPath.Cursor cursor, final PropertyMap<AttributeMap> pathToBinding, final boolean isFromReference) {
throw new UnsupportedOperationException();
}
}
4 changes: 2 additions & 2 deletions generator/src/main/java/org/jsonx/Element.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ abstract class Element {
this.doc = doc == null || (str = doc.text()) == null || (str = str.trim()).length() == 0 ? null : str;
}

abstract XmlElement toXml(Element owner, String packageName, JsonPath.Cursor cursor, PropertyMap<AttributeMap> pathToBinding);
abstract Object toJson(Element owner, String packageName, JsonPath.Cursor cursor, PropertyMap<AttributeMap> pathToBinding);
abstract XmlElement toXml(Element owner, String packageName, JsonPath.Cursor cursor, PropertyMap<AttributeMap> pathToBinding, boolean isFromReference);
abstract Object toJson(Element owner, String packageName, JsonPath.Cursor cursor, PropertyMap<AttributeMap> pathToBinding, boolean isFromReference);

static String jsd(final boolean jsd, final String name) {
return jsd ? "@" + name : name;
Expand Down

0 comments on commit 4a9a5da

Please sign in to comment.