Added support to specify property type

This commit is contained in:
Luca Frosini 2024-06-21 18:10:49 +02:00
parent 5abcb94c52
commit 8cea94d297
5 changed files with 82 additions and 11 deletions

View File

@ -122,6 +122,7 @@ public class PropertyTypeName {
protected static final Map<Class<?>,BaseType> BASE_PROPERTY_TYPES_BY_CLASS;
protected static final Map<Class<?>,String> REGEX_BY_CLASS_MAPPED_AS_STRING;
protected static final Map<String,String> REGEX_BY_CLASS_MAPPED_AS_STRING_BY_CLASS_NAME;
static {
BASE_PROPERTY_TYPES_BY_CLASS = new HashMap<>();
@ -180,6 +181,13 @@ public class PropertyTypeName {
REGEX_BY_CLASS_MAPPED_AS_STRING.put(UUID.class,PropertyTypeName.UUID_REGEX);
REGEX_BY_CLASS_MAPPED_AS_STRING.put(Version.class, Version.VERSION_REGEX);
REGEX_BY_CLASS_MAPPED_AS_STRING_BY_CLASS_NAME = new HashMap<>();
for(Class<?> clz : REGEX_BY_CLASS_MAPPED_AS_STRING.keySet()) {
String className = clz.getSimpleName();
String regex = REGEX_BY_CLASS_MAPPED_AS_STRING.get(clz);
REGEX_BY_CLASS_MAPPED_AS_STRING_BY_CLASS_NAME.put(className, regex);
}
}
public final static String URI_REGEX = null;
@ -203,6 +211,10 @@ public class PropertyTypeName {
return REGEX_BY_CLASS_MAPPED_AS_STRING.get(clz);
}
public static String getRegexByClassname(String className) {
return REGEX_BY_CLASS_MAPPED_AS_STRING_BY_CLASS_NAME.get(className);
}
/**
* Return the correspondent type by checking the "assignability" of the
* class received as parameter.
@ -235,10 +247,34 @@ public class PropertyTypeName {
protected BaseType genericBaseType;
protected String genericClassName;
protected Class<?> clz;
public PropertyTypeName(String type) {
setType(type);
}
public PropertyTypeName(Class<?> clz) {
baseType = null;
genericType = false;
genericBaseType = null;
genericClassName = null;
logger.trace("The type is {}", clz);
baseType = PropertyTypeName.getBaseTypeByClass(clz);
if(baseType == BaseType.PROPERTY){
if(clz != PropertyElement.class){
@SuppressWarnings("unchecked")
Class<? extends Element> type = (Class<? extends Element>) clz;
genericType = true;
genericClassName = TypeMapper.getType(type);
return;
}
}
}
public PropertyTypeName(Method method) {
baseType = null;
genericType = false;
@ -265,13 +301,13 @@ public class PropertyTypeName {
genericType = true;
java.lang.reflect.Type genericReturnType = method.getGenericReturnType();
logger.trace("Generic Return Type for method {} is {}", method, genericReturnType);
logger.trace("Generic Type for method {} is {}", method, genericReturnType);
java.lang.reflect.Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
java.lang.reflect.Type genericType = null;
for(java.lang.reflect.Type t : actualTypeArguments){
logger.trace("Generic Return Type {} for method {} - Actual Type Argument : {}", genericReturnType, method, t);
logger.trace("Generic Type {} for method {} - Actual Type Argument : {}", genericReturnType, method, t);
genericType = t;
}

View File

@ -0,0 +1,17 @@
package org.gcube.informationsystem.types.annotations;
import org.gcube.informationsystem.types.PropertyTypeName.BaseType;
/**
* This is a facade class.
* {@link ISProperty} has the possibility to specify the type.
* If we don't specify a default in the annotation definition the type is mandatory.
* We need a way to specify a default which means get the return of the annotated method.
* This avoid to the developer to specify something which can be get from the annotated method.
* We can't use Object as default because Object is mapped as {@link BaseType#ANY}
*
* This interface has been defined for this reason.
*/
public interface GetReturnType {
}

View File

@ -8,6 +8,7 @@ import java.lang.annotation.Target;
import org.gcube.informationsystem.model.reference.entities.Entity;
import org.gcube.informationsystem.types.TypeMapper;
import org.gcube.informationsystem.types.PropertyTypeName.BaseType;
import org.gcube.informationsystem.types.reference.properties.PropertyDefinition;
/**
@ -24,6 +25,17 @@ import org.gcube.informationsystem.types.reference.properties.PropertyDefinition
public @interface ISProperty {
String name() default "";
/**
* If we don't specify a default in the annotation definition, the type becomes mandatory.
* We wanted a way to specify a default that means using the return type of the annotated method.
* This avoids the need for developers to specify something that can be obtained from the annotated method itself.
* We can't use Object as the default because Object is mapped as {@link BaseType#ANY}.
* For this reason, the type {@link GetReturnType} has been set as the default.
*
* Please note that you can't use this functionality for generic types
* like List<String> because it is not accepted by the annotation.
*/
Class<?> type() default GetReturnType.class;
String description() default "";
boolean mandatory() default false;
boolean readonly() default false;

View File

@ -12,6 +12,7 @@ import org.gcube.com.fasterxml.jackson.annotation.JsonTypeName;
import org.gcube.informationsystem.model.impl.properties.AttributeUtility;
import org.gcube.informationsystem.types.PropertyTypeName;
import org.gcube.informationsystem.types.annotations.ISProperty;
import org.gcube.informationsystem.types.annotations.GetReturnType;
import org.gcube.informationsystem.types.reference.properties.PropertyDefinition;
import org.gcube.informationsystem.utils.TypeUtility;
import org.gcube.informationsystem.utils.Version;
@ -82,19 +83,25 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
this.min = propertyAnnotation.min();
}
this.propertyTypeName = new PropertyTypeName(method);
this.propertyTypeName = new PropertyTypeName(method);
Class<?> clz = propertyAnnotation.type();
if(clz==GetReturnType.class) {
// We have to read the return of the method
this.propertyTypeName = new PropertyTypeName(method);
clz = method.getReturnType();
logger.trace("Return Type for method {} is {}", method, clz);
}else {
this.propertyTypeName = new PropertyTypeName(clz);
}
String defaultValueAsString = propertyAnnotation.defaultValue();
defaultValueAsString = AttributeUtility.evaluateNullForDefaultValue(defaultValueAsString);
// The default value is evaluated to test if compliant with the declared type
this.defaultValue = AttributeUtility.evaluateDefaultValueStringAccordingBaseType(propertyTypeName.getBaseType(), defaultValueAsString);
Class<?> clz = method.getReturnType();
logger.trace("Return Type for method {} is {}", method, clz);
if(!propertyAnnotation.regexpr().isEmpty()) this.regexp = propertyAnnotation.regexpr();
if(this.regexp==null || this.regexp.compareTo("")==0 ){
if(!propertyAnnotation.regexpr().isEmpty()) {
this.regexp = propertyAnnotation.regexpr();
}else {
if(Enum.class.isAssignableFrom(clz)){
Object[] constants = clz.getEnumConstants();
StringBuilder stringBuilder = new StringBuilder("^(");
@ -119,7 +126,6 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
if(Version.class.isAssignableFrom(clz)){
this.regexp = Version.VERSION_REGEX;
}
}
}

View File

@ -13,7 +13,7 @@ import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
*
* @author Luca Frosini (ISTI - CNR)
*/
public class Version implements Comparable<Version> {
public final class Version implements Comparable<Version> {
/**
* Regex validating the version