View Javadoc

1   /*
2    *  Java HTML Tidy - JTidy
3    *  HTML parser and pretty printer
4    *
5    *  Copyright (c) 1998-2000 World Wide Web Consortium (Massachusetts
6    *  Institute of Technology, Institut National de Recherche en
7    *  Informatique et en Automatique, Keio University). All Rights
8    *  Reserved.
9    *
10   *  Contributing Author(s):
11   *
12   *     Dave Raggett <dsr@w3.org>
13   *     Andy Quick <ac.quick@sympatico.ca> (translation to Java)
14   *     Gary L Peskin <garyp@firstech.com> (Java development)
15   *     Sami Lempinen <sami@lempinen.net> (release management)
16   *     Fabrizio Giustina <fgiust at users.sourceforge.net>
17   *
18   *  The contributing author(s) would like to thank all those who
19   *  helped with testing, bug fixes, and patience.  This wouldn't
20   *  have been possible without all of you.
21   *
22   *  COPYRIGHT NOTICE:
23   * 
24   *  This software and documentation is provided "as is," and
25   *  the copyright holders and contributing author(s) make no
26   *  representations or warranties, express or implied, including
27   *  but not limited to, warranties of merchantability or fitness
28   *  for any particular purpose or that the use of the software or
29   *  documentation will not infringe any third party patents,
30   *  copyrights, trademarks or other rights. 
31   *
32   *  The copyright holders and contributing author(s) will not be
33   *  liable for any direct, indirect, special or consequential damages
34   *  arising out of any use of the software or documentation, even if
35   *  advised of the possibility of such damage.
36   *
37   *  Permission is hereby granted to use, copy, modify, and distribute
38   *  this source code, or portions hereof, documentation and executables,
39   *  for any purpose, without fee, subject to the following restrictions:
40   *
41   *  1. The origin of this source code must not be misrepresented.
42   *  2. Altered versions must be plainly marked as such and must
43   *     not be misrepresented as being the original source.
44   *  3. This Copyright notice may not be removed or altered from any
45   *     source or altered source distribution.
46   * 
47   *  The copyright holders and contributing author(s) specifically
48   *  permit, without fee, and encourage the use of this source code
49   *  as a component for supporting the Hypertext Markup Language in
50   *  commercial products. If you use this source code in a product,
51   *  acknowledgment is not required but would be appreciated.
52   *
53   */
54  
55  package org.w3c.tidy;
56  
57  import org.w3c.dom.DOMException;
58  import org.w3c.dom.UserDataHandler;
59  
60  
61  /**
62   * DOMNodeImpl.
63   * @author Dave Raggett <a href="mailto:dsr@w3.org">dsr@w3.org </a>
64   * @author Andy Quick <a href="mailto:ac.quick@sympatico.ca">ac.quick@sympatico.ca </a> (translation to Java)
65   * @author Fabrizio Giustina
66   * @version $Revision: 779 $ ($Author: fgiust $)
67   */
68  public class DOMNodeImpl implements org.w3c.dom.Node
69  {
70  
71      /**
72       * Wrapped tidy node.
73       */
74      protected Node adaptee;
75  
76      /**
77       * Intantiates a new DOM node.
78       * @param adaptee wrapped Tidy node
79       */
80      protected DOMNodeImpl(Node adaptee)
81      {
82          this.adaptee = adaptee;
83      }
84  
85      /**
86       * @see org.w3c.dom.Node#getNodeValue
87       */
88      public String getNodeValue()
89      {
90          String value = ""; // BAK 10/10/2000 replaced null
91          if (adaptee.type == Node.TEXT_NODE
92              || adaptee.type == Node.CDATA_TAG
93              || adaptee.type == Node.COMMENT_TAG
94              || adaptee.type == Node.PROC_INS_TAG)
95          {
96  
97              if (adaptee.textarray != null && adaptee.start < adaptee.end)
98              {
99                  value = TidyUtils.getString(adaptee.textarray, adaptee.start, adaptee.end - adaptee.start);
100             }
101         }
102         return value;
103     }
104 
105     /**
106      * @see org.w3c.dom.Node#setNodeValue
107      */
108     public void setNodeValue(String nodeValue)
109     {
110         if (adaptee.type == Node.TEXT_NODE
111             || adaptee.type == Node.CDATA_TAG
112             || adaptee.type == Node.COMMENT_TAG
113             || adaptee.type == Node.PROC_INS_TAG)
114         {
115             byte[] textarray = TidyUtils.getBytes(nodeValue);
116             adaptee.textarray = textarray;
117             adaptee.start = 0;
118             adaptee.end = textarray.length;
119         }
120     }
121 
122     /**
123      * @see org.w3c.dom.Node#getNodeName
124      */
125     public String getNodeName()
126     {
127         return adaptee.element;
128     }
129 
130     /**
131      * @see org.w3c.dom.Node#getNodeType
132      */
133     public short getNodeType()
134     {
135         short result = -1;
136         switch (adaptee.type)
137         {
138             case Node.ROOT_NODE :
139                 result = org.w3c.dom.Node.DOCUMENT_NODE;
140                 break;
141             case Node.DOCTYPE_TAG :
142                 result = org.w3c.dom.Node.DOCUMENT_TYPE_NODE;
143                 break;
144             case Node.COMMENT_TAG :
145                 result = org.w3c.dom.Node.COMMENT_NODE;
146                 break;
147             case Node.PROC_INS_TAG :
148                 result = org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE;
149                 break;
150             case Node.TEXT_NODE :
151                 result = org.w3c.dom.Node.TEXT_NODE;
152                 break;
153             case Node.CDATA_TAG :
154                 result = org.w3c.dom.Node.CDATA_SECTION_NODE;
155                 break;
156             case Node.START_TAG :
157             case Node.START_END_TAG :
158                 result = org.w3c.dom.Node.ELEMENT_NODE;
159                 break;
160         }
161         return result;
162     }
163 
164     /**
165      * @see org.w3c.dom.Node#getParentNode
166      */
167     public org.w3c.dom.Node getParentNode()
168     {
169         // Attributes are not children in the DOM, and do not have parents
170         if (adaptee.parent != null)
171         {
172             return adaptee.parent.getAdapter();
173         }
174         return null;
175     }
176 
177     /**
178      * @see org.w3c.dom.Node#getChildNodes
179      */
180     public org.w3c.dom.NodeList getChildNodes()
181     {
182         return new DOMNodeListImpl(adaptee);
183     }
184 
185     /**
186      * @see org.w3c.dom.Node#getFirstChild
187      */
188     public org.w3c.dom.Node getFirstChild()
189     {
190         if (adaptee.content != null)
191         {
192             return adaptee.content.getAdapter();
193         }
194         return null;
195     }
196 
197     /**
198      * @see org.w3c.dom.Node#getLastChild
199      */
200     public org.w3c.dom.Node getLastChild()
201     {
202         if (adaptee.last != null)
203         {
204             return adaptee.last.getAdapter();
205         }
206         return null;
207     }
208 
209     /**
210      * @see org.w3c.dom.Node#getPreviousSibling
211      */
212     public org.w3c.dom.Node getPreviousSibling()
213     {
214         if (adaptee.prev != null)
215         {
216             return adaptee.prev.getAdapter();
217         }
218         return null;
219     }
220 
221     /**
222      * @see org.w3c.dom.Node#getNextSibling
223      */
224     public org.w3c.dom.Node getNextSibling()
225     {
226         if (adaptee.next != null)
227         {
228             return adaptee.next.getAdapter();
229         }
230         return null;
231     }
232 
233     /**
234      * @see org.w3c.dom.Node#getAttributes
235      */
236     public org.w3c.dom.NamedNodeMap getAttributes()
237     {
238         return new DOMAttrMapImpl(adaptee.attributes);
239     }
240 
241     /**
242      * @see org.w3c.dom.Node#getOwnerDocument
243      */
244     public org.w3c.dom.Document getOwnerDocument()
245     {
246         Node node = this.adaptee;
247         if (node != null && node.type == Node.ROOT_NODE)
248         {
249             return null;
250         }
251 
252         while (node != null && node.type != Node.ROOT_NODE)
253         {
254             node = node.parent;
255         }
256 
257         if (node != null)
258         {
259             return (org.w3c.dom.Document) node.getAdapter();
260         }
261         return null;
262     }
263 
264     /**
265      * @see org.w3c.dom.Node#insertBefore
266      */
267     public org.w3c.dom.Node insertBefore(org.w3c.dom.Node newChild, org.w3c.dom.Node refChild)
268     {
269         // TODO - handle newChild already in tree
270 
271         if (newChild == null)
272         {
273             return null;
274         }
275         if (!(newChild instanceof DOMNodeImpl))
276         {
277             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "newChild not instanceof DOMNodeImpl");
278         }
279         DOMNodeImpl newCh = (DOMNodeImpl) newChild;
280 
281         if (this.adaptee.type == Node.ROOT_NODE)
282         {
283             if (newCh.adaptee.type != Node.DOCTYPE_TAG && newCh.adaptee.type != Node.PROC_INS_TAG)
284             {
285                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "newChild cannot be a child of this node");
286             }
287         }
288         else if (this.adaptee.type == Node.START_TAG)
289         {
290             if (newCh.adaptee.type != Node.START_TAG
291                 && newCh.adaptee.type != Node.START_END_TAG
292                 && newCh.adaptee.type != Node.COMMENT_TAG
293                 && newCh.adaptee.type != Node.TEXT_NODE
294                 && newCh.adaptee.type != Node.CDATA_TAG)
295             {
296                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "newChild cannot be a child of this node");
297             }
298         }
299         if (refChild == null)
300         {
301             this.adaptee.insertNodeAtEnd(newCh.adaptee);
302             if (this.adaptee.type == Node.START_END_TAG)
303             {
304                 this.adaptee.setType(Node.START_TAG);
305             }
306         }
307         else
308         {
309             Node ref = this.adaptee.content;
310             while (ref != null)
311             {
312                 if (ref.getAdapter() == refChild)
313                 {
314                     break;
315                 }
316                 ref = ref.next;
317             }
318             if (ref == null)
319             {
320                 throw new DOMException(DOMException.NOT_FOUND_ERR, "refChild not found");
321             }
322             Node.insertNodeBeforeElement(ref, newCh.adaptee);
323         }
324         return newChild;
325     }
326 
327     /**
328      * @see org.w3c.dom.Node#replaceChild
329      */
330     public org.w3c.dom.Node replaceChild(org.w3c.dom.Node newChild, org.w3c.dom.Node oldChild)
331     {
332         // TODO - handle newChild already in tree
333 
334         if (newChild == null)
335         {
336             return null;
337         }
338         if (!(newChild instanceof DOMNodeImpl))
339         {
340             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "newChild not instanceof DOMNodeImpl");
341         }
342         DOMNodeImpl newCh = (DOMNodeImpl) newChild;
343 
344         if (this.adaptee.type == Node.ROOT_NODE)
345         {
346             if (newCh.adaptee.type != Node.DOCTYPE_TAG && newCh.adaptee.type != Node.PROC_INS_TAG)
347             {
348                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "newChild cannot be a child of this node");
349             }
350         }
351         else if (this.adaptee.type == Node.START_TAG)
352         {
353             if (newCh.adaptee.type != Node.START_TAG
354                 && newCh.adaptee.type != Node.START_END_TAG
355                 && newCh.adaptee.type != Node.COMMENT_TAG
356                 && newCh.adaptee.type != Node.TEXT_NODE
357                 && newCh.adaptee.type != Node.CDATA_TAG)
358             {
359                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "newChild cannot be a child of this node");
360             }
361         }
362         if (oldChild == null)
363         {
364             throw new DOMException(DOMException.NOT_FOUND_ERR, "oldChild not found");
365         }
366 
367         Node n;
368         Node ref = this.adaptee.content;
369         while (ref != null)
370         {
371             if (ref.getAdapter() == oldChild)
372             {
373                 break;
374             }
375             ref = ref.next;
376         }
377         if (ref == null)
378         {
379             throw new DOMException(DOMException.NOT_FOUND_ERR, "oldChild not found");
380         }
381         newCh.adaptee.next = ref.next;
382         newCh.adaptee.prev = ref.prev;
383         newCh.adaptee.last = ref.last;
384         newCh.adaptee.parent = ref.parent;
385         newCh.adaptee.content = ref.content;
386         if (ref.parent != null)
387         {
388             if (ref.parent.content == ref)
389             {
390                 ref.parent.content = newCh.adaptee;
391             }
392             if (ref.parent.last == ref)
393             {
394                 ref.parent.last = newCh.adaptee;
395             }
396         }
397         if (ref.prev != null)
398         {
399             ref.prev.next = newCh.adaptee;
400         }
401         if (ref.next != null)
402         {
403             ref.next.prev = newCh.adaptee;
404         }
405         for (n = ref.content; n != null; n = n.next)
406         {
407             if (n.parent == ref)
408             {
409                 n.parent = newCh.adaptee;
410             }
411         }
412 
413         return oldChild;
414     }
415 
416     /**
417      * @see org.w3c.dom.Node#removeChild
418      */
419     public org.w3c.dom.Node removeChild(org.w3c.dom.Node oldChild)
420     {
421         if (oldChild == null)
422         {
423             return null;
424         }
425 
426         Node ref = this.adaptee.content;
427         while (ref != null)
428         {
429             if (ref.getAdapter() == oldChild)
430             {
431                 break;
432             }
433             ref = ref.next;
434         }
435         if (ref == null)
436         {
437             throw new DOMException(DOMException.NOT_FOUND_ERR, "refChild not found");
438         }
439         Node.discardElement(ref);
440 
441         if (this.adaptee.content == null && this.adaptee.type == Node.START_TAG)
442         {
443             this.adaptee.setType(Node.START_END_TAG);
444         }
445 
446         return oldChild;
447     }
448 
449     /**
450      * @see org.w3c.dom.Node#appendChild
451      */
452     public org.w3c.dom.Node appendChild(org.w3c.dom.Node newChild)
453     {
454         // TODO - handle newChild already in tree
455 
456         if (newChild == null)
457         {
458             return null;
459         }
460         if (!(newChild instanceof DOMNodeImpl))
461         {
462             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "newChild not instanceof DOMNodeImpl");
463         }
464         DOMNodeImpl newCh = (DOMNodeImpl) newChild;
465 
466         if (this.adaptee.type == Node.ROOT_NODE)
467         {
468             if (newCh.adaptee.type != Node.DOCTYPE_TAG && newCh.adaptee.type != Node.PROC_INS_TAG)
469             {
470                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "newChild cannot be a child of this node");
471             }
472         }
473         else if (this.adaptee.type == Node.START_TAG)
474         {
475             if (newCh.adaptee.type != Node.START_TAG
476                 && newCh.adaptee.type != Node.START_END_TAG
477                 && newCh.adaptee.type != Node.COMMENT_TAG
478                 && newCh.adaptee.type != Node.TEXT_NODE
479                 && newCh.adaptee.type != Node.CDATA_TAG)
480             {
481                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "newChild cannot be a child of this node");
482             }
483         }
484         this.adaptee.insertNodeAtEnd(newCh.adaptee);
485 
486         if (this.adaptee.type == Node.START_END_TAG)
487         {
488             this.adaptee.setType(Node.START_TAG);
489         }
490 
491         return newChild;
492     }
493 
494     /**
495      * @see org.w3c.dom.Node#hasChildNodes
496      */
497     public boolean hasChildNodes()
498     {
499         return (adaptee.content != null);
500     }
501 
502     /**
503      * @see org.w3c.dom.Node#cloneNode(boolean)
504      */
505     public org.w3c.dom.Node cloneNode(boolean deep)
506     {
507         Node node = adaptee.cloneNode(deep);
508         node.parent = null;
509         return node.getAdapter();
510     }
511 
512     /**
513      * Do nothing: text nodes in html documents are important and jtidy already removes useless text during parsing.
514      * @see org.w3c.dom.Node#normalize()
515      */
516     public void normalize()
517     {
518         // do nothing
519     }
520 
521     /**
522      * DOM2 - not implemented.
523      * @see #isSupported(java.lang.String, java.lang.String)
524      */
525     public boolean supports(String feature, String version)
526     {
527         return isSupported(feature, version);
528     }
529 
530     /**
531      * @see org.w3c.dom.Node#getNamespaceURI()
532      */
533     public String getNamespaceURI()
534     {
535         return null;
536     }
537 
538     /**
539      * @see org.w3c.dom.Node#getPrefix()
540      */
541     public String getPrefix()
542     {
543         return null;
544     }
545 
546     /**
547      * @see org.w3c.dom.Node#setPrefix(java.lang.String)
548      */
549     public void setPrefix(String prefix) throws DOMException
550     {
551         // The namespace prefix of this node, or null if it is unspecified. When it is defined to be null, setting it
552         // has no effect, including if the node is read-only.
553         // do nothing
554     }
555 
556     /**
557      * @see org.w3c.dom.Node#getLocalName()
558      */
559     public String getLocalName()
560     {
561         return getNodeName();
562     }
563 
564     /**
565      * @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String)
566      */
567     public boolean isSupported(String feature, String version)
568     {
569         return false;
570     }
571 
572     /**
573      * @see org.w3c.dom.Node#hasAttributes
574      */
575     public boolean hasAttributes()
576     {
577         // contributed by dlp@users.sourceforge.net
578         return this.adaptee.attributes != null;
579     }
580 
581     /**
582      * @todo DOM level 3 compareDocumentPosition() Not implemented.
583      * @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
584      */
585     public short compareDocumentPosition(org.w3c.dom.Node other) throws DOMException
586     {
587         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "DOM method not supported");
588     }
589 
590     /**
591      * @todo DOM level 3 getBaseURI() Not implemented. Returns null.
592      * @see org.w3c.dom.Node#getBaseURI()
593      */
594     public String getBaseURI()
595     {
596         return null;
597     }
598 
599     /**
600      * @todo DOM level 3 getFeature() Not implemented. Returns null.
601      * @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String)
602      */
603     public Object getFeature(String feature, String version)
604     {
605         return null;
606     }
607 
608     /**
609      * @todo DOM level 3 getTextContent() Not implemented. Returns null.
610      * @see org.w3c.dom.Node#getTextContent()
611      */
612     public String getTextContent() throws DOMException
613     {
614         return null;
615     }
616 
617     /**
618      * @todo DOM level 3 getUserData() Not implemented. Returns null.
619      * @see org.w3c.dom.Node#getUserData(java.lang.String)
620      */
621     public Object getUserData(String key)
622     {
623         return null;
624     }
625 
626     /**
627      * @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String)
628      */
629     public boolean isDefaultNamespace(String namespaceURI)
630     {
631         return false;
632     }
633 
634     /**
635      * @todo DOM level 3 isEqualNode() Not implemented. Returns false.
636      * @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node)
637      */
638     public boolean isEqualNode(org.w3c.dom.Node arg)
639     {
640         return false;
641     }
642 
643     /**
644      * @todo DOM level 3 isSameNode() Not implemented. Returns false.
645      * @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node)
646      */
647     public boolean isSameNode(org.w3c.dom.Node other)
648     {
649         return false;
650     }
651 
652     /**
653      * @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String)
654      */
655     public String lookupNamespaceURI(String prefix)
656     {
657         return null;
658     }
659 
660     /**
661      * @see org.w3c.dom.Node#lookupPrefix(java.lang.String)
662      */
663     public String lookupPrefix(String namespaceURI)
664     {
665         return null;
666     }
667 
668     /**
669      * @todo DOM level 3 setTextContent() Not implemented. Throws NO_MODIFICATION_ALLOWED_ERR
670      * @see org.w3c.dom.Node#setTextContent(java.lang.String)
671      */
672     public void setTextContent(String textContent) throws DOMException
673     {
674         throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "Node is read only");
675     }
676 
677     /**
678      * @todo DOM level 3 setUserData() Not implemented. Returns null.
679      * @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
680      */
681     public Object setUserData(String key, Object data, UserDataHandler handler)
682     {
683         return null;
684     }
685 }