package context.arch.storage;
import context.arch.comm.DataObject;
import context.arch.comm.DataObjects;
import context.arch.discoverer.Discoverer;
import context.arch.discoverer.component.AbstractElement;
/**
* This class is a container for an attribute name, value and type.
* TODO: how to handle characteristics such as: possible nominal values, min/max range?
*/
public class AttributeNameValue<T extends Comparable<? super T>> extends Attribute<T> {
protected T value;
/**
* Tag for an attribute name/value pair
*/
public static final String ATTRIBUTE_NAME_VALUE = "attributeNameValue";
/** Tag for an attribute name */
public static final String ATTRIBUTE_NAME = "attributeName";
/** Tag for an attribute value */
public static final String ATTRIBUTE_VALUE = "attributeValue";
protected long lastModifiedTimestamp;
/** ID of the component that last modified it */
protected String lastModifiedById;
protected String lastModifiedByHostName;
protected int lastModifiedByPort;
/** Tag for an attribute lastModifiedTimestamp */
public static final String ATTRIBUTE_LAST_MODIFIED_TIMESTAMP = "attributeLastModifiedTimestamp";
/** Tag for an attribute lastModifiedById,
* which is the ID of the component that last modified it */
public static final String ATTRIBUTE_LAST_MODIFIED_BY_ID = "attributeLastModifiedById";
public static final String ATTRIBUTE_LAST_MODIFIED_BY_HOSTNAME = "attributeLastModifiedByHostName";
public static final String ATTRIBUTE_LAST_MODIFIED_BY_PORT = "attributeLastModifiedByPort";
/**
* Constructor that takes only a name
*
* @param name Name of attribute to store
*/
public AttributeNameValue(String name, Class<T> type) {
super(name, type);
}
@SuppressWarnings("unchecked")
public AttributeNameValue(String name, T value) {
this(name, (Class<T>)value.getClass(), value);
}
/**
* Constructor that takes a name, value and type
*
* @param name Name of attribute to store
* @param value Value of attribute to store
* @param type Datatype of attribute to store
*/
public AttributeNameValue(String name, Class<T> type, T value) {
super(name, type);
this.value = value;
}
public AttributeNameValue(String name, Class<T> type, T value, Attributes subAttributes) {
this(name, type, value);
this.subAttributes = subAttributes;
}
/**
* Allows to dynamically (at runtime) instantiate a AttributeNameValue with the
* corresponding generic type.
* @param <T>
* @param name
* @param value
* @return
*/
public static <T extends Comparable<? super T>> AttributeNameValue<T> instance(String name, T value) {
return new AttributeNameValue<T>(name, value);
}
public static <T extends Comparable<? super T>> AttributeNameValue<T> instance(String name, Class<T> type) {
return new AttributeNameValue<T>(name, type);
}
/**
* Use this to create a AttributeNameValue where the type of value is determined dynamically at runtime.
* @param <T>
* @param name
* @param type
* @param value
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> AttributeNameValue<T> instance(String name, Class<T> type, Comparable<? super T> value) {
return new AttributeNameValue<T>(name, type, (T) value);
}
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> AttributeNameValue<T> instance(String name, Class<T> type, Comparable<? super T> value, Attributes subAttrs) {
return new AttributeNameValue<T>(name, type, (T) value, subAttrs);
}
/**
* Constructor that takes a DataObject as input. The DataObject
* must have <ATTRIBUTE_NAME_VALUE> as its top-level tag
*
* @param attribute DataObject containing the attribute info
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> AttributeNameValue<T> fromDataObject(DataObject data) {
String name = data.getDataObject(ATTRIBUTE_NAME_VALUE).getValue();
String tClassName = data.getDataObject(ATTRIBUTE_TYPE).getValue();
try {
return fromDataObject(data, name, (Class<T>) Class.forName(tClassName));
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
protected static <T extends Comparable<? super T>> AttributeNameValue<T> fromDataObject(DataObject data, String name, Class<T> type) {
try {
Attributes subAttrs = Attributes.fromDataObject(data);
// invoke .valueOf(String) method to parse value from string
String strValue = data.getDataObject(ATTRIBUTE_VALUE).getValue();
if (strValue == null || strValue.equals("null")) { // e.g. because value not set
return null;
}
T value = valueOf(type, strValue);
// return (AttributeNameValue<T>) AttributeNameValue.class
// .getConstructor(String.class, Class.class, Object.class, Attributes.class)
// .newInstance(name, type, value, subAttrs);
return AttributeNameValue.instance(name, type, value, subAttrs);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Convenience method to use reflection to call the valueOf static method of the class of type
* to get a corresponding object representation from String strValue.
* @param type
* @param strValue
* @return Object but actually instantiated to its respective type
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> T valueOf(Class<T> type, String strValue) {
T value = null;
if (type.equals(String.class)) { // special case that String does not have a valueOf(String) method
value = (T) strValue;
}
else {
try {
value = (T) type.getMethod(Attribute.METHOD_VALUE_OF, String.class)
.invoke(null, strValue);
} catch (Exception e) {
e.printStackTrace();
}
}
return value;
}
public T valueOf(String strValue) {
return valueOf(this.getType(), strValue);
}
/**
* Converts this object to a DataObject.
*
* @return AttributeNameValue object converted to an <ATTRIBUTE_NAME_VALUE> DataObject
*/
public DataObject toDataObject() {
DataObject dobj = super.toDataObject();
// change name value for data object
dobj.setName(ATTRIBUTE_NAME_VALUE);
/*
* Extend data object from Attribute
*/
DataObjects children = dobj.getChildren();
children.add(new DataObject(ATTRIBUTE_VALUE, String.valueOf(getValue())));
children.add(new DataObject(ATTRIBUTE_LAST_MODIFIED_TIMESTAMP, String.valueOf(lastModifiedTimestamp)));
children.add(new DataObject(ATTRIBUTE_LAST_MODIFIED_BY_ID, lastModifiedById));
children.add(new DataObject(ATTRIBUTE_LAST_MODIFIED_BY_HOSTNAME, lastModifiedByHostName));
children.add(new DataObject(ATTRIBUTE_LAST_MODIFIED_BY_PORT, String.valueOf(lastModifiedByPort)));
return dobj;
}
/**
* Sets the value of an attribute
*
* @param value of the attribute to store
* @param true if value is the correct type of this Attribute; false otherwise and does not set if failed
*/
@SuppressWarnings("unchecked")
public boolean setValue(Object value) {
if (type.isAssignableFrom(value.getClass())) {
this.value = (T) value;
return true;
}
return false;
}
/**
* Copy value from source attribute if their types match.
* @param source
* @return true if the copy was successful.
*/
@SuppressWarnings("unchecked")
public boolean copyValue(AttributeNameValue<?> source) {
if (source.getType().equals(type)) {
setValue((T)source.getValue());
return true;
}
else {
return false;
}
}
/**
* Returns the value of the stored attribute
*
* @return value of the stored attribute
*/
public T getValue() {
return value;
}
public long getLastModifiedTimestamp() {
return lastModifiedTimestamp;
}
public String getLastModifiedById() {
return lastModifiedById;
}
public String getLastModifiedByHostName() {
return lastModifiedByHostName;
}
public int getLastModifiedByPort() {
return lastModifiedByPort;
}
public void setLastModified(long timestamp, String byId, String byHostName, int byPort) {
this.lastModifiedTimestamp = timestamp;
this.lastModifiedById = byId;
this.lastModifiedByHostName = byHostName;
this.lastModifiedByPort = byPort;
}
/**
* A printable version of this class.
*
* @return String version of this class
*/
public String toString() {
// return new String("[name="+getName()+",value="+getValue()+",type="+getType()+"]");
// return "\n\t(" + getType() + ", by=" + getLastModifiedById() + ", at=" + getLastModifiedTimestamp() + ")\t" + getName() + " = " + getValue();
return super.toString() +
",value=" + getValue() +
",by=" + getLastModifiedById() +
",time=" + getLastModifiedTimestamp();
}
@Override
public AttributeNameValue<T> clone() {
return new AttributeNameValue<T>(name, type, value, subAttributes);
}
@Override
public AttributeNameValue<T> cloneWithNewName(String name) {
return new AttributeNameValue<T>(name, type, value, subAttributes);
}
public AttributeNameValue<T> cloneWithNewValue(Object value) {
AttributeNameValue<T> att = new AttributeNameValue<T>(name, type, null, subAttributes);
att.setValue(value);
return att;
}
/**
* Convert to value codex representation that is used by the Discoverer component model, for querying.
* Format: name+type+value, where '+' would be the URL encoded form of space ' '.
* @see #fromValueCodex(String)
* @see AbstractElement#fromDataObject(DataObject)
* @return
*/
@Override
public String toValueCodex() {
return super.toValueCodex() + Discoverer.FIELD_SEPARATOR + value;
}
/**
* Create a AttributeNameValue (shallow with no sub-attributes) from the value codex representation
* that is used by the Discoverer component model, for querying.
* Format: name+type+value, where '+' would be the URL encoded form of space ' '.
* @param valueCodex
* @return
* @see #toValueCodex()
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> AttributeNameValue<T> fromValueCodex(String valueCodex) {
String[] args = valueCodex.split("\\+"); // "name+type+value" -> {name, type, value}
String name = args[0];
String typeClassname = args[1];
String strValue = args[2];
try {
Class<T> type = (Class<T>) Class.forName(typeClassname);
T value = valueOf(type, strValue);
return AttributeNameValue.instance(name, type, value);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
/**
* Compares the values of this AttributeNameValue and other.
* @param other
* @return null if this and other are not compatible (i.e. different names or types); otherwise return value.compareTo(other.value)
*/
@SuppressWarnings("unchecked")
public Integer compareToValue(AttributeNameValue<?> other) {
// check if names and types are the same, otherwise, invalid
if (!this.name.equals(other.name) ||
!this.type.equals(other.type)
) {
return null;
}
T otherValue = (T) other.value;
return this.value.compareTo(otherValue);
}
}