/******************************************************************************* * Copyright (c) 2001, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * * Jens Lukowski/Innoopract - initial renaming/restructuring * * Balazs Banfai: Bug 154737 getUserData/setUserData support for Node * https://bugs.eclipse.org/bugs/show_bug.cgi?id=154737 * * David Carver (STAR) - bug 295127 - implement isSameNode and compareDocumentPosition methods. * Unit Tests covered in wst.xsl XPath 2.0 tests. * David Carver (STAR) - bug 296999 - Inefficient use of new String() *******************************************************************************/ package org.eclipse.wst.xml.core.internal.document; import java.io.Serializable; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Platform; import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; import org.eclipse.wst.sse.core.internal.provisional.AbstractNotifier; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.w3c.dom.DOMException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.w3c.dom.TypeInfo; import org.w3c.dom.UserDataHandler; /** * NodeImpl class */ public abstract class NodeImpl extends AbstractNotifier implements Node, IDOMNode, IAdaptable { // define one empty nodelist, for repeated use private final static NodeList EMPTY_NODE_LIST = new NodeListImpl(); // DocumentPosition //private final static short DOCUMENT_POSITION_DISCONNECTED = 0x01; private final static short DOCUMENT_POSITION_PRECEDING = 0x02; private final static short DOCUMENT_POSITION_FOLLOWING = 0x04; //private final static short DOCUMENT_POSITION_CONTAINS = 0x08; //private final static short DOCUMENT_POSITION_CONTAINED_BY = 0x10; private final static short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; private boolean fDataEditable = true; private IStructuredDocumentRegion flatNode = null; private NodeImpl nextSibling = null; private DocumentImpl ownerDocument = null; private NodeImpl parentNode = null; private NodeImpl previousSibling = null; // define one empty String constant for repeated use static final String EMPTY_STRING = ""; /** * NodeImpl constructor */ protected NodeImpl() { super(); } /** * NodeImpl constructor * * @param that * NodeImpl */ protected NodeImpl(NodeImpl that) { if (that != null) { this.ownerDocument = that.ownerDocument; } } public Object getAdapter(Class adapter) { final IStructuredModel model = getModel(); return model != null ? Platform.getAdapterManager().getAdapter(model, adapter) : null; } /** * appendChild method * * @return org.w3c.dom.Node * @param newChild * org.w3c.dom.Node */ public Node appendChild(Node newChild) throws DOMException { throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, DOMMessages.HIERARCHY_REQUEST_ERR); } /** * contains method * * @return boolean * @param offset * int */ public boolean contains(int offset) { return (offset >= getStartOffset() && offset < getEndOffset()); } /** * @param s * @param tagName * @return */ protected String createDOMExceptionMessage(short s, String tagName) { String result = null; // TODO: Should localize these messages, and provide /u escaped // version of tagName result = lookupMessage(s) + " " + tagName; //$NON-NLS-1$ return result; } /** * getAttributes method * * @return org.w3c.dom.NamedNodeMap */ public NamedNodeMap getAttributes() { return null; } /** */ protected String getCharValue(String name) { DocumentImpl document = (DocumentImpl) getOwnerDocument(); if (document == null) return null; return document.getCharValue(name); } /** * getChildNodes method * * @return org.w3c.dom.NodeList */ public NodeList getChildNodes() { // As per DOM spec, correct behavior for getChildNodes is to return a // zero length NodeList, not null, when there are no children. // We'll use a common instance of an empty node list, just to prevent // creating a trival object many many times. return EMPTY_NODE_LIST; } /** * getCommonAncestor method * * @return org.w3c.dom.Node * @param node * org.w3c.dom.Node */ public Node getCommonAncestor(Node node) { if (node == null) return null; for (Node na = node; na != null; na = na.getParentNode()) { for (Node ta = this; ta != null; ta = ta.getParentNode()) { if (ta == na) return ta; } } return null; // not found } /** * getContainerDocument method * * @return org.w3c.dom.Document */ public Document getContainerDocument() { Node parent = null; for (Node node = this; node != null; node = parent) { if (node.getNodeType() == Node.DOCUMENT_NODE) { return (Document) node; } /* Break out of a bad hierarchy */ if ((parent = node.getParentNode()) == node) break; } return null; } /** * getEndOffset method * * @return int */ public int getEndOffset() { Node node = this; while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { ElementImpl element = (ElementImpl) node; IStructuredDocumentRegion endStructuredDocumentRegion = element.getEndStructuredDocumentRegion(); if (endStructuredDocumentRegion != null) return endStructuredDocumentRegion.getEnd(); } Node last = node.getLastChild(); if (last != null) { // dig into the last node = last; continue; } IStructuredDocumentRegion lastStructuredDocumentRegion = ((NodeImpl) node).getStructuredDocumentRegion(); if (lastStructuredDocumentRegion != null) return lastStructuredDocumentRegion.getEnd(); Node prev = node.getPreviousSibling(); if (prev != null) { // move to the previous node = prev; continue; } Node parent = node.getParentNode(); node = null; while (parent != null) { if (parent.getNodeType() == Node.ELEMENT_NODE) { ElementImpl element = (ElementImpl) parent; IStructuredDocumentRegion startStructuredDocumentRegion = element.getStartStructuredDocumentRegion(); if (startStructuredDocumentRegion != null) return startStructuredDocumentRegion.getEnd(); } Node parentPrev = parent.getPreviousSibling(); if (parentPrev != null) { // move to the previous node = parentPrev; break; } parent = parent.getParentNode(); } } return 0; } public IStructuredDocumentRegion getEndStructuredDocumentRegion() { return null; } /** */ public FactoryRegistry getFactoryRegistry() { IDOMModel model = getModel(); if (model != null) { FactoryRegistry reg = model.getFactoryRegistry(); if (reg != null) return reg; } return null; } /** * getFirstChild method * * @return org.w3c.dom.Node */ public Node getFirstChild() { return null; } /** * getFirstStructuredDocumentRegion method * */ public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { return StructuredDocumentRegionUtil.getStructuredDocumentRegion(this.flatNode); } /** */ public int getIndex() { Node parent = getParentNode(); if (parent == null) return -1; // error int index = 0; for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { if (child == this) return index; index++; } return -1; // error } /** * getLastChild method * * @return org.w3c.dom.Node */ public Node getLastChild() { return null; } /** * getLastStructuredDocumentRegion method * */ public IStructuredDocumentRegion getLastStructuredDocumentRegion() { return StructuredDocumentRegionUtil.getStructuredDocumentRegion(this.flatNode); } /** */ public String getLocalName() { return null; } /** * the default implementation can just refer to the owning document */ public IDOMModel getModel() { if (this.ownerDocument == null) return null; return this.ownerDocument.getModel(); } /** * all but attr return null */ public ITextRegion getNameRegion() { return null; } /** */ public String getNamespaceURI() { return null; } /** * getNextSibling method * * @return org.w3c.dom.Node */ public Node getNextSibling() { return this.nextSibling; } /** * getNodeAt method * * @return org.w3c.dom.Node * @param offset * int */ Node getNodeAt(int offset) { IDOMNode parent = this; IDOMNode child = (IDOMNode) getFirstChild(); while (child != null) { if (child.getEndOffset() <= offset) { child = (IDOMNode) child.getNextSibling(); continue; } if (child.getStartOffset() > offset) { break; } IStructuredDocumentRegion startStructuredDocumentRegion = child.getStartStructuredDocumentRegion(); if (startStructuredDocumentRegion != null) { if (startStructuredDocumentRegion.getEnd() > offset) return child; } // dig more parent = child; child = (IDOMNode) parent.getFirstChild(); } return parent; } /* (non-Javadoc) * @see org.w3c.dom.Node#getNodeValue() */ public String getNodeValue() throws DOMException { return null; } /** * getOwnerDocument method * * @return org.w3c.dom.Document */ public Document getOwnerDocument() { return this.ownerDocument; } /** * getParentNode method * * @return org.w3c.dom.Node */ public Node getParentNode() { return this.parentNode; } /** */ public String getPrefix() { return null; } /** * getPreviousSibling method * * @return org.w3c.dom.Node */ public Node getPreviousSibling() { return this.previousSibling; } /** */ public String getSource() { if (this.flatNode == null) return NodeImpl.EMPTY_STRING; return this.flatNode.getText(); } /** * getStartOffset method * * @return int */ public int getStartOffset() { if (this.flatNode != null) return this.flatNode.getStart(); NodeImpl prev = (NodeImpl) getPreviousSibling(); if (prev != null) return prev.getEndOffset(); Node parent = getParentNode(); if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { ElementImpl element = (ElementImpl) parent; if (element.hasStartTag()) return element.getStartEndOffset(); return element.getStartOffset(); } // final fallback to look into first child NodeImpl child = (NodeImpl) getFirstChild(); while (child != null) { IStructuredDocumentRegion childStructuredDocumentRegion = child.getStructuredDocumentRegion(); if (childStructuredDocumentRegion != null) return childStructuredDocumentRegion.getStart(); child = (NodeImpl) child.getFirstChild(); } return 0; } public IStructuredDocumentRegion getStartStructuredDocumentRegion() { return getFirstStructuredDocumentRegion(); } /** * Every node (indirectly) knows its structuredDocument */ public IStructuredDocument getStructuredDocument() { return getModel().getStructuredDocument(); } /** */ IStructuredDocumentRegion getStructuredDocumentRegion() { return this.flatNode; } /** * all but attr return null */ public ITextRegion getValueRegion() { return null; } /** */ public String getValueSource() { return getNodeValue(); } /* (non-Javadoc) * @see org.w3c.dom.Node#hasAttributes() */ public boolean hasAttributes() { return false; } /* (non-Javadoc) * @see org.w3c.dom.Node#hasChildNodes() */ public boolean hasChildNodes() { return false; } /** * hasProperties method * * @return boolean */ public boolean hasProperties() { return false; } /** * insertBefore method * * @return org.w3c.dom.Node * @param newChild * org.w3c.dom.Node * @param refChild * org.w3c.dom.Node */ public Node insertBefore(Node newChild, Node refChild) throws DOMException { throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, DOMMessages.HIERARCHY_REQUEST_ERR); } public boolean isChildEditable() { return false; } /** */ public boolean isClosed() { return true; } /** * isContainer method * * @return boolean */ public boolean isContainer() { return false; } public boolean isDataEditable() { if (!fDataEditable) { DOMModelImpl model = (DOMModelImpl) getModel(); if (model != null && model.isReparsing()) { return true; } } return fDataEditable; } /** */ public boolean isSupported(String feature, String version) { if (this.ownerDocument == null) return false; DOMImplementation impl = this.ownerDocument.getImplementation(); if (impl == null) return false; return impl.hasFeature(feature, version); } /** * @param s * @return */ private String lookupMessage(short s) { // TODO: make localized version String result = null; switch (s) { case DOMException.DOMSTRING_SIZE_ERR : result = DOMMessages.DOMSTRING_SIZE_ERR; break; case DOMException.HIERARCHY_REQUEST_ERR : result = DOMMessages.HIERARCHY_REQUEST_ERR; break; case DOMException.INDEX_SIZE_ERR : result = DOMMessages.INDEX_SIZE_ERR; break; case DOMException.INUSE_ATTRIBUTE_ERR : result = DOMMessages.INUSE_ATTRIBUTE_ERR; break; case DOMException.INVALID_ACCESS_ERR : result = DOMMessages.INVALID_ACCESS_ERR; break; case DOMException.INVALID_CHARACTER_ERR : result = DOMMessages.INVALID_CHARACTER_ERR; break; case DOMException.INVALID_MODIFICATION_ERR : result = DOMMessages.INVALID_MODIFICATION_ERR; break; case DOMException.INVALID_STATE_ERR : result = DOMMessages.INVALID_STATE_ERR; break; case DOMException.NAMESPACE_ERR : result = DOMMessages.NAMESPACE_ERR; break; case DOMException.NO_DATA_ALLOWED_ERR : result = DOMMessages.NO_DATA_ALLOWED_ERR; break; case DOMException.NO_MODIFICATION_ALLOWED_ERR : result = DOMMessages.NO_MODIFICATION_ALLOWED_ERR; break; case DOMException.NOT_FOUND_ERR : result = DOMMessages.NOT_FOUND_ERR; break; case DOMException.NOT_SUPPORTED_ERR : result = DOMMessages.NOT_SUPPORTED_ERR; break; case DOMException.SYNTAX_ERR : result = DOMMessages.SYNTAX_ERR; break; case 17:// DOMException.TYPE_MISMATCH_ERR : result = DOMMessages.TYPE_MISMATCH_ERR; break; case 16:// DOMException.VALIDATION_ERR : result = DOMMessages.VALIDATION_ERR; break; case DOMException.WRONG_DOCUMENT_ERR : result = DOMMessages.WRONG_DOCUMENT_ERR; break; default : result = NodeImpl.EMPTY_STRING; break; } return result; } /** * normalize method */ public void normalize() { TextImpl prevText = null; for (Node child = getFirstChild(); child != null; child = child.getNextSibling()) { switch (child.getNodeType()) { case TEXT_NODE : { if (prevText == null) { prevText = (TextImpl) child; break; } Text text = (Text) child; removeChild(text); prevText.appendText(text); child = prevText; break; } case ELEMENT_NODE : { Element element = (Element) child; element.normalize(); prevText = null; break; } default : prevText = null; break; } } } protected void notifyEditableChanged() { DocumentImpl document = (DocumentImpl) getContainerDocument(); if (document == null) return; DOMModelImpl model = (DOMModelImpl) document.getModel(); if (model == null) return; model.editableChanged(this); } /** * notifyValueChanged method */ protected void notifyValueChanged() { DocumentImpl document = (DocumentImpl) getContainerDocument(); if (document == null) return; syncDataEditableState(); DOMModelImpl model = (DOMModelImpl) document.getModel(); if (model == null) return; model.valueChanged(this); } /** * removeChild method * * @return org.w3c.dom.Node * @param oldChild * org.w3c.dom.Node */ public Node removeChild(Node oldChild) throws DOMException { throw new DOMException(DOMException.NOT_FOUND_ERR, DOMMessages.NOT_FOUND_ERR); } /** * removeChildNodes method */ public void removeChildNodes() { } /** * removeChildNodes method * * @return org.w3c.dom.DocumentFragment * @param firstChild * org.w3c.dom.Node * @param lastChild * org.w3c.dom.Node */ public DocumentFragment removeChildNodes(Node firstChild, Node lastChild) { return null; } /** * replaceChild method * * @return org.w3c.dom.Node * @param newChild * org.w3c.dom.Node * @param oldChild * org.w3c.dom.Node */ public Node replaceChild(Node newChild, Node oldChild) throws DOMException { throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, DOMMessages.HIERARCHY_REQUEST_ERR); } /** * Resets children values from IStructuredDocumentRegion. */ void resetStructuredDocumentRegions() { for (NodeImpl child = (NodeImpl) getFirstChild(); child != null; child = (NodeImpl) child.getNextSibling()) { child.resetStructuredDocumentRegions(); } this.flatNode = null; } public void setChildEditable(boolean editable) { // nop } public void setDataEditable(boolean editable) { if (fDataEditable == editable) { return; } ReadOnlyController roc = ReadOnlyController.getInstance(); if (editable) { roc.unlockData(this); } else { roc.lockData(this); } fDataEditable = editable; notifyEditableChanged(); } public void setEditable(boolean editable, boolean deep) { if (deep) { IDOMNode node = (IDOMNode) getFirstChild(); while (node != null) { node.setEditable(editable, deep); node = (IDOMNode) node.getNextSibling(); } } setChildEditable(editable); setDataEditable(editable); } /** * setNextSibling method * * @param nextSibling * org.w3c.dom.Node */ protected void setNextSibling(Node nextSibling) { this.nextSibling = (NodeImpl) nextSibling; } /** * setNodeValue method * * @param nodeValue * java.lang.String */ public void setNodeValue(String nodeValue) throws DOMException { } /** * setOwnerDocument method * * @param ownerDocument * org.w3c.dom.Document */ protected void setOwnerDocument(Document ownerDocument) { this.ownerDocument = (DocumentImpl) ownerDocument; } /** */ protected void setOwnerDocument(Document ownerDocument, boolean deep) { this.ownerDocument = (DocumentImpl) ownerDocument; if (deep) { for (NodeImpl child = (NodeImpl) getFirstChild(); child != null; child = (NodeImpl) child.getNextSibling()) { child.setOwnerDocument(ownerDocument, deep); } } } /** * setParentNode method * * @param parentNode * org.w3c.dom.Node */ protected void setParentNode(Node parentNode) { this.parentNode = (NodeImpl) parentNode; } /** */ public void setPrefix(String prefix) throws DOMException { } /** * setPreviousSibling method * * @param previousSibling * org.w3c.dom.Node */ protected void setPreviousSibling(Node previousSibling) { this.previousSibling = (NodeImpl) previousSibling; } /** */ public void setSource(String source) throws InvalidCharacterException { // not supported } /** */ void setStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { this.flatNode = flatNode; } /** */ public void setValueSource(String source) { setNodeValue(source); } protected void syncDataEditableState() { ReadOnlyController roc = ReadOnlyController.getInstance(); if (fDataEditable) { roc.unlockData(this); } else { roc.lockData(this); } } /** * toString method * * @return java.lang.String */ public String toString() { return getNodeName(); } public int getLength() { int result = -1; int start = getStartOffset(); if (start >= 0) { int end = getEndOffset(); if (end >= 0) { result = end - start; if (result < -1) { result = -1; } } } return result; } /* (non-Javadoc) * @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node) */ public short compareDocumentPosition(Node other) throws DOMException { if (!(other instanceof IDOMNode)) throw new DOMException(DOMException.NOT_SUPPORTED_ERR, DOMMessages.NOT_SUPPORTED_ERR); int nodeStart = this.getStartOffset(); int otherStart = ((IDOMNode) other).getStartOffset(); if (otherStart > nodeStart) { return DOCUMENT_POSITION_FOLLOWING; } else if (otherStart < nodeStart) { return DOCUMENT_POSITION_PRECEDING; } return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public String getBaseURI() { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public Object getFeature(String feature, String version) { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /* * (non-Javadoc) * * @see org.w3c.dom.Node#getTextContent() */ public String getTextContent() throws DOMException { switch (getNodeType()) { case Node.DOCUMENT_NODE : case Node.DOCUMENT_TYPE_NODE : case Node.NOTATION_NODE : return null; case Node.TEXT_NODE : case Node.CDATA_SECTION_NODE : case Node.COMMENT_NODE : case Node.PROCESSING_INSTRUCTION_NODE : return getNodeValue(); } if (hasChildNodes()) { final StringBuffer builder = new StringBuffer(); Node child = getFirstChild(); while (child != null) { short nodeType = child.getNodeType(); if (nodeType == Node.COMMENT_NODE || nodeType == Node.PROCESSING_INSTRUCTION_NODE) { child = child.getNextSibling(); continue; } String text = ((IDOMNode) child).getTextContent(); if (text != null) { builder.append(text); } child = child.getNextSibling(); } return builder.toString(); } return EMPTY_STRING; } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public boolean isDefaultNamespace(String namespaceURI) { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node) */ public boolean isEqualNode(Node arg) { return this.equals(arg); } /* (non-Javadoc) * @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node) */ public boolean isSameNode(Node other) { return this == other; } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public String lookupNamespaceURI(String prefix) { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public String lookupPrefix(String namespaceURI) { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public void setTextContent(String textContent) throws DOMException { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public TypeInfo getSchemaTypeInfo() { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /** * NOT IMPLEMENTED, is defined here in preparation of DOM Level 3 */ public boolean isId() { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented in this version."); //$NON-NLS-1$ } /** * Retrieves the object for a node associated to a key. * * @param key The key associated with the object. * @return object The object for this node, associated with the key passed * or null if there was none set. * * @since DOM Level 3 */ public Object getUserData(String key) { if (key==null) return null; Map userDataTable = UserData.getInstance().getUserDataTable(this); if (userDataTable!=null) { UserDataAndHandler userDataAndHandler = (UserDataAndHandler) userDataTable.get(key); if (userDataAndHandler!=null) { return userDataAndHandler.getData(); } } return null; } /** * Sets the user data and handler for a key * * @param key The key associated with the object. * @param object The object for this node, associated with the key passed. * Null passed removes the existing association to the key. * @param handler UserDataHandler for the userdata associated with this key * @return Object The previous userdata object if set, null if it was none. * * @since DOM Level 3 */ public Object setUserData(String key, Object data, UserDataHandler handler) { UserDataAndHandler previousDataAndHandler=null; //return immediately for null keys if (key ==null) return null; Map userDataTable = UserData.getInstance().getUserDataTable(this); //remove association for the key if data is null if (data==null) { if (userDataTable==null) return null; //see if there is a previous value set previousDataAndHandler = (UserDataAndHandler) userDataTable.get(key); if (previousDataAndHandler!=null) { userDataTable.remove(key); return previousDataAndHandler.getData(); } //there is no previous value found return null; } //if data passed is not null //the first data in the hashtable if (userDataTable==null) { userDataTable=new Hashtable(); UserDataAndHandler userDataAndHandler=new UserDataAndHandler(data,handler); userDataTable.put(key,userDataAndHandler); return null; //there is already data in the hashtable } else { UserDataAndHandler userDataAndHandler=new UserDataAndHandler(data,handler); previousDataAndHandler=(UserDataAndHandler) userDataTable.put(key,userDataAndHandler); //if we replace a value if (previousDataAndHandler!=null) return previousDataAndHandler.getData(); else { return null; } } } /** * Notifies the UserDataHandlers of the node. * * @param operation * @param destination */ protected void notifyUserDataHandlers(short operation, Node destination) { if (operation!=UserDataHandler.NODE_ADOPTED & operation!=UserDataHandler.NODE_CLONED & operation!=UserDataHandler.NODE_DELETED & operation!=UserDataHandler.NODE_IMPORTED & operation!=UserDataHandler.NODE_RENAMED) return; Map userDataTable = UserData.getInstance().getUserDataTable(this); if (userDataTable!=null) { Iterator entries =userDataTable.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Map.Entry) entries.next(); String key = entry.getKey().toString(); //should always be a string UserDataAndHandler dataAndHandler = (UserDataAndHandler) entry.getValue(); if (dataAndHandler!=null) { UserDataHandler dataHandler=dataAndHandler.getHandler(); if (dataHandler!=null) { dataHandler.handle(operation, key, dataAndHandler.getData(), this, destination); } } } } } /** * * Class for user data and UserDataHandler */ protected static class UserDataAndHandler implements Serializable { /** * Generated Serial ID */ private static final long serialVersionUID = 4860521237315444841L; /** * Generated serialization version */ private Object data; private UserDataHandler handler; public UserDataAndHandler(Object data, UserDataHandler handler) { this.data=data; this.handler=handler; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public UserDataHandler getHandler() { return handler; } public void setHandler(UserDataHandler handler) { this.handler = handler; } } }