/*
* AbstractDescriptionElement.java
*
* Created on July 5, 2001, 3:36 PM
*/
package context.arch.discoverer.component;
import context.arch.discoverer.ComponentDescription;
import context.arch.discoverer.query.comparison.AbstractComparison;
import context.arch.storage.AttributeNameValue;
import context.arch.comm.DataObject;
import context.arch.comm.DataObjects;
/**
* Abstract class to describe an element of a component ({@link ComponentDescription}),
* particularly important for the discovery subscription and query mechanism of the Context Toolkit.
* Subclasses would typically describe elements like host name, port number, non-constant attributes, etc.
* However, elements can also be multi-variate and complex combinations of the information in components, e.g. Euclidean distance of x and y attributes in a Widget.
*
* @param <E> class of return of extractElement(ComponentDescription); i.e. the format of the element that we want to compare
* @param <C1> class of the first comparison argument to AbstractComparison
* @param <C2> class of the second comparison argument to AbstractComparison; this is usually set to be the same as C1.
*
* @author Agathe
* @author Brian Y. Lim
*/
public abstract class AbstractElement<E, C1,C2> {
/** Tag for DataObject version */
public static final String ABSTRACT_DESCRIPTION_ELEMENT = "abDesElem";
/** Tag for the E generics class parameter of AbstractDescriptionElement */
public static final String ABSTRACT_DESCRIPTION_ELEMENT_E = "abDesElemE";
/*
* eClass is a hack to be able to retain knowledge of which class C1 and C2 are at runtime, after generics erasure.
*/
private Class<E> e;
public Class<?> getE() { return e; }
protected Class<C1> c1;
public Class<?> getC1() { return c1; }
protected Class<C2> c2;
public Class<?> getC2() { return c2; }
/** Tag for DataObject version */
public static final String NAME = "abName";
/** Tag for DataObject version */
public static final String VALUE = "abValue";
/** The name of this description element */
protected String elementName;
/** Value of this element */
protected C2 value;
protected AbstractElement(String elementName,
Class<E> e,
Class<C1> c1, Class<C2> c2) {
this.elementName = elementName;
this.e = e;
this.c1 = c1;
this.c2 = c2;
}
protected AbstractElement(String elementName,
Class<E> e,
Class<C1> c1, Class<C2> c2,
C2 value) {
this(elementName, e, c1, c2);
this.value = value;
}
public String getElementName() {
return elementName;
}
public C2 getValue() {
return value;
}
/**
* When passing value through the query system, it could to be converted to a string.
* This method is called to get the string representation.
* Default is to return the toString() method of the getValue().
* If value is a complex object, then this method should be overridden to allow sufficient conversion.
* @return
* @see #getValueDataObject()
*/
public String getValueCodex() {
return value.toString();
}
/**
* When passing value through the query system, it could to be converted to a DataObject.
* This is especially useful for complex value objects. The system would check if this is set first,
* then check {@link #getValueCodex()}. It should be overridden by the subclass to set it if used.
* @return null by default, otherwise a DataObject representing the value if set.
* @see #getValueCodex()
*/
public DataObject getValueDataObject() {
return null;
}
/** Sets the value of this element */
// TODO: change to support storing value as an actual Value, and not a String
// maybe store as setValue(value, value.class) internally
public void setValue(C2 value){
this.value = value;
// if (value instanceof String) {
// ((String)this.value).toLowerCase();
// }
}
/**
* Returns the element from the componentDescription corresponding to this
* description element
*
* @param cd
* @return Object
*/
public abstract E extractElement(ComponentDescription component);
/**
* Returns true if the comparison between the value of this object and the value
* of the corresponding field of the componentDescription returns true
*
* @param componentDescription The component to compare to this object
* @param comparison The comparison element to use
* @return boolean
*/
public abstract Boolean processQueryItem(ComponentDescription component, AbstractComparison<C1,C2> comparison);
/** Returns a printable version */
public String toString(){
return getElementName() + ": " + getValue();
}
/**
* Returns the DataObject version
*/
@SuppressWarnings("serial")
public DataObject toDataObject() {
DataObjects v = new DataObjects();
v.add(new DataObject(NAME, getElementName()));
// v.add(new DataObject(VALUE, getValueCodex())); // TODO: why does this transmit a String and yet expects an Object (see fromDataObject)? Because it eventually needs to be XML serialized; still not generalizable though
// add value as either ValueCodex or DataObject
final DataObject valueDobj = getValueDataObject();
if (valueDobj != null) {
v.add(new DataObject(VALUE, new DataObjects() {{ add(valueDobj); }}));
}
else {
v.add(new DataObject(VALUE, getValueCodex()));
}
v.add(new DataObject(ABSTRACT_DESCRIPTION_ELEMENT_E, getE().getName()));
v.add(new DataObject(AbstractComparison.ABSTRACT_COMPARISON_C1, getC1().getName()));
v.add(new DataObject(AbstractComparison.ABSTRACT_COMPARISON_C2, getC2().getName()));
return new DataObject(ABSTRACT_DESCRIPTION_ELEMENT, v);
}
/**
* Takes a DataObject and return an AbstractDescriptionElement object
*
* @param data
* @return AbstractDescriptionElement
*/
public static AbstractElement<?,?,?> fromDataObject(DataObject data) {
String name = data.getDataObject(NAME).getValue();
String valueCodex = data.getDataObject(VALUE).getValue();
// return AbstractDescriptionElement.fromDataObject(name, value,
// Class.forName(eClassName),
// Class.forName(c1), Class.forName(c2));
if (name.equals(ComponentDescription.ID_ELEMENT)) {
return new IdElement(valueCodex);
}
else if (name.equals(ComponentDescription.PORT_ELEMENT)) {
return new PortElement(Integer.valueOf(valueCodex));
}
else if (name.equals(ComponentDescription.TYPE_ELEMENT) ){
return new TypeElement(valueCodex);
}
else if (name.equals(ComponentDescription.CLASSNAME_ELEMENT)) {
return new ClassnameElement(valueCodex);
}
else if (name.equals(ComponentDescription.HOSTNAME_ELEMENT)) {
return new HostnameElement(valueCodex);
}
/*
* Value for these are AttributeNameValue instead of string
*/
else if (name.equals(ComponentDescription.CONST_ATT_ELEMENT)) {
AttributeNameValue<?> valueAtt = AttributeNameValue.fromDataObject(data.getDataObject(VALUE));
return new ConstantAttributeElement(valueAtt);
}
else if (name.equals(ComponentDescription.NON_CONST_ATT_ELEMENT)) {
AttributeNameValue<?> valueAtt = AttributeNameValue.fromDataObject(data.getDataObject(VALUE));
return new NonConstantAttributeElement(valueAtt);
}
else if (name.equals(ComponentDescription.NON_CONST_ATT_NAME_ELEMENT)) { // just matching for name, and not value
return new NonConstantAttributeNameElement(valueCodex); // value would be name of Attribute
}
else if (name.equals(ComponentDescription.SERVICE_ELEMENT)) {
return new ServiceElement(valueCodex);
}
else if (name.equals(ComponentDescription.SUBSCRIBER_ELEMENT)) {
return new SubscriberElement(valueCodex);
}
else if (name.equals(ComponentDescription.CALLBACK_ELEMENT)) {
return new CallbackElement(valueCodex);
}
// some other element type not defined by the existing library
else {
// use reflections to initialize
String eClassName = data.getDataObject(ABSTRACT_DESCRIPTION_ELEMENT_E).getValue();
String c1 = data.getDataObject(AbstractComparison.ABSTRACT_COMPARISON_C1).getValue();
String c2 = data.getDataObject(AbstractComparison.ABSTRACT_COMPARISON_C2).getValue();
try {
return AbstractElement.fromDataObject(name, valueCodex,
Class.forName(eClassName),
Class.forName(c1), Class.forName(c2));
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
// System.out.println("AbstractDescriptionElement.name = " + name);
// System.out.println("AbstractDescriptionElement.value = " + value);
// return null;
}
}
/**
* Returns the AbstracDescriptionElement corresponding to the specified name
* TODO: still need to work out an extensible way to retrieve a concrete
* AbstractDescriptionElement
*
* @param
* @param
* @return
*/
@SuppressWarnings("unchecked")
protected static <E,C1,C2> AbstractElement<E,C1,C2> fromDataObject(String name, Object value,
Class<E> eClass,
Class<C1> c1, Class<C2> c2) {
// System.err.println("AbstractDescriptionElement.factory");
// System.out.println("\tname = " + name + ", value = " + value + " (" + value.getClass().getSimpleName() + ")");
try {
AbstractElement<E,C1,C2> instance =
(AbstractElement<E,C1,C2>) Class.forName(name)
.getConstructor(
String.class,
Class.class,
Class.class, Class.class,
Object.class)
.newInstance(
name,
eClass,
c1, c2,
value); // TODO: need to parse value from string to actual class
// TODO: may want to support some mapping instead of assuming name is the fully qualified class name; also may not handle classes that need to be remotely loaded
return instance;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public abstract AbstractComparison<C1,C2> getDefaultComparison();
}