diff --git a/src/main/java/org/gcube/documentstore/records/Record.java b/src/main/java/org/gcube/documentstore/records/Record.java index 1663f78..006d9dd 100644 --- a/src/main/java/org/gcube/documentstore/records/Record.java +++ b/src/main/java/org/gcube/documentstore/records/Record.java @@ -84,19 +84,19 @@ public interface Record extends Comparable, Serializable { * not affect the object * @return a Map containing the properties */ - public Map> getResourceProperties(); + public Map getResourceProperties(); /** * Set all resource-specific properties, replacing existing ones */ - public void setResourceProperties(Map> resourceSpecificProperties) throws InvalidValueException; + public void setResourceProperties(Map resourceSpecificProperties) throws InvalidValueException; /** * Return the value of the given resource property. * @param key the key of the requested property * @return the value of the given resource property */ - public Comparable getResourceProperty(String key); + public Serializable getResourceProperty(String key); /** * Set the value of the given resource property. @@ -105,8 +105,7 @@ public interface Record extends Comparable, Serializable { * @param key the key of the requested property * @param value the value of the given resource property */ - public void setResourceProperty(String key, Comparable value) throws InvalidValueException; - + public void setResourceProperty(String key, Serializable value) throws InvalidValueException; /** * Validate the Resource Record. diff --git a/src/main/java/org/gcube/documentstore/records/RecordUtility.java b/src/main/java/org/gcube/documentstore/records/RecordUtility.java index 2d2777c..a8c7295 100644 --- a/src/main/java/org/gcube/documentstore/records/RecordUtility.java +++ b/src/main/java/org/gcube/documentstore/records/RecordUtility.java @@ -131,7 +131,7 @@ public abstract class RecordUtility { * } */ - protected static Map> getMapFromString(String serializedMap){ + protected static Map getMapFromString(String serializedMap){ /* Checking line sanity */ if(!serializedMap.startsWith(LINE_FREFIX) && !serializedMap.endsWith(LINE_SUFFIX)){ return null; @@ -141,7 +141,7 @@ public abstract class RecordUtility { serializedMap = serializedMap.replace(LINE_FREFIX, ""); serializedMap = serializedMap.replace(LINE_SUFFIX, ""); - Map> map = new HashMap>(); + Map map = new HashMap(); String[] pairs = serializedMap.split(KEY_VALUE_PAIR_SEPARATOR); for (int i=0;i value = keyValue[1].trim(); + Serializable value = keyValue[1].trim(); map.put(key, value); } @@ -165,7 +165,7 @@ public abstract class RecordUtility { * @throws Exception if deserialization fails */ public static Record getRecord(String serializedMap) throws Exception { - Map> map = getMapFromString(serializedMap); + Map map = getMapFromString(serializedMap); return getRecord(map); } @@ -175,7 +175,7 @@ public abstract class RecordUtility { * @return the Record * @throws Exception if deserialization fails */ - public static Record getRecord(Map> recordMap) throws Exception { + public static Record getRecord(Map recordMap) throws Exception { String className = (String) recordMap.get(Record.RECORD_TYPE); boolean aggregated = false; diff --git a/src/main/java/org/gcube/documentstore/records/aggregation/AggregationUtility.java b/src/main/java/org/gcube/documentstore/records/aggregation/AggregationUtility.java index 5dc3614..604a017 100644 --- a/src/main/java/org/gcube/documentstore/records/aggregation/AggregationUtility.java +++ b/src/main/java/org/gcube/documentstore/records/aggregation/AggregationUtility.java @@ -3,6 +3,7 @@ */ package org.gcube.documentstore.records.aggregation; +import java.io.Serializable; import java.util.Calendar; import java.util.HashSet; import java.util.Set; @@ -82,12 +83,20 @@ public class AggregationUtility> { @SuppressWarnings("unchecked") public boolean isAggregable(T record) { for(String field : aggregationFields){ - @SuppressWarnings("rawtypes") - Comparable recordValue = record.getResourceProperty(field); - @SuppressWarnings("rawtypes") - Comparable thisValue = t.getResourceProperty(field); - if(recordValue.compareTo(thisValue)!=0){ - return false; + Serializable recordValue = record.getResourceProperty(field); + Serializable thisValue = t.getResourceProperty(field); + if(recordValue instanceof Comparable && thisValue instanceof Comparable){ + @SuppressWarnings("rawtypes") + Comparable recordValueComparable = (Comparable) recordValue; + @SuppressWarnings("rawtypes") + Comparable thisValueComparable = (Comparable) thisValue; + if(recordValueComparable.compareTo(thisValueComparable)!=0){ + return false; + } + }else{ + if(recordValue.hashCode()!=this.hashCode()){ + return false; + } } } return true; diff --git a/src/main/java/org/gcube/documentstore/records/implementation/AbstractRecord.java b/src/main/java/org/gcube/documentstore/records/implementation/AbstractRecord.java index 021535b..99551ab 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/AbstractRecord.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/AbstractRecord.java @@ -48,9 +48,10 @@ public abstract class AbstractRecord implements Record { protected static final String RECORD_TYPE = Record.RECORD_TYPE; /** resource-specific properties */ - protected Map> resourceProperties; + protected Map resourceProperties; protected Map> validation; + protected Map> computation; protected Set requiredFields; protected Set computedFields; @@ -78,9 +79,14 @@ public abstract class AbstractRecord implements Record { } List fieldValidators = new ArrayList(); validation.put(keyString, fieldValidators); + + List fieldComputations = new ArrayList(); + computation.put(keyString, fieldComputations); + for (Annotation annotation : field.getAnnotations()){ - if (annotation.annotationType().isAnnotationPresent(FieldDecorator.class)){ - Class managedClass = ((FieldDecorator)annotation.annotationType().getAnnotation(FieldDecorator.class)).action(); + Class annotationType = annotation.annotationType(); + if (annotationType.isAnnotationPresent(FieldDecorator.class)){ + Class managedClass = ((FieldDecorator)annotationType.getAnnotation(FieldDecorator.class)).action(); FieldAction validator; try { validator = managedClass.newInstance(); @@ -90,14 +96,23 @@ public abstract class AbstractRecord implements Record { } fieldValidators.add(validator); } - if(annotation.annotationType().isAssignableFrom(RequiredField.class)){ + if(annotationType.isAssignableFrom(RequiredField.class)){ requiredFields.add(keyString); } - if(annotation.annotationType().isAssignableFrom(AggregatedField.class)){ + if(annotationType.isAssignableFrom(AggregatedField.class)){ aggregatedFields.add(keyString); } - if(annotation.annotationType().isAssignableFrom(ComputedField.class)){ + if(annotationType.isAssignableFrom(ComputedField.class)){ computedFields.add(keyString); + Class managedClass = ((ComputedField) annotation).action(); + FieldAction computeAction; + try { + computeAction = managedClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + logger.error("{} {}", keyString, annotation, e); + continue; + } + fieldComputations.add(computeAction); } } field.setAccessible(defaultAccessibility); @@ -131,10 +146,11 @@ public abstract class AbstractRecord implements Record { */ protected void init() { this.validation = new HashMap>(); + this.computation = new HashMap>(); this.requiredFields = new HashSet(); this.aggregatedFields = new HashSet(); this.computedFields = new HashSet(); - this.resourceProperties = new HashMap>(); + this.resourceProperties = new HashMap(); initializeValidation(); } @@ -146,7 +162,7 @@ public abstract class AbstractRecord implements Record { this.resourceProperties.put(CREATION_TIME, calendar.getTimeInMillis()); } - public AbstractRecord(Map> properties) throws InvalidValueException { + public AbstractRecord(Map properties) throws InvalidValueException { init(); setResourceProperties(properties); if(this instanceof AggregatedRecord){ @@ -217,24 +233,24 @@ public abstract class AbstractRecord implements Record { * {@inheritDoc} */ @Override - public Map> getResourceProperties() { - return new HashMap>(this.resourceProperties); + public Map getResourceProperties() { + return new HashMap(this.resourceProperties); } /** * {@inheritDoc} */ @Override - public void setResourceProperties(Map> properties) throws InvalidValueException { - Map> validated = validateProperties(properties); - this.resourceProperties = new HashMap>(validated); + public void setResourceProperties(Map properties) throws InvalidValueException { + Map validated = validateProperties(properties); + this.resourceProperties = new HashMap(validated); } /** * {@inheritDoc} */ @Override - public Comparable getResourceProperty(String key) { + public Serializable getResourceProperty(String key) { return this.resourceProperties.get(key); } @@ -242,8 +258,8 @@ public abstract class AbstractRecord implements Record { * {@inheritDoc} */ @Override - public void setResourceProperty(String key, Comparable value) throws InvalidValueException { - Comparable checkedValue = validateField(key, value); + public void setResourceProperty(String key, Serializable value) throws InvalidValueException { + Serializable checkedValue = validateField(key, value); if(checkedValue == null){ this.resourceProperties.remove(key); }else{ @@ -314,11 +330,11 @@ public abstract class AbstractRecord implements Record { return timestampToCalendar(millis); } - protected Comparable validateField(String key, Comparable value) throws InvalidValueException { + protected Serializable validateField(String key, Serializable value) throws InvalidValueException { if(key == null){ throw new InvalidValueException("The key of property to set cannot be null"); } - Comparable checkedValue = value; + Serializable checkedValue = value; List fieldValidators = validation.get(key); if(fieldValidators!=null){ for(FieldAction fieldValidator : fieldValidators){ @@ -339,10 +355,30 @@ public abstract class AbstractRecord implements Record { return checkedValue; } - protected Map> validateProperties(Map> properties) throws InvalidValueException{ - Map> validated = new HashMap>(); + protected void computeField(String key) throws InvalidValueException { + if(key == null){ + throw new InvalidValueException("The key of property to set cannot be null"); + } + Serializable computedValue = null; + List fieldComputations = computation.get(key); + if(fieldComputations!=null){ + for(FieldAction fieldValidator : fieldComputations){ + try { + computedValue = fieldValidator.validate(key, null, this); + this.resourceProperties.put(key, computedValue); + } catch (InvalidValueException e) { + logger.error(String.format("Unable to calculate the field with key %s", key)); + throw e; + } + } + } + } + + + protected Map validateProperties(Map properties) throws InvalidValueException{ + Map validated = new HashMap(); for(String key : properties.keySet()){ - Comparable value = properties.get(key); + Serializable value = properties.get(key); validated.put(key, validateField(key, value)); } return validated; @@ -353,6 +389,10 @@ public abstract class AbstractRecord implements Record { */ @Override public void validate() throws InvalidValueException { + for(String key : this.computedFields){ + computeField(key); + } + validateProperties(this.resourceProperties); Set notPresentProperties = new HashSet(); for(String key : this.requiredFields){ @@ -384,8 +424,8 @@ public abstract class AbstractRecord implements Record { */ @Override public int compareTo(Record record) { - Set>> thisSet = this.resourceProperties.entrySet(); - Set>> recordSet = record.getResourceProperties().entrySet(); + Set> thisSet = this.resourceProperties.entrySet(); + Set> recordSet = record.getResourceProperties().entrySet(); if(thisSet.size() != recordSet.size()){ return thisSet.size() - recordSet.size(); } diff --git a/src/main/java/org/gcube/documentstore/records/implementation/ComputedField.java b/src/main/java/org/gcube/documentstore/records/implementation/ComputedField.java index e130115..f81956b 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/ComputedField.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/ComputedField.java @@ -13,5 +13,6 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ComputedField { - + + Class action(); } diff --git a/src/main/java/org/gcube/documentstore/records/implementation/FieldAction.java b/src/main/java/org/gcube/documentstore/records/implementation/FieldAction.java index 25aa907..f700c4a 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/FieldAction.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/FieldAction.java @@ -24,6 +24,6 @@ public interface FieldAction { * @throws InvalidValueException if the validation or the eventual * conversion fails */ - public Comparable validate(String key, Comparable value, Record record) throws InvalidValueException; + public Serializable validate(String key, Serializable value, Record record) throws InvalidValueException; } diff --git a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotEmptyValidator.java b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotEmptyValidator.java index d806b07..1dcb9af 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotEmptyValidator.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotEmptyValidator.java @@ -28,7 +28,7 @@ public class NotEmptyValidator implements FieldAction { * {@inheritDoc} */ @Override - public Comparable validate(String key, Comparable value, Record record) throws InvalidValueException { + public Serializable validate(String key, Serializable value, Record record) throws InvalidValueException { try{ if(isValid((Serializable) value)){ return value; diff --git a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotNullValidator.java b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotNullValidator.java index dac3e8c..f7b1bb5 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotNullValidator.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/NotNullValidator.java @@ -14,7 +14,7 @@ public class NotNullValidator implements FieldAction { * {@inheritDoc} */ @Override - public Comparable validate(String key, Comparable value, Record record) throws InvalidValueException { + public Serializable validate(String key, Serializable value, Record record) throws InvalidValueException { if(value!=null){ return value; } diff --git a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidIntegerValidator.java b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidIntegerValidator.java index 077204a..0ffab9a 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidIntegerValidator.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidIntegerValidator.java @@ -15,7 +15,7 @@ public class ValidIntegerValidator implements FieldAction { * {@inheritDoc} */ @Override - public Comparable validate(String key, Comparable value, Record record) throws InvalidValueException { + public Serializable validate(String key, Serializable value, Record record) throws InvalidValueException { if(value instanceof Integer){ return value; } diff --git a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidLongValidator.java b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidLongValidator.java index 43ae4dc..8ed1703 100644 --- a/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidLongValidator.java +++ b/src/main/java/org/gcube/documentstore/records/implementation/validations/validators/ValidLongValidator.java @@ -15,7 +15,7 @@ public class ValidLongValidator implements FieldAction { * {@inheritDoc} */ @Override - public Comparable validate(String key, Comparable value, Record record) throws InvalidValueException { + public Serializable validate(String key, Serializable value, Record record) throws InvalidValueException { if(value instanceof Long){ return value; }