package ring.nrapi.business; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import ring.persistence.DataStoreFactory; import ring.persistence.Persistable; import ring.persistence.RingConstants; import ring.events.listeners.*; @XmlAccessorType(XmlAccessType.PROPERTY) @XmlRootElement @XmlType( namespace = RingConstants.RING_NAMESPACE, propOrder= { "ID", "documentName", "uuid" }) public abstract class BusinessObject implements Persistable { private Persistable parent; private List<Persistable> children = new ArrayList<Persistable>(); private UUID uuid; private String id; private String docName; private List<BusinessObjectListener> listeners = new ArrayList<BusinessObjectListener>(); private boolean storeAsUpdate; public void save() { DataStoreFactory.getDefaultStore().storePersistable(this); } /** * Responsible for propagating information down the hierarchy * of objects found in the document or fragment that this * BusinessObject was loaded from. It propagates information * and calls each child's <code>createChildRelationships()</code> method. This * results in a recursive propagation of information down through the object * hierarchy. * <br/><br/> * This method only concerns objects that are created at object load. It does * not propagate information to children added later during server operation. */ public final void createChildRelationships() { for (Persistable child : children) { createChildRelationship(this, child); } } private void createChildRelationship(Persistable parent, Persistable child) { child.setParent(parent); child.setStoreAsUpdate(parent.storeAsUpdate()); //If the document name is null, then it wasn't set anywhere else //so the child is probably from the same document... if (child.getDocumentName() == null) { child.setDocumentName(parent.getDocumentName()); } for (Persistable grandchild : child.getChildren()) { createChildRelationship(child, grandchild); } } /** * Adds a child to the list of children to propagate information * to when <code>createChildRelationships()</code> is called. This * list is only read when this abstract business object is first loaded. * Afterwards, it is not considered relevant. In other words, any new * child business objects added to this object (i.e. a mobile added to a * room) do not have parent information propagated to them. It is * possible that this might change depending on how world state saving * is implemented. * @param child The child to add. */ @Override public void addChild(Persistable child) { children.add(child); } @Override public List<Persistable> getChildren() { return children; } @Override public boolean isRoot() { return (getParent() == null); } @Override public Persistable getRoot() { if (getParent() == null) { return this; } else { Persistable root = getParent(); while (root.getParent() != null) { root = root.getParent(); } return root; } } @Override @XmlTransient public Persistable getParent() { return parent; } @Override public void setParent(Persistable obj) { parent = obj; } @XmlTransient public String getCanonicalID() { return getDocumentName() + ":" + getID(); } @XmlAttribute(name = "docname") public String getDocumentName() { return docName; } public void setDocumentName(String docName) { this.docName = docName; } /** * Returns the objects unique's ID. * @return the id */ @XmlAttribute(name = "id", required = true) public String getID() { return id; } public void setID(String id) { this.id = id; } @Override public void setStoreAsUpdate(boolean val) { storeAsUpdate = val; } @Override public boolean storeAsUpdate() { return storeAsUpdate; } @Override @XmlAttribute public UUID getUuid() { return uuid; } @Override public void setUuid(UUID uuid) { this.uuid = uuid; } public void addBusinessObjectListener(BusinessObjectListener listener) { if (!listeners.contains(listener)) { listeners.add(listener); } } public boolean removeBusinessObjectListener(BusinessObjectListener listener) { return listeners.remove(listener); } public List<BusinessObjectListener> getBusinessObjectListeners() { return listeners; } /** * Returns this object represented as a full XML document containing * the standard processing instruction and a <ring> element as the * root element of the document. */ public String toXMLDocument() { String xml = marshalledXMLDocument(); String body = xml.substring(xml.indexOf("?>") + 2); body = "<ring>" + body + "</ring>"; String xmlHeader = xml.substring(0, xml.indexOf("?>") + 2); return xmlHeader + "\n" + body; } /** * Returns this object represented as an XML fragment. The XML does not contain * the standard XML processing instruction(s), nor does it contain a root element. */ @Override public String toXML() { return marshalledXMLFragment(); } private String marshalledXMLFragment() { try { JAXBContext ctx = JAXBContext.newInstance(this.getClass()); Marshaller m = ctx.createMarshaller(); //m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); StringWriter writer = new StringWriter(); m.marshal(this, writer); String xml = writer.toString(); //TODO find a MUCH better way to do this... so hackish. return xml.substring(xml.indexOf("?>") + 2); } catch (JAXBException e) { e.printStackTrace(); return null; } } private String marshalledXMLDocument() { try { JAXBContext ctx = JAXBContext.newInstance(this.getClass()); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); StringWriter writer = new StringWriter(); m.marshal(this, writer); String xml = writer.toString(); return xml; } catch (JAXBException e) { e.printStackTrace(); return null; } } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((docName == null) ? 0 : docName.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((listeners == null) ? 0 : listeners.hashCode()); result = prime * result + (storeAsUpdate ? 1231 : 1237); result = prime * result + ((uuid == null) ? 0 : uuid.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BusinessObject other = (BusinessObject) obj; if (docName == null) { if (other.docName != null) return false; } else if (!docName.equals(other.docName)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (listeners == null) { if (other.listeners != null) return false; } else if (!listeners.equals(other.listeners)) return false; if (storeAsUpdate != other.storeAsUpdate) return false; if (uuid == null) { if (other.uuid != null) return false; } else if (uuid != other.uuid) return false; return true; } }