Changing the way to serialise a type of a property

This commit is contained in:
Luca Frosini 2021-01-19 15:32:19 +01:00
parent 613fc76259
commit 268878d9f7
9 changed files with 218 additions and 125 deletions

View File

@ -1,5 +1,7 @@
package org.gcube.informationsystem.types;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
@ -8,17 +10,23 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import org.gcube.informationsystem.base.reference.Element;
import org.gcube.informationsystem.base.reference.properties.PropertyElement;
import org.gcube.informationsystem.utils.TypeVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class PropertyTypeName {
private static Logger logger = LoggerFactory.getLogger(PropertyTypeName.class);
public enum BaseType {
BOOLEAN("Boolean"),
@ -146,15 +154,18 @@ public class PropertyTypeName {
BaseType type = BASE_PROPERTY_TYPES_BY_CLASS.get(clazz);
if(type == null && Enum.class.isAssignableFrom(clazz)) {
type = BASE_PROPERTY_TYPES_BY_CLASS.get(Enum.class);
if(type == null) {
for(Class<?> c : BASE_PROPERTY_TYPES_BY_CLASS.keySet()) {
if(c.isAssignableFrom(clazz)) {
type = BASE_PROPERTY_TYPES_BY_CLASS.get(c);
}
}
}
return type;
}
protected BaseType baseType;
protected boolean genericType;
protected BaseType genericBaseType;
protected String genericClassName;
@ -163,6 +174,58 @@ public class PropertyTypeName {
setType(type);
}
public PropertyTypeName(Method method) {
baseType = null;
genericType = false;
genericBaseType = null;
genericClassName = null;
Class<?> clz = method.getReturnType();
logger.trace("Return Type for method {} is {}", method, clz);
baseType = PropertyTypeName.getBaseTypeByClass(clz);
if(baseType == BaseType.PROPERTY){
if(clz != PropertyElement.class){
@SuppressWarnings("unchecked")
Class<? extends Element> type = (Class<? extends Element>) clz;
genericClassName = TypeMapper.getType(type);
return;
}
}
if(baseType!=null) {
if(baseType.ordinal()>BaseType.PROPERTY.ordinal()){
genericType = true;
java.lang.reflect.Type genericReturnType = method.getGenericReturnType();
logger.trace("Generic Return 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);
genericType = t;
}
genericBaseType = PropertyTypeName.getBaseTypeByClass((Class<?>) genericType);
if(genericBaseType!= null && genericBaseType.ordinal()>BaseType.PROPERTY.ordinal()) {
throw new IllegalArgumentException("The generic of a " + baseType.toString() + " cannot be a Set, a List or a Map.");
}
if(genericBaseType == BaseType.PROPERTY){
@SuppressWarnings("unchecked")
Class<? extends Element> genericElementType = (Class<? extends Element>) genericType;
genericClassName = TypeMapper.getType(genericElementType);
}
}
}else {
throw new IllegalArgumentException("Type " + clz.getSimpleName() + " not supported as property type");
}
}
public void setType(String type) {
int indexOfMinor = type.indexOf("<");
if(indexOfMinor == -1) {
@ -208,6 +271,7 @@ public class PropertyTypeName {
genericBaseType = null;
genericBaseType = BaseType.getBaseTypeFromString(generic);
}catch (IllegalArgumentException e) {
genericBaseType = BaseType.PROPERTY;
genericClassName = generic;
}
@ -224,7 +288,11 @@ public class PropertyTypeName {
switch (baseType) {
case PROPERTY:
stringBuffer.append(genericClassName);
if(genericClassName != null) {
stringBuffer.append(genericClassName);
}else {
stringBuffer.append(baseType.toString());
}
break;
case SET: case LIST: case MAP:
@ -272,4 +340,22 @@ public class PropertyTypeName {
return genericClassName;
}
@Override
public int hashCode() {
return Objects.hash(baseType, genericBaseType, genericClassName, genericType);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PropertyTypeName other = (PropertyTypeName) obj;
return baseType == other.baseType && genericBaseType == other.genericBaseType
&& Objects.equals(genericClassName, other.genericClassName) && genericType == other.genericType;
}
}

View File

@ -31,6 +31,9 @@ import org.gcube.informationsystem.utils.TypeVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class TypeImpl implements Type {
private static Logger logger = LoggerFactory.getLogger(TypeImpl.class);
@ -114,6 +117,9 @@ public class TypeImpl implements Type {
}
}
if(properties.size()==0) {
properties = null;
}
return properties;
}

View File

@ -1,21 +1,15 @@
package org.gcube.informationsystem.types.impl.properties;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.net.URI;
import java.net.URL;
import java.util.Objects;
import java.util.UUID;
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
import org.gcube.com.fasterxml.jackson.annotation.JsonSetter;
import org.gcube.com.fasterxml.jackson.annotation.JsonTypeName;
import org.gcube.informationsystem.base.reference.Element;
import org.gcube.informationsystem.base.reference.properties.PropertyElement;
import org.gcube.informationsystem.types.OrientDBType;
import org.gcube.informationsystem.types.OrientDBType.OType;
import org.gcube.informationsystem.types.TypeMapper;
import org.gcube.informationsystem.types.PropertyTypeName;
import org.gcube.informationsystem.types.annotations.ISProperty;
import org.gcube.informationsystem.types.impl.TypeImpl;
import org.gcube.informationsystem.types.reference.properties.PropertyDefinition;
import org.gcube.informationsystem.utils.TypeVersion;
import org.slf4j.Logger;
@ -30,7 +24,7 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
*/
private static final long serialVersionUID = -5925314595659292025L;
private static Logger logger = LoggerFactory.getLogger(TypeImpl.class);
private static Logger logger = LoggerFactory.getLogger(PropertyDefinitionImpl.class);
public final static String UUID_REGEX = "^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1}$";
public final static String URI_REGEX = null;
@ -44,12 +38,8 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
private Integer max= null;
private Integer min= null;
private String regexp= null;
private Integer linkedType = null;
private String linkedClass = null;
private Integer type=null;
private PropertyTypeName propertyTypeName = null;
private static String getPropertyNameFromMethodName(Method method){
String name = method.getName();
@ -81,77 +71,41 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
this.readonly = propertyAnnotation.readonly();
if(propertyAnnotation.max()>0) this.max = propertyAnnotation.max();
if(propertyAnnotation.max()>=propertyAnnotation.min() && propertyAnnotation.min()>0) this.min = propertyAnnotation.min();
this.propertyTypeName = new PropertyTypeName(method);
Class<?> clz = method.getReturnType();
logger.trace("Return Type for method {} is {}", method, clz);
if(!propertyAnnotation.regexpr().isEmpty()) this.regexp = propertyAnnotation.regexpr();
logger.trace("Looking for property type {}", method.getReturnType());
@SuppressWarnings("unchecked")
Class<? extends Element> type = (Class<? extends Element>) method.getReturnType();
this.type = OType.PROPERTY.getIntValue();
if(PropertyElement.class.isAssignableFrom(type)){
if(type != PropertyElement.class){
this.linkedClass = TypeMapper.getType(type);
}
}else if (OrientDBType.getTypeByClass(type)!=null) {
this.type = OrientDBType.getTypeByClass(type).getIntValue();
if(this.type > 9 && this.type <= 12){
java.lang.reflect.Type genericReturnType = method.getGenericReturnType();
logger.trace("Generic Return Type {} for method {}", genericReturnType, method);
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);
genericType = t;
}
@SuppressWarnings("unchecked")
Class<? extends Element> genericClass = (Class<? extends Element>) genericType;
OType linkedOType = OrientDBType.getTypeByClass(genericClass);
if(linkedOType!=null){
this.linkedType = linkedOType.getIntValue();
}else{
this.linkedClass = TypeMapper.getType(genericClass);
}
}
if((this.regexp==null || this.regexp.compareTo("")==0 )&& this.type==OType.STRING.getIntValue()){
if(Enum.class.isAssignableFrom(type)){
Object[] constants = type.getEnumConstants();
StringBuilder stringBuilder = new StringBuilder("^(");
for(int i=0; i<constants.length; i++){
stringBuilder.append(constants[i].toString());
if(i<constants.length-1){
stringBuilder.append("|");
}
if(this.regexp==null || this.regexp.compareTo("")==0 ){
if(Enum.class.isAssignableFrom(clz)){
Object[] constants = clz.getEnumConstants();
StringBuilder stringBuilder = new StringBuilder("^(");
for(int i=0; i<constants.length; i++){
stringBuilder.append(constants[i].toString());
if(i<constants.length-1){
stringBuilder.append("|");
}
stringBuilder.append(")$");
this.regexp = stringBuilder.toString();
}
if(UUID.class.isAssignableFrom(type)){
this.regexp = UUID_REGEX;
}
if(URI.class.isAssignableFrom(type)){
this.regexp = URI_REGEX;
}
if(URL.class.isAssignableFrom(type)){
this.regexp = URL_REGEX;
}
if(TypeVersion.class.isAssignableFrom(type)){
this.regexp = TypeVersion.TYPE_VERSION_REGEX;
}
stringBuilder.append(")$");
this.regexp = stringBuilder.toString();
}
if(UUID.class.isAssignableFrom(clz)){
this.regexp = UUID_REGEX;
}
if(URI.class.isAssignableFrom(clz)){
this.regexp = URI_REGEX;
}
if(URL.class.isAssignableFrom(clz)){
this.regexp = URL_REGEX;
}
if(TypeVersion.class.isAssignableFrom(clz)){
this.regexp = TypeVersion.TYPE_VERSION_REGEX;
}
if(this.regexp!=null && this.regexp.compareTo("")==0){
this.regexp = null;
}
} else {
throw new RuntimeException("Type " + type.getSimpleName() + " not reconized");
}
}
@Override
@ -195,26 +149,13 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
}
@Override
public Integer getLinkedType() {
return linkedType;
public String getType() {
return propertyTypeName.toString();
}
@Override
public String getLinkedClass() {
return linkedClass;
}
@Override
public Integer getType() {
return type;
}
@JsonIgnore
public String getTypeStringValue() {
if(type==null){
return null;
}
return OType.values()[type].getStringValue();
@JsonSetter(value = PropertyDefinition.TYPE_PROPERTY)
public void setType(String type) {
this.propertyTypeName = new PropertyTypeName(type);
}
@Override
@ -222,15 +163,12 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
return "Property [name=" + name + ", description=" + description
+ ", mandatory=" + mandatory + ", readonly=" + readonly
+ ", notnull=" + notnull + ", max=" + max + ", min="
+ min + ", regexpr=" + regexp + ", type = " + type
+ " (" + getTypeStringValue() + "), linkedType = " + linkedType + ", linkedClass = "
+ linkedClass + "]";
+ min + ", regexpr=" + regexp + ", type=" + propertyTypeName.toString() + "]";
}
@Override
public int hashCode() {
return Objects.hash(description, linkedClass, linkedType, mandatory, max, min, name, notnull, readonly, regexp,
type);
return Objects.hash(description, mandatory, max, min, name, notnull, propertyTypeName.toString(), readonly, regexp);
}
@Override
@ -242,11 +180,12 @@ public final class PropertyDefinitionImpl implements PropertyDefinition {
if (getClass() != obj.getClass())
return false;
PropertyDefinitionImpl other = (PropertyDefinitionImpl) obj;
return Objects.equals(description, other.description) && Objects.equals(linkedClass, other.linkedClass)
&& Objects.equals(linkedType, other.linkedType) && mandatory == other.mandatory
return Objects.equals(description, other.description) && mandatory == other.mandatory
&& Objects.equals(max, other.max) && Objects.equals(min, other.min) && Objects.equals(name, other.name)
&& notnull == other.notnull && readonly == other.readonly && Objects.equals(regexp, other.regexp)
&& Objects.equals(type, other.type);
&& notnull == other.notnull && Objects.equals(propertyTypeName.toString(), other.propertyTypeName.toString())
&& readonly == other.readonly && Objects.equals(regexp, other.regexp);
}
}

View File

@ -8,6 +8,9 @@ import org.gcube.informationsystem.types.impl.TypeImpl;
import org.gcube.informationsystem.types.reference.properties.PropertyDefinition;
import org.gcube.informationsystem.types.reference.properties.PropertyType;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@JsonTypeName(value = PropertyType.NAME)
public final class PropertyTypeImpl<P extends PropertyElement> extends TypeImpl
implements PropertyType<P> {

View File

@ -29,8 +29,6 @@ public interface PropertyDefinition extends PropertyElement {
public static final String MAX_PROPERTY = "max";
public static final String MIN_PROPERTY = "min";
public static final String REGEX_PROPERTY = "regexp";
public static final String LINKED_TYPE_PROPERTY = "linkedType";
public static final String LINKED_CLASS_PROPERTY = "linkedClass";
public static final String TYPE_PROPERTY = "type";
@ISProperty(name = NAME_PROPERTY, readonly = true, mandatory = true, nullable = false)
@ -57,13 +55,7 @@ public interface PropertyDefinition extends PropertyElement {
@ISProperty(name = REGEX_PROPERTY, readonly = false, mandatory = true, nullable = false)
public String getRegexp();
@ISProperty(name = LINKED_TYPE_PROPERTY, readonly = false, mandatory = true, nullable = false)
public Integer getLinkedType();
@ISProperty(name = LINKED_CLASS_PROPERTY, readonly = false, mandatory = true, nullable = false)
public String getLinkedClass();
@ISProperty(name = TYPE_PROPERTY, readonly = false, mandatory = true, nullable = false)
public Integer getType();
public String getType();
}

View File

@ -29,6 +29,9 @@ import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class SerializationTest {
private static Logger logger = LoggerFactory.getLogger(SerializationTest.class);

View File

@ -1,6 +1,7 @@
package org.gcube.informationsystem.types;
import org.gcube.informationsystem.model.reference.properties.Encrypted;
import org.gcube.informationsystem.model.reference.properties.Header;
import org.gcube.informationsystem.types.PropertyTypeName.BaseType;
import org.junit.Assert;
import org.junit.Test;
@ -11,6 +12,13 @@ public class TypeNameTest {
private static Logger logger = LoggerFactory.getLogger(TypeNameTest.class);
@Test
public void getBaseType() throws Exception {
Class<?> clz = Header.class;
BaseType baseType = PropertyTypeName.getBaseTypeByClass(clz);
logger.debug("{} for class {} is {}", BaseType.class.getSimpleName(), clz, baseType);
}
@Test
public void testValidBaseType() throws Exception {
for(BaseType baseType : BaseType.values()) {

View File

@ -2,6 +2,7 @@ package org.gcube.informationsystem.utils.discovery;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import org.gcube.informationsystem.base.reference.AccessType;
@ -12,11 +13,15 @@ import org.gcube.informationsystem.base.reference.relations.RelationElement;
import org.gcube.informationsystem.types.TypeMapper;
import org.gcube.informationsystem.types.reference.Type;
import org.gcube.informationsystem.types.reference.properties.PropertyType;
import org.gcube.informationsystem.utils.TypeVersion;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ISMDiscoveryTest {
private static final Logger logger = LoggerFactory.getLogger(ISMDiscoveryTest.class);
@ -27,6 +32,47 @@ public class ISMDiscoveryTest {
propertyDiscovery.analizeISM(PropertyType.class);
}
private void checkType(Type type, Type expected) {
Assert.assertTrue(type.getName().compareTo(expected.getName())==0);
Assert.assertTrue(type.getVersion().compareTo(expected.getVersion())==0);
Assert.assertTrue(type.getDescription().compareTo(expected.getDescription())==0);
Assert.assertTrue(type.isAbstract() == expected.isAbstract());
Assert.assertTrue(type.getSuperClasses().containsAll(expected.getSuperClasses()));
Assert.assertTrue(expected.getSuperClasses().containsAll(type.getSuperClasses()));
Map<TypeVersion, String> typeChangelog = type.getChangelog();
Map<TypeVersion, String> typeManagedChangelog = expected.getChangelog();
Assert.assertTrue(typeChangelog.keySet().containsAll(typeManagedChangelog.keySet()));
Assert.assertTrue(typeManagedChangelog.keySet().containsAll(typeChangelog.keySet()));
for(TypeVersion typeVersion : typeChangelog.keySet()) {
Assert.assertTrue(typeChangelog.get(typeVersion).compareTo(typeManagedChangelog.get(typeVersion))==0);
}
if(type.getProperties()!=null || expected.getProperties()!=null) {
Assert.assertTrue(type.getProperties().containsAll(expected.getProperties()));
Assert.assertTrue(expected.getProperties().containsAll(type.getProperties()));
}
}
@Test
public void testcheckType() throws Exception {
Class<? extends Element> clz = PropertyType.class;
Type type = TypeMapper.createTypeDefinition(clz);
Type expected = TypeMapper.createTypeDefinition(PropertyType.class);
checkType(type, expected);
String json = TypeMapper.serializeType(clz);
expected = TypeMapper.deserializeTypeDefinition(json);
checkType(type, expected);
}
@Test
public void test() throws Exception {
@ -54,25 +100,35 @@ public class ISMDiscoveryTest {
Class<R> r) throws Exception {
Type type = TypeMapper.createTypeDefinition(r);
Assert.assertTrue(type.getName().compareTo(r.getSimpleName())==0);
logger.debug("{} {}", r.getSimpleName(), TypeMapper.serializeTypeDefinition(type));
String typeDefinitionJsonString = TypeMapper.serializeTypeDefinition(type);
logger.debug("{} {}", r.getSimpleName(), typeDefinitionJsonString);
Type expected = TypeMapper.deserializeTypeDefinition(typeDefinitionJsonString);
checkType(type, expected);
}
@Override
public <P extends PropertyElement> void managePropertyClass(Class<P> p) throws Exception {
Type type = TypeMapper.createTypeDefinition(p);
Assert.assertTrue(type.getName().compareTo(p.getSimpleName())==0);
logger.debug("{} {}", p.getSimpleName(), TypeMapper.serializeTypeDefinition(type));
String typeDefinitionJsonString = TypeMapper.serializeTypeDefinition(type);
logger.debug("{} {}", p.getSimpleName(), typeDefinitionJsonString);
Type expected = TypeMapper.deserializeTypeDefinition(typeDefinitionJsonString);
checkType(type, expected);
}
@Override
public <E extends EntityElement> void manageEntityClass(Class<E> e) throws Exception {
Type type = TypeMapper.createTypeDefinition(e);
Assert.assertTrue(type.getName().compareTo(e.getSimpleName())==0);
logger.debug("{} {}", e.getSimpleName(), TypeMapper.serializeTypeDefinition(type));
String typeDefinitionJsonString = TypeMapper.serializeTypeDefinition(type);
logger.debug("{} {}", e.getSimpleName(), typeDefinitionJsonString);
Type expected = TypeMapper.deserializeTypeDefinition(typeDefinitionJsonString);
checkType(type, expected);
}
};
ElementSpecilizationDiscovery.manageISM(schemaAction , packages);
}
}

View File

@ -9,7 +9,7 @@
</appender>
<logger name="org.gcube" level="INFO" />
<logger name="org.gcube" level="TRACE" />
<logger name="org.gcube.informationsystem" level="TRACE" />
<logger name="org.gcube.informationsystem.types" level="TRACE" />
<logger name="org.gcube.informationsystem.resourceregistry.dbinitialization" level="INFO" />