| /* GnomeNode.java - |
| Copyright (C) 2004 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.xml.libxmlj.dom; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentType; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.Text; |
| import org.w3c.dom.UserDataHandler; |
| |
| import gnu.xml.libxmlj.util.StandaloneDocumentType; |
| |
| /** |
| * A DOM node implemented in libxml2. |
| * |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> |
| */ |
| class GnomeNode |
| implements Node, Comparable |
| { |
| |
| /** |
| * Maps document pointers to a map of node pointers to node instances. |
| */ |
| static Map instances; |
| |
| /** |
| * Retrieves the node instance for the specified node pointer. |
| * This creates a new instance and adds it to the cache if required. |
| * @param doc the document pointer |
| * @param node the node pointer |
| * @param type the node type |
| */ |
| static GnomeNode newInstance(final Object doc, final Object node, |
| final int type) |
| { |
| if (doc == null) |
| { |
| throw new NullPointerException("doc"); |
| } |
| if (node == null) |
| { |
| throw new NullPointerException("node"); |
| } |
| if (instances == null) |
| { |
| instances = new HashMap(); |
| } |
| Map docNodes = (Map) instances.get(doc); |
| if (docNodes == null) |
| { |
| docNodes = new HashMap(1024); // TODO review optimal initial capacity |
| instances.put(doc, docNodes); |
| } |
| GnomeNode nodeInstance = (GnomeNode) docNodes.get(node); |
| if (nodeInstance != null) |
| { |
| return nodeInstance; // Return cached version |
| } |
| switch (type) |
| { |
| case ELEMENT_NODE: |
| nodeInstance = new GnomeElement(node); |
| break; |
| case ATTRIBUTE_NODE: |
| nodeInstance = new GnomeAttr(node); |
| break; |
| case TEXT_NODE: |
| nodeInstance = new GnomeText(node); |
| break; |
| case CDATA_SECTION_NODE: |
| nodeInstance = new GnomeCDATASection(node); |
| break; |
| case ENTITY_REFERENCE_NODE: |
| nodeInstance = new GnomeEntityReference(node); |
| break; |
| case ENTITY_NODE: |
| nodeInstance = new GnomeEntity(node); |
| break; |
| case PROCESSING_INSTRUCTION_NODE: |
| nodeInstance = new GnomeProcessingInstruction(node); |
| break; |
| case COMMENT_NODE: |
| nodeInstance = new GnomeComment(node); |
| break; |
| case DOCUMENT_NODE: |
| nodeInstance = new GnomeDocument(node); |
| break; |
| case DOCUMENT_TYPE_NODE: |
| nodeInstance = new GnomeDocumentType(node); |
| break; |
| case DOCUMENT_FRAGMENT_NODE: |
| nodeInstance = new GnomeDocumentFragment(node); |
| break; |
| case NOTATION_NODE: |
| nodeInstance = new GnomeNotation(node); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown node type: " + type); |
| } |
| docNodes.put(node, nodeInstance); |
| return nodeInstance; |
| } |
| |
| /** |
| * Frees the specified document. |
| * This removes all its nodes from the cache. |
| */ |
| static void freeDocument(final Object doc) |
| { |
| if (instances == null || doc == null) |
| { |
| return; |
| } |
| instances.remove(doc); |
| //System.out.println("Freed "+instances.remove(doc)); |
| } |
| |
| /** |
| * xmlNodePtr |
| */ |
| final Object id; |
| |
| Map userData; |
| Map userDataHandlers; |
| |
| GnomeNode(final Object id) |
| { |
| this.id = id; |
| } |
| |
| public native String getNodeName(); |
| |
| public native String getNodeValue() |
| throws DOMException; |
| |
| public native void setNodeValue(String nodeValue) |
| throws DOMException; |
| |
| public native short getNodeType(); |
| |
| public native Node getParentNode(); |
| |
| public NodeList getChildNodes() |
| { |
| return new GnomeNodeList(id); |
| } |
| |
| public native Node getFirstChild(); |
| |
| public native Node getLastChild(); |
| |
| public native Node getPreviousSibling(); |
| |
| public native Node getNextSibling(); |
| |
| public NamedNodeMap getAttributes() |
| { |
| return new GnomeNamedNodeMap(id, 0); |
| } |
| |
| public native Document getOwnerDocument(); |
| |
| public Node insertBefore(Node newChild, Node refChild) |
| throws DOMException |
| { |
| if (newChild instanceof StandaloneDocumentType) |
| { |
| DocumentType dt = (DocumentType) newChild; |
| newChild = ((GnomeDocument) getOwnerDocument()) |
| .createDocumentType(dt.getName(), dt.getPublicId(), |
| dt.getSystemId()); |
| } |
| if (newChild == null) |
| { |
| throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null); |
| } |
| if (!(newChild instanceof GnomeNode)) |
| { |
| throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, null); |
| } |
| if (refChild == null || !(refChild instanceof GnomeNode)) |
| { |
| throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null); |
| } |
| return xmljInsertBefore(newChild, refChild); |
| } |
| |
| private native Node xmljInsertBefore(Node newChild, Node refChild) |
| throws DOMException; |
| |
| public Node replaceChild(Node newChild, Node oldChild) |
| throws DOMException |
| { |
| if (newChild instanceof StandaloneDocumentType) |
| { |
| DocumentType dt = (DocumentType) newChild; |
| newChild = ((GnomeDocument) getOwnerDocument()) |
| .createDocumentType(dt.getName(), dt.getPublicId(), |
| dt.getSystemId()); |
| } |
| if (newChild == null) |
| { |
| throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null); |
| } |
| if (!(newChild instanceof GnomeNode)) |
| { |
| throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, newChild.toString()); |
| } |
| if (oldChild == null || !(oldChild instanceof GnomeNode)) |
| { |
| throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null); |
| } |
| return xmljReplaceChild(newChild, oldChild); |
| } |
| |
| private native Node xmljReplaceChild(Node newChild, Node oldChild) |
| throws DOMException; |
| |
| public Node removeChild(Node oldChild) |
| throws DOMException |
| { |
| if (!(oldChild instanceof GnomeNode)) |
| { |
| throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, null); |
| } |
| return xmljRemoveChild(oldChild); |
| } |
| |
| private native Node xmljRemoveChild(Node oldChild) |
| throws DOMException; |
| |
| public Node appendChild(Node newChild) |
| throws DOMException |
| { |
| if (newChild instanceof StandaloneDocumentType) |
| { |
| DocumentType dt = (DocumentType) newChild; |
| newChild = ((GnomeDocument) getOwnerDocument()) |
| .createDocumentType(dt.getName(), dt.getPublicId(), |
| dt.getSystemId()); |
| } |
| if (!(newChild instanceof GnomeNode)) |
| { |
| throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, null); |
| } |
| return xmljAppendChild(newChild); |
| } |
| |
| private native Node xmljAppendChild(Node newChild) |
| throws DOMException; |
| |
| public native boolean hasChildNodes(); |
| |
| public Node cloneNode(boolean deep) |
| { |
| Node ret = xmljCloneNode(deep); |
| notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, ret); |
| return ret; |
| } |
| |
| private native Node xmljCloneNode(boolean deep); |
| |
| public native void normalize(); |
| |
| public boolean isSupported(String feature, String version) |
| { |
| return getOwnerDocument().getImplementation() |
| .hasFeature(feature, version); |
| } |
| |
| public native String getNamespaceURI(); |
| |
| public native String getPrefix(); |
| |
| public native void setPrefix(String prefix) |
| throws DOMException; |
| |
| public native String getLocalName(); |
| |
| public native boolean hasAttributes(); |
| |
| public int hashCode() |
| { |
| return id.hashCode(); |
| } |
| |
| public boolean equals(Object other) |
| { |
| if (other == this) |
| { |
| return true; |
| } |
| return (other instanceof GnomeNode && |
| ((GnomeNode) other).id == id); |
| } |
| |
| // DOM Level 3 methods |
| |
| public native String getBaseURI(); |
| |
| public short compareDocumentPosition(Node other) |
| throws DOMException |
| { |
| return (short) compareTo(other); |
| } |
| |
| public final int compareTo(Object other) |
| { |
| if (other instanceof GnomeNode) |
| { |
| return xmljCompareTo(other); |
| } |
| return 0; |
| } |
| |
| private native int xmljCompareTo(Object other); |
| |
| public String getTextContent() |
| throws DOMException |
| { |
| switch (getNodeType()) |
| { |
| case ELEMENT_NODE: |
| case ATTRIBUTE_NODE: |
| case ENTITY_NODE: |
| case ENTITY_REFERENCE_NODE: |
| case DOCUMENT_FRAGMENT_NODE: |
| StringBuffer buffer = new StringBuffer(); |
| NodeList children = getChildNodes(); |
| int len = children.getLength(); |
| for (int i = 0; i < len; i++) |
| { |
| Node child = children.item(i); |
| String textContent = child.getTextContent(); |
| if (textContent != null) |
| { |
| buffer.append(textContent); |
| } |
| } |
| return buffer.toString(); |
| case TEXT_NODE: |
| case CDATA_SECTION_NODE: |
| case COMMENT_NODE: |
| case PROCESSING_INSTRUCTION_NODE: |
| return getNodeValue(); |
| default: |
| return null; |
| } |
| } |
| |
| public void setTextContent(String textContent) |
| throws DOMException |
| { |
| switch (getNodeType()) |
| { |
| case ENTITY_REFERENCE_NODE: |
| // entity references are read only |
| throw new GnomeDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, |
| null); |
| case ELEMENT_NODE: |
| case ATTRIBUTE_NODE: |
| case ENTITY_NODE: |
| case DOCUMENT_FRAGMENT_NODE: |
| NodeList children = getChildNodes(); |
| int len = children.getLength(); |
| for (int i = 0; i < len; i++) |
| { |
| Node child = children.item(i); |
| removeChild(child); |
| } |
| if (textContent != null) |
| { |
| Text text = getOwnerDocument().createTextNode(textContent); |
| appendChild(text); |
| } |
| break; |
| case TEXT_NODE: |
| case CDATA_SECTION_NODE: |
| case COMMENT_NODE: |
| case PROCESSING_INSTRUCTION_NODE: |
| setNodeValue(textContent); |
| break; |
| } |
| } |
| |
| public boolean isSameNode(Node other) |
| { |
| return equals(other); |
| } |
| |
| public native String lookupPrefix(String namespaceURI); |
| |
| public native boolean isDefaultNamespace(String namespaceURI); |
| |
| public native String lookupNamespaceURI(String prefix); |
| |
| public native boolean isEqualNode(Node arg); |
| |
| public Object getFeature(String feature, String version) |
| { |
| return getOwnerDocument().getImplementation() |
| .getFeature(feature, version); |
| } |
| |
| public Object setUserData(String key, Object data, UserDataHandler handler) |
| { |
| // TODO handler |
| if (userData == null) |
| { |
| userData = new HashMap(); |
| } |
| if (handler != null) |
| { |
| if (userDataHandlers == null) |
| { |
| userDataHandlers = new HashMap(); |
| } |
| userDataHandlers.put(key, handler); |
| } |
| return userData.put(key, data); |
| } |
| |
| public Object getUserData(String key) |
| { |
| if (userData == null) |
| { |
| return null; |
| } |
| return userData.get(key); |
| } |
| |
| void notifyUserDataHandlers(short op, Node src, Node dst) |
| { |
| if (userDataHandlers != null) |
| { |
| for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); ) |
| { |
| Map.Entry entry = (Map.Entry) i.next(); |
| String key = (String) entry.getKey(); |
| UserDataHandler handler = (UserDataHandler) entry.getValue(); |
| Object data = userData.get(key); |
| handler.handle(op, key, data, src, dst); |
| } |
| } |
| } |
| |
| public String toString() |
| { |
| StringBuffer buffer = new StringBuffer(getClass().getName()); |
| buffer.append("[nodeName="); |
| buffer.append(getNodeName()); |
| buffer.append("]"); |
| return buffer.toString(); |
| } |
| |
| } |