package context.arch.storage; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import context.arch.comm.DataObject; import context.arch.comm.DataObjects; /** * This class is a container for a group of related attributes. * Attributes can be added, removed, and found in the container. * key = attribute name, value = attribute * * TODO: make type safe by either supporting Attribute or AttributeNameValue, but not both in a single Vector? */ public class Attributes extends ConcurrentHashMap<String, Attribute<?>> { private static final long serialVersionUID = 5240091799907839312L; /** * Connector for nested attributes */ public static final char SEPARATOR = '.'; /** * Connector for nested attributes - String */ public static final String SEPARATOR_STRING = new Character(SEPARATOR).toString(); /** * Tag for attributes */ public static final String ATTRIBUTES = "attributes"; public Attributes() { super(); } public Attributes(Attributes attributes) { super(attributes); } public Attributes(Collection<? extends Attribute<?>> attributes) { super(); for (Attribute<?> att : attributes) { add(att); } } /** * Constructor that takes a DataObject as a parameter. The DataObject * is expected to contain an <ATTRIBUTES> tag. * The constructor stores the encoded data in an Attributes object. * * @param data DataObject that contains the attribute name (and possibly type) info */ public static Attributes fromDataObject(DataObject data) { data = data.getDataObject(ATTRIBUTES); if (data == null) { return null; } Attributes atts = new Attributes(); // System.out.println("Attributes.fromDataObject atts : " + atts); for (DataObject dobj : data.getChildren()) { String dobjName = dobj.getName(); // System.out.println("dobjName = " + dobjName + " : value = " + dobj.getValue()); if (dobjName.equals(Attribute.ATTRIBUTE)) { Attribute<?> att = Attribute.fromDataObject(dobj); // System.out.println("Attributes.fromDataObject (Attribute)att : " + att); atts.add(att); } else if (dobjName.equals(AttributeNameValue.ATTRIBUTE_NAME_VALUE)) { AttributeNameValue<?> att = AttributeNameValue.fromDataObject(dobj); // System.out.println("Attributes.fromDataObject (AttributeNameValue)att : " + att); atts.add(att); } else if (dobjName.equals(AttributeNameValuePin.ATTRIBUTE_NAME_VALUE_PIN)) { AttributeNameValuePin<?> att = AttributeNameValuePin.fromDataObject(dobj); atts.add(att); } } return atts; } /** * Converts to a DataObject. * * @return Attributes object converted to an <ATTRIBUTES> DataObject */ public DataObject toDataObject() { DataObjects v = new DataObjects(); for (Attribute<?> att : this.values()) { // System.out.println("Attributes.toDataObject att : " + att); v.add(att.toDataObject()); } return new DataObject(ATTRIBUTES, v); } /** * Add an attribute, but may replace an old one if there existed one with the same name. * @param the attribute to add * @return the replaced attribute */ public Attribute<?> add(Attribute<?> att) { return super.put(att.getName(), att); } /** * Convenience method to add an Attribute. * @param <T> * @param name * @param type * @return the replaced attribute if an old one existed */ public <T extends Comparable<? super T>> Attribute<?> addAttribute(String name, Class<T> type) { return add(new Attribute<T>(name, type)); } /** * Convenience method to add an AttributeNameValue; type is implicit in the class of T * @param <T> the type for value * @param name * @param value * @return the replaced attribute if an old one existed */ public <T extends Comparable<? super T>> Attribute<?> addAttribute(String name, T value) { return add(new AttributeNameValue<T>(name, value)); } /** * Checks whether this contains an Attribute with a specific name. * @param attributeName the name to find an Attribute with * @return true if there exists an Attribute with name.equals(attributeName) */ public boolean containsName(String attributeName) { return this.containsKey(attributeName); } /** * This method takes an Attributes containing the list of attributes * (names) wanted and it filters all the rest out from this Attributes * object. * * @param atts Attributes object containing the attributes to return * @return filtered Attributes object */ public Attributes getSubset(Attributes filterAtts) { if (filterAtts.isEmpty()) { return this; } Attributes subset = new Attributes(); for (Attribute<?> filterAtt : filterAtts.values()) { //System.out.println("filterAtt = " + filterAtt); Attribute<?> localAtt = get(filterAtt.getName()); if (localAtt != null) { if (filterAtt instanceof AttributeNameValuePin<?>) { if (localAtt instanceof AttributeNameValuePin<?>) { Object filterPin = ((AttributeNameValuePin<?>)filterAtt).getPin(); Object localPin = ((AttributeNameValuePin<?>)localAtt).getPin(); if (localPin != null && localPin.equals(filterPin)) { subset.add(localAtt); } } } // if subAtt is an AttributeNameValue, filter with the value as well else if (filterAtt instanceof AttributeNameValue<?>) { if (localAtt instanceof AttributeNameValue<?>) { Object filterValue = ((AttributeNameValue<?>)filterAtt).getValue(); Object localValue = ((AttributeNameValue<?>)localAtt).getValue(); // System.out.println("filterValue = " + filterValue + " | " + filterAtt); // System.out.println("localValue = " + localValue + " | " + localAtt); if (localValue != null && localValue.equals(filterValue)) { subset.add(localAtt); } } } else { subset.add(localAtt); } } } // System.out.println(); return subset; } @SuppressWarnings("unchecked") public <T extends Comparable<? super T>> T getAttributeValue(String attName) { Attribute<?> att = super.get(attName); if (att != null && att instanceof AttributeNameValue<?>) { return ((AttributeNameValue<T>) att).getValue(); } else { return null; } } /** * Compare this Attributes with another. * It only checks for each Attribute in this (i.e. via their name), and compares with the value in other. * This is an asymmetric comparison; be careful the proper direction to use it. * @param other * @return Attributes containing any Attribute in other that is different in value in this; the value assigned is that belonging to the original collection in this. */ public <T extends Comparable<? super T>> Attributes compare(Attributes other) { Attributes diff = new Attributes(); /* * Iterate through this */ for (Attribute<?> att : this.values()) { String name = att.getName(); T val = this.getAttributeValue(name); // if different value, then add T otherVal = other.getAttributeValue(name); if (val != null && !val.equals(otherVal)) { diff.add(AttributeNameValue.instance(name, val)); } } return diff; } }