package context.arch.comm; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * This class stores the data used for sending messages between components. * * Dissociated children from value; they are now separate entities and concepts. * * TODO: consider using XStream for data binding and serialization. * * @author Anind K. Dey * @author Brian Y. Lim */ public class DataObject { private String name; private String value; private DataObjects children = new DataObjects(); /* these two fields seem to be used for some internal chaining mechanism * @see addElement, closeElement * TODO: so where does this chaining matter? Maybe SAX_XMLDecoder */ private DataObject currentObject; private DataObject parent; /** * Debug flag. Set to true to see debug messages. */ public static boolean DEBUG = false; /** * Basic constructor. Sets up necessary internal variables. */ public DataObject() { currentObject = this; currentObject.parent = null; } /** * Constructor that sets the name of the DataObject element * * @param name Name of the DataObject element */ public DataObject(String name) { this(); this.name = name; } /** * Constructor that sets the name of the DataObject element and a single * value for the element * * @param name Name of the DataObject element * @param value Value of the DataObject element */ public DataObject(String name, String value) { this(name); if (name != null) { this.value = value; } } /** * Constructor that sets the name of the DataObject element and a vector * of values for the element * * @param name Name of the DataObject element * @param children Vector of values for the DataObject element */ public DataObject(String name, DataObjects children) { this(name); this.value = null; this.children = children; children.setParent(this); } public DataObject(String name, String value, DataObjects children) { this(name); this.value = value; this.children = children; children.setParent(this); } /** * Returns the name of the DataObject element * * @return Name of the DataObject element */ public String getName() { return name; } /** * Returns the values for the DataObject element * @return value in string form; may need to be reconstituted with T.valueOf(String), where T is the type class */ public String getValue() { return value; } /** * Sets the name of the DataObject element * @param name of the DataObject element */ public void setName(String name) { this.name = name; } /** * Counts the children of the current DataObject * * @return int the number of children */ protected int countChildren() { return children.size(); } /** * Note similarity to getDataObject, but maybe this doesn't do a recursive search * TODO: so why would this be called instead of getDataObject?? --Brian */ public DataObject getChild(String name) { for (DataObject child : children) { if (name.equals(child.getName())) { return child; } } return null; } /** * Returns the children of the current DataObject * * @return Vector a vector containing the children */ public DataObjects getChildren() { return children; } /** * Returns the DataObject element/sub-element with the specified name. * Could return itself or any recursively selected child. * If multiple children have the same name, then only the first child is returned. * @see getDataObjects to retrieve multiple children with name. * * @param name Name of the element to return * @return DataObject with the specified name or null, if not found * */ public DataObject getDataObject(String name) { // object's name matches, so return it if (this.name.equals(name)) { return this; } // not found, so recursively search among children for (DataObject child : children) { // iterate through each child DataObject result = child.getDataObject(name); if (result != null) { return result; } } // didn't find anything return null; } /** * Returns all (possibly multiple) the DataObject elements/sub-elements with the specified name. * Could include itself or any recursively selected child. * * @param name Name of the element to return * @return DataObject with the specified name or null, if not found * */ public List<DataObject> getDataObjects(String name) { List<DataObject> result = new ArrayList<DataObject>(); // object's name matches, so add it if (this.name.equals(name)) { result.add(this); } // so recursively search among children for (DataObject child : children) { // iterate through each child to add more // note that child may add an empty list result.addAll(child.getDataObjects(name)); } return result; } /** * Returns the Nth DataObject element/sub-element with the specified name * NB: we assume the current DataObject has 1 level of children. Thus, * getNthDataObject (x, 1) is not equivalent to getDataObject (x) * I'll fix this later. --DS * * Returns the Nth DataObject element/sub-element with the specified name * NB: Solved the problem with the previous method which considered only 1 level of children * ~~Kanupriya * * @param name Name of the element to return * @return DataObject with the specified name or null, if not found */ public DataObject getNthDataObject(String name, int n) { DataObject result = null; int count = 0; if (this.name.equals(name)) { result = this; count++; if (count == n) { return result; } } for (DataObject object : children) { result = object.getDataObject(name); if (result != null) { count++; if (count == n) { return result; } } } return null; } /** * This method looks for an element in this DataObject. * Why does this method exist? No external class uses this! --Brian * * @param name Name of an element * @return boolean true if there is an element by that name, else false */ protected boolean elementExists(String name) { // int n = this.countChildren(); boolean result = false; for (DataObject currentChild : this.getChildren()) { String currentName = currentChild.getName(); if (currentName.equals(name)) { // sounds like we've found it result = true; } if (result == false) { // carry on searching the children if (currentChild.countChildren() > 0) { result = currentChild.elementExists(name); } if (result) { // did we find it? break; // then stop here } } } return result; } /** * This method adds an element and list of attributes to this DataObject. * So far this was only used by SAX_XMLDecoder * * @param name Name of an element * @param atts Map list of attributes; actually not being used anymore (deprecated) */ public void addElement(String name, Map<String, String> atts) { currentObject.name = name; DataObject newObject = new DataObject(); newObject.parent = currentObject; // new object is a child of the current if (currentObject.parent != null) { // currentObject.parent.getValue().addElement(currentObject); currentObject.parent.children.add(currentObject); } currentObject = newObject; } /** * This method closes the currently open/added element in order to do some * internal housecleaning. * * @param name Name of the element being closed */ public void closeElement(String name) { DataObject newObject = new DataObject(); if (currentObject.parent != null) { newObject.parent = currentObject.parent.parent; } else { newObject.parent = null; } currentObject = newObject; } /** * Adds a value to the current element. * Not really adding to this, per se, but setting the value of an internally shifted currentObject's parent. * TODO: should really be refactored more --Brian * * @param value Value being added to the current element */ public void addValue(String value) { // currentObject.parent.getValue().addElement(value); currentObject.parent.value = value; // assume this only happens once for each currentObject /* * I'm not sure if this gets called more than once per DataObject. * It's only called by SAX_XMLDecoder.character. * I'll assume it's called only once, since elsewhere in the toolkit, only one value is possible. * Warn me and kill the process disgracefully if otherwise. * WORKS well w/o this condition failing so far * --Brian */ // currentObject.parent.numInvocations_addValue++; // if (currentObject.parent.numInvocations_addValue > 1) { // System.err.println("DataObject " + currentObject.parent + "(" + currentObject.parent.hashCode() + ").addValue(value) called more than once"); // System.exit(-1); // quit // } } // private int numInvocations_addValue = 0; /** * Returns the first value of the DataObject element/sub-element with * the specified name, if it exists. Returns null otherwise. * * @param string Name of the element to return * @return 1st value of the DataObject with the specified name or null, if not found */ public Object getDataObjectFirstValue(String string) { DataObject dobj = getDataObject(string); // get the data object, then return its first element of its vector if (dobj != null) { return dobj.value; } return null; } /** * This method creates a string version of the recursive DataObject * * @return String version (printable) version of the DataObject */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("\n[name="+getName()); // print value sb.append(", value=" + value); // print children int i = 0; for (DataObject child : children) { sb.append(", " + name + "-child(" + i++ + ")="); sb.append(child.toString()); } sb.append ("]"); return sb.toString(); } /** * Convenience method to check whether an object is of a certain class type. * Ordinarily, we could just use "o instanceof SomeClass", * but since we are potentially operating in a distributed environment with multiple runtimes, * each runtime would not recognize the same class loaded in a different runtime. * So we test by class name instead. * * TODO: Not sure if this class is the best place to put this static method. * * @param o * @param someClass * @return */ public static boolean instanceOf(Object o, Class<?> someClass) { // alternative: o instanceof someClass.class; return o.getClass().getName().equals(someClass.getName()); } }