package no.met.metadataeditor.dataTypes.attributes;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import no.met.metadataeditor.EditorException;
import no.met.metadataeditor.dataTypes.AttributesMismatchException;
import no.met.metadataeditor.dataTypes.DataAttributeValidationResult;
import no.met.metadataeditor.dataTypes.DataType;
import no.met.metadataeditor.dataTypes.IsAttributeValue;
/**
* Abstract base class for all DataAttributes
*/
public abstract class DataAttribute {
private static final Logger logger = Logger.getLogger(DataAttribute.class.getName());
/**
* @return A mapping between field names and DataType for all attributes
* supported by the class.
*/
public Map<String, DataType> getAttributesSetup(){
return getAttributesSetup(getClass());
}
private Map<String, DataType> getAttributesSetup(Class<? extends Object> inClass){
Map<String, DataType> fields = new HashMap<>();
for (Field f : inClass.getDeclaredFields()) {
if (f.isAnnotationPresent(IsAttributeValue.class)) {
IsAttributeValue ia = f.getAnnotation(IsAttributeValue.class);
DataType type = ia.value();
fields.put(f.getName(), type);
}
}
// attribute might be declared in super class so call this function recursively on the super
// class
if( inClass.getSuperclass() != null ){
Map<String,DataType> superFields = getAttributesSetup(inClass.getSuperclass());
fields.putAll(superFields);
}
return fields;
}
/**
* @return a new Instance of the subtype
*/
public DataAttribute newInstance(){
try {
@SuppressWarnings("unchecked")
Constructor<DataAttribute> c = (Constructor<DataAttribute>) this.getClass().getConstructor(new Class<?>[] {} );
return c.newInstance();
} catch (NoSuchMethodException e) {
String msg = "Did not find constructor with no arguments for class: " + this.getClass().getName();
logger.log(Level.SEVERE, msg, e);
throw new EditorException(msg, e, EditorException.GENERAL_ERROR_CODE);
} catch (SecurityException e) {
String msg = "Security exception finnd constructor with no arguments for class: " + this.getClass().getName();
logger.log(Level.SEVERE, msg, e);
throw new EditorException(msg, e, EditorException.GENERAL_ERROR_CODE);
} catch (InstantiationException e) {
String msg = "Failed to instansiate class: " + this.getClass().getName();
logger.log(Level.SEVERE, msg, e);
throw new EditorException(msg, e, EditorException.GENERAL_ERROR_CODE);
} catch (IllegalAccessException e) {
String msg = "Access exception when accessing empty constructor for class: " + this.getClass().getName();
logger.log(Level.SEVERE, msg, e);
throw new EditorException(msg, e, EditorException.GENERAL_ERROR_CODE);
} catch (IllegalArgumentException e) {
String msg = "Wrong arguments to constructor: " + this.getClass().getName();
logger.log(Level.SEVERE, msg, e);
throw new EditorException(msg, e, EditorException.GENERAL_ERROR_CODE);
} catch (InvocationTargetException e) {
String msg = "Invocation target when making new instance for: " + this.getClass().getName();
logger.log(Level.SEVERE, msg, e);
throw new EditorException(msg, e, EditorException.GENERAL_ERROR_CODE);
}
}
/**
* add a attributes value by a string
* @param attr The name of the attribute to set.
* @param value The value that the attribute should be set to.
* @throws AttributeMismatchException If the class does not have the attribute.
*/
public void addAttribute(String attr, String value){
Map<String,DataType> attributes = getAttributesSetup();
if(!(attributes.containsKey(attr))){
throw new AttributesMismatchException(attr + " is not a valid attribute for " + getClass().getName());
}
addAttribute(attr, value, getClass());
}
private void addAttribute(String attr, String value, Class<? extends Object> inClass){
boolean isSet = false;
try {
for (Field f : inClass.getDeclaredFields()) {
if (!f.getName().equals(attr)) {
continue;
}
if (f.isAnnotationPresent(IsAttributeValue.class)) {
f.set(this, value);
isSet = true;
}
}
// attribute might be declared in super class so call this function recursively on the super
// class
if( !isSet && inClass.getSuperclass() != null ){
addAttribute(attr, value, inClass.getSuperclass());
}
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "Illegal to access the get method of the property: " + attr, e);
throw new EditorException("Illegal to access the get method of the property: " + attr, e, EditorException.GENERAL_ERROR_CODE);
} catch (SecurityException e) {
logger.log(Level.SEVERE, "Security problem to access the get method of the property: " + attr, e);
throw new EditorException("Security problem to access the get method of the property: " + attr, e, EditorException.GENERAL_ERROR_CODE);
}
}
/**
* @param attr The name of the attribute.
* @throws AttributeMismatchException Thrown if the attribute does not exist for the class.
* @return The value of an attribute.
*/
public String getAttribute(String attr) {
Map<String,DataType> attributes = getAttributesSetup();
if(!(attributes.containsKey(attr))){
throw new AttributesMismatchException(attr + " is not a valid attribute for " + getClass().getName());
}
return getAttribute(attr,getClass());
}
public Map<String,String> getAttributes(String... attributeNames){
Map<String,String> attributes = new HashMap<>();
for( String attrName : attributeNames ){
attributes.put(attrName, getAttribute(attrName));
}
return attributes;
}
private String getAttribute(String attribute, Class<? extends Object> inClass) {
String value = null;
boolean isFetched = false;
try {
for (Field f : inClass.getDeclaredFields()) {
if (!f.getName().equals(attribute)) {
continue;
}
if (f.isAnnotationPresent(IsAttributeValue.class)) {
value = "" + f.get(this);
if (value.trim().equalsIgnoreCase("null"))
value = "";
isFetched = true;
}
}
// attribute might be declared in super class so call this function recursively on the super
// class
if( !isFetched && inClass.getSuperclass() != null ){
value = getAttribute(attribute, inClass.getSuperclass());
}
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "Illegal to access the get method of the property: " + attribute, e);
throw new EditorException("Illegal to access the get method of the property: " + attribute, e, EditorException.GENERAL_ERROR_CODE);
} catch (SecurityException e) {
logger.log(Level.SEVERE, "Security problem to access the get method of the property: " + attribute, e);
throw new EditorException("Security problem to access the get method of the property: " + attribute, e, EditorException.GENERAL_ERROR_CODE);
}
return value;
}
public Map<String, DataAttributeValidationResult> validateAttributes(){
Map<String, DataType> attributes = getAttributesSetup();
Map<String,DataAttributeValidationResult> result = new HashMap<>();
for( Map.Entry<String, DataType> attr : attributes.entrySet() ){
String attrName = attr.getKey();
String value = getAttribute(attrName);
DataAttributeValidationResult r = attr.getValue().validate(value);
result.put(attrName, r);
}
return result;
}
/**
* @return Returns true if two data attributes are of the same type and all
* their attribute values are the same.
*/
@Override
public boolean equals(Object o){
if( !(o instanceof DataAttribute)){
return false;
}
if( !( getClass().isInstance(o))){
return false;
}
DataAttribute that = (DataAttribute) o;
Map<String,DataType> attributes = getAttributesSetup(getClass());
for( String attributeName : attributes.keySet()){
String thisValue = this.getAttribute(attributeName);
String thatValue = that.getAttribute(attributeName);
if( !thisValue.equals(thatValue)){
return false;
}
}
return true;
}
}