001: /*
002: * Java HTML Tidy - JTidy
003: * HTML parser and pretty printer
004: *
005: * Copyright (c) 1998-2000 World Wide Web Consortium (Massachusetts
006: * Institute of Technology, Institut National de Recherche en
007: * Informatique et en Automatique, Keio University). All Rights
008: * Reserved.
009: *
010: * Contributing Author(s):
011: *
012: * Dave Raggett <dsr@w3.org>
013: * Andy Quick <ac.quick@sympatico.ca> (translation to Java)
014: * Gary L Peskin <garyp@firstech.com> (Java development)
015: * Sami Lempinen <sami@lempinen.net> (release management)
016: * Fabrizio Giustina <fgiust at users.sourceforge.net>
017: *
018: * The contributing author(s) would like to thank all those who
019: * helped with testing, bug fixes, and patience. This wouldn't
020: * have been possible without all of you.
021: *
022: * COPYRIGHT NOTICE:
023: *
024: * This software and documentation is provided "as is," and
025: * the copyright holders and contributing author(s) make no
026: * representations or warranties, express or implied, including
027: * but not limited to, warranties of merchantability or fitness
028: * for any particular purpose or that the use of the software or
029: * documentation will not infringe any third party patents,
030: * copyrights, trademarks or other rights.
031: *
032: * The copyright holders and contributing author(s) will not be
033: * liable for any direct, indirect, special or consequential damages
034: * arising out of any use of the software or documentation, even if
035: * advised of the possibility of such damage.
036: *
037: * Permission is hereby granted to use, copy, modify, and distribute
038: * this source code, or portions hereof, documentation and executables,
039: * for any purpose, without fee, subject to the following restrictions:
040: *
041: * 1. The origin of this source code must not be misrepresented.
042: * 2. Altered versions must be plainly marked as such and must
043: * not be misrepresented as being the original source.
044: * 3. This Copyright notice may not be removed or altered from any
045: * source or altered source distribution.
046: *
047: * The copyright holders and contributing author(s) specifically
048: * permit, without fee, and encourage the use of this source code
049: * as a component for supporting the Hypertext Markup Language in
050: * commercial products. If you use this source code in a product,
051: * acknowledgment is not required but would be appreciated.
052: *
053: */
054:
055: package org.w3c.tidy;
056:
057: import org.w3c.dom.DOMException;
058: import org.w3c.dom.UserDataHandler;
059:
060: /**
061: * DOMNodeImpl.
062: * @author Dave Raggett <a href="mailto:dsr@w3.org">dsr@w3.org </a>
063: * @author Andy Quick <a href="mailto:ac.quick@sympatico.ca">ac.quick@sympatico.ca </a> (translation to Java)
064: * @author Fabrizio Giustina
065: * @version $Revision: 1.15 $ ($Author: fgiust $)
066: */
067: public class DOMNodeImpl implements org.w3c.dom.Node {
068:
069: /**
070: * Wrapped tidy node.
071: */
072: protected Node adaptee;
073:
074: /**
075: * Intantiates a new DOM node.
076: * @param adaptee wrapped Tidy node
077: */
078: protected DOMNodeImpl(Node adaptee) {
079: this .adaptee = adaptee;
080: }
081:
082: /**
083: * @see org.w3c.dom.Node#getNodeValue
084: */
085: public String getNodeValue() {
086: String value = ""; // BAK 10/10/2000 replaced null
087: if (adaptee.type == Node.TEXT_NODE
088: || adaptee.type == Node.CDATA_TAG
089: || adaptee.type == Node.COMMENT_TAG
090: || adaptee.type == Node.PROC_INS_TAG) {
091:
092: if (adaptee.textarray != null
093: && adaptee.start < adaptee.end) {
094: value = TidyUtils.getString(adaptee.textarray,
095: adaptee.start, adaptee.end - adaptee.start);
096: }
097: }
098: return value;
099: }
100:
101: /**
102: * @see org.w3c.dom.Node#setNodeValue
103: */
104: public void setNodeValue(String nodeValue) {
105: if (adaptee.type == Node.TEXT_NODE
106: || adaptee.type == Node.CDATA_TAG
107: || adaptee.type == Node.COMMENT_TAG
108: || adaptee.type == Node.PROC_INS_TAG) {
109: byte[] textarray = TidyUtils.getBytes(nodeValue);
110: adaptee.textarray = textarray;
111: adaptee.start = 0;
112: adaptee.end = textarray.length;
113: }
114: }
115:
116: /**
117: * @see org.w3c.dom.Node#getNodeName
118: */
119: public String getNodeName() {
120: return adaptee.element;
121: }
122:
123: /**
124: * @see org.w3c.dom.Node#getNodeType
125: */
126: public short getNodeType() {
127: short result = -1;
128: switch (adaptee.type) {
129: case Node.ROOT_NODE:
130: result = org.w3c.dom.Node.DOCUMENT_NODE;
131: break;
132: case Node.DOCTYPE_TAG:
133: result = org.w3c.dom.Node.DOCUMENT_TYPE_NODE;
134: break;
135: case Node.COMMENT_TAG:
136: result = org.w3c.dom.Node.COMMENT_NODE;
137: break;
138: case Node.PROC_INS_TAG:
139: result = org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE;
140: break;
141: case Node.TEXT_NODE:
142: result = org.w3c.dom.Node.TEXT_NODE;
143: break;
144: case Node.CDATA_TAG:
145: result = org.w3c.dom.Node.CDATA_SECTION_NODE;
146: break;
147: case Node.START_TAG:
148: case Node.START_END_TAG:
149: result = org.w3c.dom.Node.ELEMENT_NODE;
150: break;
151: }
152: return result;
153: }
154:
155: /**
156: * @see org.w3c.dom.Node#getParentNode
157: */
158: public org.w3c.dom.Node getParentNode() {
159: // Attributes are not children in the DOM, and do not have parents
160: if (adaptee.parent != null) {
161: return adaptee.parent.getAdapter();
162: }
163: return null;
164: }
165:
166: /**
167: * @see org.w3c.dom.Node#getChildNodes
168: */
169: public org.w3c.dom.NodeList getChildNodes() {
170: return new DOMNodeListImpl(adaptee);
171: }
172:
173: /**
174: * @see org.w3c.dom.Node#getFirstChild
175: */
176: public org.w3c.dom.Node getFirstChild() {
177: if (adaptee.content != null) {
178: return adaptee.content.getAdapter();
179: }
180: return null;
181: }
182:
183: /**
184: * @see org.w3c.dom.Node#getLastChild
185: */
186: public org.w3c.dom.Node getLastChild() {
187: if (adaptee.last != null) {
188: return adaptee.last.getAdapter();
189: }
190: return null;
191: }
192:
193: /**
194: * @see org.w3c.dom.Node#getPreviousSibling
195: */
196: public org.w3c.dom.Node getPreviousSibling() {
197: if (adaptee.prev != null) {
198: return adaptee.prev.getAdapter();
199: }
200: return null;
201: }
202:
203: /**
204: * @see org.w3c.dom.Node#getNextSibling
205: */
206: public org.w3c.dom.Node getNextSibling() {
207: if (adaptee.next != null) {
208: return adaptee.next.getAdapter();
209: }
210: return null;
211: }
212:
213: /**
214: * @see org.w3c.dom.Node#getAttributes
215: */
216: public org.w3c.dom.NamedNodeMap getAttributes() {
217: return new DOMAttrMapImpl(adaptee.attributes);
218: }
219:
220: /**
221: * @see org.w3c.dom.Node#getOwnerDocument
222: */
223: public org.w3c.dom.Document getOwnerDocument() {
224: Node node = this .adaptee;
225: if (node != null && node.type == Node.ROOT_NODE) {
226: return null;
227: }
228:
229: while (node != null && node.type != Node.ROOT_NODE) {
230: node = node.parent;
231: }
232:
233: if (node != null) {
234: return (org.w3c.dom.Document) node.getAdapter();
235: }
236: return null;
237: }
238:
239: /**
240: * @see org.w3c.dom.Node#insertBefore
241: */
242: public org.w3c.dom.Node insertBefore(org.w3c.dom.Node newChild,
243: org.w3c.dom.Node refChild) {
244: // TODO - handle newChild already in tree
245:
246: if (newChild == null) {
247: return null;
248: }
249: if (!(newChild instanceof DOMNodeImpl)) {
250: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
251: "newChild not instanceof DOMNodeImpl");
252: }
253: DOMNodeImpl newCh = (DOMNodeImpl) newChild;
254:
255: if (this .adaptee.type == Node.ROOT_NODE) {
256: if (newCh.adaptee.type != Node.DOCTYPE_TAG
257: && newCh.adaptee.type != Node.PROC_INS_TAG) {
258: throw new DOMException(
259: DOMException.HIERARCHY_REQUEST_ERR,
260: "newChild cannot be a child of this node");
261: }
262: } else if (this .adaptee.type == Node.START_TAG) {
263: if (newCh.adaptee.type != Node.START_TAG
264: && newCh.adaptee.type != Node.START_END_TAG
265: && newCh.adaptee.type != Node.COMMENT_TAG
266: && newCh.adaptee.type != Node.TEXT_NODE
267: && newCh.adaptee.type != Node.CDATA_TAG) {
268: throw new DOMException(
269: DOMException.HIERARCHY_REQUEST_ERR,
270: "newChild cannot be a child of this node");
271: }
272: }
273: if (refChild == null) {
274: this .adaptee.insertNodeAtEnd(newCh.adaptee);
275: if (this .adaptee.type == Node.START_END_TAG) {
276: this .adaptee.setType(Node.START_TAG);
277: }
278: } else {
279: Node ref = this .adaptee.content;
280: while (ref != null) {
281: if (ref.getAdapter() == refChild) {
282: break;
283: }
284: ref = ref.next;
285: }
286: if (ref == null) {
287: throw new DOMException(DOMException.NOT_FOUND_ERR,
288: "refChild not found");
289: }
290: Node.insertNodeBeforeElement(ref, newCh.adaptee);
291: }
292: return newChild;
293: }
294:
295: /**
296: * @see org.w3c.dom.Node#replaceChild
297: */
298: public org.w3c.dom.Node replaceChild(org.w3c.dom.Node newChild,
299: org.w3c.dom.Node oldChild) {
300: // TODO - handle newChild already in tree
301:
302: if (newChild == null) {
303: return null;
304: }
305: if (!(newChild instanceof DOMNodeImpl)) {
306: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
307: "newChild not instanceof DOMNodeImpl");
308: }
309: DOMNodeImpl newCh = (DOMNodeImpl) newChild;
310:
311: if (this .adaptee.type == Node.ROOT_NODE) {
312: if (newCh.adaptee.type != Node.DOCTYPE_TAG
313: && newCh.adaptee.type != Node.PROC_INS_TAG) {
314: throw new DOMException(
315: DOMException.HIERARCHY_REQUEST_ERR,
316: "newChild cannot be a child of this node");
317: }
318: } else if (this .adaptee.type == Node.START_TAG) {
319: if (newCh.adaptee.type != Node.START_TAG
320: && newCh.adaptee.type != Node.START_END_TAG
321: && newCh.adaptee.type != Node.COMMENT_TAG
322: && newCh.adaptee.type != Node.TEXT_NODE
323: && newCh.adaptee.type != Node.CDATA_TAG) {
324: throw new DOMException(
325: DOMException.HIERARCHY_REQUEST_ERR,
326: "newChild cannot be a child of this node");
327: }
328: }
329: if (oldChild == null) {
330: throw new DOMException(DOMException.NOT_FOUND_ERR,
331: "oldChild not found");
332: }
333:
334: Node n;
335: Node ref = this .adaptee.content;
336: while (ref != null) {
337: if (ref.getAdapter() == oldChild) {
338: break;
339: }
340: ref = ref.next;
341: }
342: if (ref == null) {
343: throw new DOMException(DOMException.NOT_FOUND_ERR,
344: "oldChild not found");
345: }
346: newCh.adaptee.next = ref.next;
347: newCh.adaptee.prev = ref.prev;
348: newCh.adaptee.last = ref.last;
349: newCh.adaptee.parent = ref.parent;
350: newCh.adaptee.content = ref.content;
351: if (ref.parent != null) {
352: if (ref.parent.content == ref) {
353: ref.parent.content = newCh.adaptee;
354: }
355: if (ref.parent.last == ref) {
356: ref.parent.last = newCh.adaptee;
357: }
358: }
359: if (ref.prev != null) {
360: ref.prev.next = newCh.adaptee;
361: }
362: if (ref.next != null) {
363: ref.next.prev = newCh.adaptee;
364: }
365: for (n = ref.content; n != null; n = n.next) {
366: if (n.parent == ref) {
367: n.parent = newCh.adaptee;
368: }
369: }
370:
371: return oldChild;
372: }
373:
374: /**
375: * @see org.w3c.dom.Node#removeChild
376: */
377: public org.w3c.dom.Node removeChild(org.w3c.dom.Node oldChild) {
378: if (oldChild == null) {
379: return null;
380: }
381:
382: Node ref = this .adaptee.content;
383: while (ref != null) {
384: if (ref.getAdapter() == oldChild) {
385: break;
386: }
387: ref = ref.next;
388: }
389: if (ref == null) {
390: throw new DOMException(DOMException.NOT_FOUND_ERR,
391: "refChild not found");
392: }
393: Node.discardElement(ref);
394:
395: if (this .adaptee.content == null
396: && this .adaptee.type == Node.START_TAG) {
397: this .adaptee.setType(Node.START_END_TAG);
398: }
399:
400: return oldChild;
401: }
402:
403: /**
404: * @see org.w3c.dom.Node#appendChild
405: */
406: public org.w3c.dom.Node appendChild(org.w3c.dom.Node newChild) {
407: // TODO - handle newChild already in tree
408:
409: if (newChild == null) {
410: return null;
411: }
412: if (!(newChild instanceof DOMNodeImpl)) {
413: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
414: "newChild not instanceof DOMNodeImpl");
415: }
416: DOMNodeImpl newCh = (DOMNodeImpl) newChild;
417:
418: if (this .adaptee.type == Node.ROOT_NODE) {
419: if (newCh.adaptee.type != Node.DOCTYPE_TAG
420: && newCh.adaptee.type != Node.PROC_INS_TAG) {
421: throw new DOMException(
422: DOMException.HIERARCHY_REQUEST_ERR,
423: "newChild cannot be a child of this node");
424: }
425: } else if (this .adaptee.type == Node.START_TAG) {
426: if (newCh.adaptee.type != Node.START_TAG
427: && newCh.adaptee.type != Node.START_END_TAG
428: && newCh.adaptee.type != Node.COMMENT_TAG
429: && newCh.adaptee.type != Node.TEXT_NODE
430: && newCh.adaptee.type != Node.CDATA_TAG) {
431: throw new DOMException(
432: DOMException.HIERARCHY_REQUEST_ERR,
433: "newChild cannot be a child of this node");
434: }
435: }
436: this .adaptee.insertNodeAtEnd(newCh.adaptee);
437:
438: if (this .adaptee.type == Node.START_END_TAG) {
439: this .adaptee.setType(Node.START_TAG);
440: }
441:
442: return newChild;
443: }
444:
445: /**
446: * @see org.w3c.dom.Node#hasChildNodes
447: */
448: public boolean hasChildNodes() {
449: return (adaptee.content != null);
450: }
451:
452: /**
453: * @see org.w3c.dom.Node#cloneNode(boolean)
454: */
455: public org.w3c.dom.Node cloneNode(boolean deep) {
456: Node node = adaptee.cloneNode(deep);
457: node.parent = null;
458: return node.getAdapter();
459: }
460:
461: /**
462: * Do nothing: text nodes in html documents are important and jtidy already removes useless text during parsing.
463: * @see org.w3c.dom.Node#normalize()
464: */
465: public void normalize() {
466: // do nothing
467: }
468:
469: /**
470: * DOM2 - not implemented.
471: * @see #isSupported(java.lang.String, java.lang.String)
472: */
473: public boolean supports(String feature, String version) {
474: return isSupported(feature, version);
475: }
476:
477: /**
478: * @see org.w3c.dom.Node#getNamespaceURI()
479: */
480: public String getNamespaceURI() {
481: return null;
482: }
483:
484: /**
485: * @see org.w3c.dom.Node#getPrefix()
486: */
487: public String getPrefix() {
488: return null;
489: }
490:
491: /**
492: * @see org.w3c.dom.Node#setPrefix(java.lang.String)
493: */
494: public void setPrefix(String prefix) throws DOMException {
495: // The namespace prefix of this node, or null if it is unspecified. When it is defined to be null, setting it
496: // has no effect, including if the node is read-only.
497: // do nothing
498: }
499:
500: /**
501: * @see org.w3c.dom.Node#getLocalName()
502: */
503: public String getLocalName() {
504: return getNodeName();
505: }
506:
507: /**
508: * @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String)
509: */
510: public boolean isSupported(String feature, String version) {
511: return false;
512: }
513:
514: /**
515: * @see org.w3c.dom.Node#hasAttributes
516: */
517: public boolean hasAttributes() {
518: // contributed by dlp@users.sourceforge.net
519: return this .adaptee.attributes != null;
520: }
521:
522: /**
523: * @todo DOM level 3 compareDocumentPosition() Not implemented.
524: * @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
525: */
526: public short compareDocumentPosition(org.w3c.dom.Node other)
527: throws DOMException {
528: throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
529: "DOM method not supported");
530: }
531:
532: /**
533: * @todo DOM level 3 getBaseURI() Not implemented. Returns null.
534: * @see org.w3c.dom.Node#getBaseURI()
535: */
536: public String getBaseURI() {
537: return null;
538: }
539:
540: /**
541: * @todo DOM level 3 getFeature() Not implemented. Returns null.
542: * @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String)
543: */
544: public Object getFeature(String feature, String version) {
545: return null;
546: }
547:
548: /**
549: * @todo DOM level 3 getTextContent() Not implemented. Returns null.
550: * @see org.w3c.dom.Node#getTextContent()
551: */
552: public String getTextContent() throws DOMException {
553: return null;
554: }
555:
556: /**
557: * @todo DOM level 3 getUserData() Not implemented. Returns null.
558: * @see org.w3c.dom.Node#getUserData(java.lang.String)
559: */
560: public Object getUserData(String key) {
561: return null;
562: }
563:
564: /**
565: * @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String)
566: */
567: public boolean isDefaultNamespace(String namespaceURI) {
568: return false;
569: }
570:
571: /**
572: * @todo DOM level 3 isEqualNode() Not implemented. Returns false.
573: * @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node)
574: */
575: public boolean isEqualNode(org.w3c.dom.Node arg) {
576: return false;
577: }
578:
579: /**
580: * @todo DOM level 3 isSameNode() Not implemented. Returns false.
581: * @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node)
582: */
583: public boolean isSameNode(org.w3c.dom.Node other) {
584: return false;
585: }
586:
587: /**
588: * @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String)
589: */
590: public String lookupNamespaceURI(String prefix) {
591: return null;
592: }
593:
594: /**
595: * @see org.w3c.dom.Node#lookupPrefix(java.lang.String)
596: */
597: public String lookupPrefix(String namespaceURI) {
598: return null;
599: }
600:
601: /**
602: * @todo DOM level 3 setTextContent() Not implemented. Throws NO_MODIFICATION_ALLOWED_ERR
603: * @see org.w3c.dom.Node#setTextContent(java.lang.String)
604: */
605: public void setTextContent(String textContent) throws DOMException {
606: throw new DOMException(
607: DOMException.NO_MODIFICATION_ALLOWED_ERR,
608: "Node is read only");
609: }
610:
611: /**
612: * @todo DOM level 3 setUserData() Not implemented. Returns null.
613: * @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
614: */
615: public Object setUserData(String key, Object data,
616: UserDataHandler handler) {
617: return null;
618: }
619: }
|