1 /* GnomeNode.java -
2    Copyright (C) 2004 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.xml.libxmlj.dom;
39 
40 import gnu.java.lang.CPStringBuilder;
41 
42 import java.util.HashMap;
43 import java.util.Iterator;
44 import java.util.Map;
45 
46 import org.w3c.dom.Document;
47 import org.w3c.dom.DocumentType;
48 import org.w3c.dom.DOMException;
49 import org.w3c.dom.NamedNodeMap;
50 import org.w3c.dom.Node;
51 import org.w3c.dom.NodeList;
52 import org.w3c.dom.Text;
53 import org.w3c.dom.UserDataHandler;
54 
55 import gnu.xml.libxmlj.util.StandaloneDocumentType;
56 
57 /**
58  * A DOM node implemented in libxml2.
59  *
60  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
61  */
62 class GnomeNode
63   implements Node, Comparable
64 {
65 
66   /**
67    * Maps document pointers to a map of node pointers to node instances.
68    */
69   static Map instances;
70 
71   /**
72    * Retrieves the node instance for the specified node pointer.
73    * This creates a new instance and adds it to the cache if required.
74    * @param doc the document pointer
75    * @param node the node pointer
76    * @param type the node type
77    */
newInstance(final Object doc, final Object node, final int type)78   static GnomeNode newInstance(final Object doc, final Object node,
79                                final int type)
80   {
81     if (doc == null)
82       {
83         throw new NullPointerException("doc");
84       }
85     if (node == null)
86       {
87         throw new NullPointerException("node");
88       }
89     if (instances == null)
90       {
91         instances = new HashMap();
92       }
93     Map docNodes = (Map) instances.get(doc);
94     if (docNodes == null)
95       {
96         docNodes = new HashMap(1024); // TODO review optimal initial capacity
97         instances.put(doc, docNodes);
98       }
99     GnomeNode nodeInstance = (GnomeNode) docNodes.get(node);
100     if (nodeInstance != null)
101       {
102         return nodeInstance; // Return cached version
103       }
104     switch (type)
105       {
106       case ELEMENT_NODE:
107         nodeInstance = new GnomeElement(node);
108         break;
109       case ATTRIBUTE_NODE:
110         nodeInstance = new GnomeAttr(node);
111         break;
112       case TEXT_NODE:
113         nodeInstance = new GnomeText(node);
114         break;
115       case CDATA_SECTION_NODE:
116         nodeInstance = new GnomeCDATASection(node);
117         break;
118       case ENTITY_REFERENCE_NODE:
119         nodeInstance = new GnomeEntityReference(node);
120         break;
121       case ENTITY_NODE:
122         nodeInstance = new GnomeEntity(node);
123         break;
124       case PROCESSING_INSTRUCTION_NODE:
125         nodeInstance = new GnomeProcessingInstruction(node);
126         break;
127       case COMMENT_NODE:
128         nodeInstance = new GnomeComment(node);
129         break;
130       case DOCUMENT_NODE:
131         nodeInstance = new GnomeDocument(node);
132         break;
133       case DOCUMENT_TYPE_NODE:
134         nodeInstance = new GnomeDocumentType(node);
135         break;
136       case DOCUMENT_FRAGMENT_NODE:
137         nodeInstance = new GnomeDocumentFragment(node);
138         break;
139       case NOTATION_NODE:
140         nodeInstance = new GnomeNotation(node);
141         break;
142       default:
143         throw new IllegalArgumentException("Unknown node type: " + type);
144       }
145     docNodes.put(node, nodeInstance);
146     return nodeInstance;
147   }
148 
149   /**
150    * Frees the specified document.
151    * This removes all its nodes from the cache.
152    */
freeDocument(final Object doc)153   static void freeDocument(final Object doc)
154   {
155     if (instances == null || doc == null)
156       {
157         return;
158       }
159     instances.remove(doc);
160     //System.out.println("Freed "+instances.remove(doc));
161   }
162 
163   /**
164    * xmlNodePtr
165    */
166   final Object id;
167 
168   Map userData;
169   Map userDataHandlers;
170 
GnomeNode(final Object id)171   GnomeNode(final Object id)
172   {
173     this.id = id;
174   }
175 
getNodeName()176   public native String getNodeName();
177 
getNodeValue()178   public native String getNodeValue()
179     throws DOMException;
180 
setNodeValue(String nodeValue)181   public native void setNodeValue(String nodeValue)
182     throws DOMException;
183 
getNodeType()184   public native short getNodeType();
185 
getParentNode()186   public native Node getParentNode();
187 
getChildNodes()188   public NodeList getChildNodes()
189   {
190     return new GnomeNodeList(id);
191   }
192 
getFirstChild()193   public native Node getFirstChild();
194 
getLastChild()195   public native Node getLastChild();
196 
getPreviousSibling()197   public native Node getPreviousSibling();
198 
getNextSibling()199   public native Node getNextSibling();
200 
getAttributes()201   public NamedNodeMap getAttributes()
202   {
203     return new GnomeNamedNodeMap(id, 0);
204   }
205 
getOwnerDocument()206   public native Document getOwnerDocument();
207 
insertBefore(Node newChild, Node refChild)208   public Node insertBefore(Node newChild, Node refChild)
209     throws DOMException
210   {
211     if (newChild instanceof StandaloneDocumentType)
212       {
213         DocumentType dt = (DocumentType) newChild;
214         newChild = ((GnomeDocument) getOwnerDocument())
215           .createDocumentType(dt.getName(), dt.getPublicId(),
216                               dt.getSystemId());
217       }
218     if (newChild == null)
219       {
220         throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null);
221       }
222     if (!(newChild instanceof GnomeNode))
223       {
224         throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, null);
225       }
226     if (refChild == null || !(refChild instanceof GnomeNode))
227       {
228         throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null);
229       }
230     return xmljInsertBefore(newChild, refChild);
231   }
232 
xmljInsertBefore(Node newChild, Node refChild)233   private native Node xmljInsertBefore(Node newChild, Node refChild)
234     throws DOMException;
235 
replaceChild(Node newChild, Node oldChild)236   public Node replaceChild(Node newChild, Node oldChild)
237     throws DOMException
238   {
239     if (newChild instanceof StandaloneDocumentType)
240       {
241         DocumentType dt = (DocumentType) newChild;
242         newChild = ((GnomeDocument) getOwnerDocument())
243           .createDocumentType(dt.getName(), dt.getPublicId(),
244                               dt.getSystemId());
245       }
246     if (newChild == null)
247       {
248         throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null);
249       }
250     if (!(newChild instanceof GnomeNode))
251       {
252         throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, newChild.toString());
253       }
254     if (oldChild == null || !(oldChild instanceof GnomeNode))
255       {
256         throw new GnomeDOMException(DOMException.NOT_FOUND_ERR, null);
257       }
258     return xmljReplaceChild(newChild, oldChild);
259   }
260 
xmljReplaceChild(Node newChild, Node oldChild)261   private native Node xmljReplaceChild(Node newChild, Node oldChild)
262     throws DOMException;
263 
removeChild(Node oldChild)264   public Node removeChild(Node oldChild)
265     throws DOMException
266   {
267     if (!(oldChild instanceof GnomeNode))
268       {
269         throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, null);
270       }
271     return xmljRemoveChild(oldChild);
272   }
273 
xmljRemoveChild(Node oldChild)274   private native Node xmljRemoveChild(Node oldChild)
275     throws DOMException;
276 
appendChild(Node newChild)277   public Node appendChild(Node newChild)
278     throws DOMException
279   {
280     if (newChild instanceof StandaloneDocumentType)
281       {
282         DocumentType dt = (DocumentType) newChild;
283         newChild = ((GnomeDocument) getOwnerDocument())
284           .createDocumentType(dt.getName(), dt.getPublicId(),
285                               dt.getSystemId());
286       }
287     if (!(newChild instanceof GnomeNode))
288       {
289         throw new GnomeDOMException(DOMException.WRONG_DOCUMENT_ERR, null);
290       }
291     return xmljAppendChild(newChild);
292   }
293 
xmljAppendChild(Node newChild)294   private native Node xmljAppendChild(Node newChild)
295     throws DOMException;
296 
hasChildNodes()297   public native boolean hasChildNodes();
298 
cloneNode(boolean deep)299   public Node cloneNode(boolean deep)
300   {
301     Node ret = xmljCloneNode(deep);
302     notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, ret);
303     return ret;
304   }
305 
xmljCloneNode(boolean deep)306   private native Node xmljCloneNode(boolean deep);
307 
normalize()308   public native void normalize();
309 
isSupported(String feature, String version)310   public boolean isSupported(String feature, String version)
311   {
312     return getOwnerDocument().getImplementation()
313       .hasFeature(feature, version);
314   }
315 
getNamespaceURI()316   public native String getNamespaceURI();
317 
getPrefix()318   public native String getPrefix();
319 
setPrefix(String prefix)320   public native void setPrefix(String prefix)
321     throws DOMException;
322 
getLocalName()323   public native String getLocalName();
324 
hasAttributes()325   public native boolean hasAttributes();
326 
hashCode()327   public int hashCode()
328   {
329     return id.hashCode();
330   }
331 
equals(Object other)332   public boolean equals(Object other)
333   {
334     if (other == this)
335       {
336         return true;
337       }
338     return (other instanceof GnomeNode &&
339             ((GnomeNode) other).id == id);
340   }
341 
342   // DOM Level 3 methods
343 
getBaseURI()344   public native String getBaseURI();
345 
compareDocumentPosition(Node other)346   public short compareDocumentPosition(Node other)
347     throws DOMException
348   {
349     return (short) compareTo(other);
350   }
351 
compareTo(Object other)352   public final int compareTo(Object other)
353   {
354     if (other instanceof GnomeNode)
355       {
356         return xmljCompareTo(other);
357       }
358     return 0;
359   }
360 
xmljCompareTo(Object other)361   private native int xmljCompareTo(Object other);
362 
getTextContent()363   public String getTextContent()
364     throws DOMException
365   {
366     switch (getNodeType())
367       {
368       case ELEMENT_NODE:
369       case ATTRIBUTE_NODE:
370       case ENTITY_NODE:
371       case ENTITY_REFERENCE_NODE:
372       case DOCUMENT_FRAGMENT_NODE:
373         CPStringBuilder buffer = new CPStringBuilder();
374         NodeList children = getChildNodes();
375         int len = children.getLength();
376         for (int i = 0; i < len; i++)
377           {
378             Node child = children.item(i);
379             String textContent = child.getTextContent();
380             if (textContent != null)
381               {
382                 buffer.append(textContent);
383               }
384           }
385         return buffer.toString();
386       case TEXT_NODE:
387       case CDATA_SECTION_NODE:
388       case COMMENT_NODE:
389       case PROCESSING_INSTRUCTION_NODE:
390         return getNodeValue();
391       default:
392         return null;
393       }
394   }
395 
setTextContent(String textContent)396   public void setTextContent(String textContent)
397     throws DOMException
398   {
399     switch (getNodeType())
400       {
401       case ENTITY_REFERENCE_NODE:
402         // entity references are read only
403         throw new GnomeDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
404                                     null);
405       case ELEMENT_NODE:
406       case ATTRIBUTE_NODE:
407       case ENTITY_NODE:
408       case DOCUMENT_FRAGMENT_NODE:
409         NodeList children = getChildNodes();
410         int len = children.getLength();
411         for (int i = 0; i < len; i++)
412           {
413             Node child = children.item(i);
414             removeChild(child);
415           }
416         if (textContent != null)
417           {
418             Text text = getOwnerDocument().createTextNode(textContent);
419             appendChild(text);
420           }
421         break;
422       case TEXT_NODE:
423       case CDATA_SECTION_NODE:
424       case COMMENT_NODE:
425       case PROCESSING_INSTRUCTION_NODE:
426         setNodeValue(textContent);
427         break;
428       }
429   }
430 
isSameNode(Node other)431   public boolean isSameNode(Node other)
432   {
433     return equals(other);
434   }
435 
lookupPrefix(String namespaceURI)436   public native String lookupPrefix(String namespaceURI);
437 
isDefaultNamespace(String namespaceURI)438   public native boolean isDefaultNamespace(String namespaceURI);
439 
lookupNamespaceURI(String prefix)440   public native String lookupNamespaceURI(String prefix);
441 
isEqualNode(Node arg)442   public native boolean isEqualNode(Node arg);
443 
getFeature(String feature, String version)444   public Object getFeature(String feature, String version)
445   {
446     return getOwnerDocument().getImplementation()
447       .getFeature(feature, version);
448   }
449 
setUserData(String key, Object data, UserDataHandler handler)450   public Object setUserData(String key, Object data, UserDataHandler handler)
451   {
452     // TODO handler
453     if (userData == null)
454       {
455         userData = new HashMap();
456       }
457     if (handler != null)
458       {
459         if (userDataHandlers == null)
460           {
461             userDataHandlers = new HashMap();
462           }
463         userDataHandlers.put(key, handler);
464       }
465     return userData.put(key, data);
466   }
467 
getUserData(String key)468   public Object getUserData(String key)
469   {
470     if (userData == null)
471       {
472         return null;
473       }
474     return userData.get(key);
475   }
476 
notifyUserDataHandlers(short op, Node src, Node dst)477   void notifyUserDataHandlers(short op, Node src, Node dst)
478   {
479     if (userDataHandlers != null)
480       {
481         for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
482           {
483             Map.Entry entry = (Map.Entry) i.next();
484             String key = (String) entry.getKey();
485             UserDataHandler handler = (UserDataHandler) entry.getValue();
486             Object data = userData.get(key);
487             handler.handle(op, key, data, src, dst);
488           }
489       }
490   }
491 
toString()492   public String toString()
493   {
494     CPStringBuilder buffer = new CPStringBuilder(getClass().getName());
495     buffer.append("[nodeName=");
496     buffer.append(getNodeName());
497     buffer.append("]");
498     return buffer.toString();
499   }
500 
501 }
502