001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.dom;
020:
021: import java.io.Serializable;
022:
023: import org.apache.batik.dom.events.DOMMutationEvent;
024: import org.apache.batik.util.XMLConstants;
025:
026: import org.w3c.dom.DOMException;
027: import org.w3c.dom.Node;
028: import org.w3c.dom.NodeList;
029: import org.w3c.dom.events.DocumentEvent;
030: import org.w3c.dom.events.MutationEvent;
031:
032: /**
033: * This class implements the Node interface with support for children.
034: *
035: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
036: * @version $Id: AbstractParentNode.java 478249 2006-11-22 17:29:37Z dvholten $
037: */
038: public abstract class AbstractParentNode extends AbstractNode {
039:
040: /**
041: * The children.
042: */
043: protected ChildNodes childNodes;
044:
045: /**
046: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getChildNodes()}.
047: * @return {@link #childNodes}
048: */
049: public NodeList getChildNodes() {
050: return (childNodes == null) ? childNodes = new ChildNodes()
051: : childNodes;
052: }
053:
054: /**
055: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getFirstChild()}.
056: * @return {@link #childNodes}.firstChild
057: */
058: public Node getFirstChild() {
059: return (childNodes == null) ? null : childNodes.firstChild;
060: }
061:
062: /**
063: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getLastChild()}.
064: * @return {@link #childNodes}.lastChild
065: */
066: public Node getLastChild() {
067: return (childNodes == null) ? null : childNodes.lastChild;
068: }
069:
070: /**
071: * <b>DOM</b>: Implements {@link
072: * org.w3c.dom.Node#insertBefore(Node, Node)}.
073: */
074: public Node insertBefore(Node newChild, Node refChild)
075: throws DOMException {
076: if ((refChild != null)
077: && ((childNodes == null) || (refChild.getParentNode() != this )))
078: throw createDOMException(DOMException.NOT_FOUND_ERR,
079: "child.missing", new Object[] {
080: new Integer(refChild.getNodeType()),
081: refChild.getNodeName() });
082:
083: checkAndRemove(newChild, false);
084:
085: if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
086: Node n = newChild.getFirstChild();
087: while (n != null) {
088: Node ns = n.getNextSibling();
089: insertBefore(n, refChild);
090: n = ns;
091: }
092: return newChild;
093: } else {
094: // Node modification
095: if (childNodes == null) {
096: childNodes = new ChildNodes();
097: }
098: ExtendedNode n = childNodes.insert((ExtendedNode) newChild,
099: (ExtendedNode) refChild);
100: n.setParentNode(this );
101:
102: nodeAdded(n);
103:
104: // Mutation event
105: fireDOMNodeInsertedEvent(n);
106: fireDOMSubtreeModifiedEvent();
107: return n;
108: }
109: }
110:
111: /**
112: * <b>DOM</b>: Implements {@link
113: * org.w3c.dom.Node#replaceChild(Node, Node)}.
114: */
115: public Node replaceChild(Node newChild, Node oldChild)
116: throws DOMException {
117: if ((childNodes == null) || (oldChild.getParentNode() != this ))
118: throw createDOMException(DOMException.NOT_FOUND_ERR,
119: "child.missing", new Object[] {
120: new Integer(oldChild.getNodeType()),
121: oldChild.getNodeName() });
122:
123: checkAndRemove(newChild, true);
124:
125: if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
126: Node n = newChild.getLastChild();
127: if (n == null)
128: return newChild;
129:
130: Node ps = n.getPreviousSibling();
131: replaceChild(n, oldChild);
132: Node ns = n;
133: n = ps;
134: while (n != null) {
135: ps = n.getPreviousSibling();
136: insertBefore(n, ns);
137: ns = n;
138: n = ps;
139: }
140:
141: return newChild;
142: }
143:
144: // Mutation event
145: fireDOMNodeRemovedEvent(oldChild);
146:
147: getCurrentDocument().nodeToBeRemoved(oldChild);
148: nodeToBeRemoved(oldChild);
149:
150: // Node modification
151: ExtendedNode n = (ExtendedNode) newChild;
152: ExtendedNode o = childNodes.replace(n, (ExtendedNode) oldChild);
153: n.setParentNode(this );
154: o.setParentNode(null);
155:
156: nodeAdded(n);
157:
158: // Mutation event
159: fireDOMNodeInsertedEvent(n);
160: fireDOMSubtreeModifiedEvent();
161: return n;
162: }
163:
164: /**
165: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#removeChild(Node)}.
166: */
167: public Node removeChild(Node oldChild) throws DOMException {
168: if (childNodes == null || oldChild.getParentNode() != this ) {
169: throw createDOMException(DOMException.NOT_FOUND_ERR,
170: "child.missing", new Object[] {
171: new Integer(oldChild.getNodeType()),
172: oldChild.getNodeName() });
173: }
174: if (isReadonly()) {
175: throw createDOMException(
176: DOMException.NO_MODIFICATION_ALLOWED_ERR,
177: "readonly.node", new Object[] {
178: new Integer(getNodeType()), getNodeName() });
179: }
180:
181: // Mutation event
182: fireDOMNodeRemovedEvent(oldChild);
183:
184: getCurrentDocument().nodeToBeRemoved(oldChild);
185: nodeToBeRemoved(oldChild);
186:
187: // Node modification
188: ExtendedNode result = childNodes
189: .remove((ExtendedNode) oldChild);
190: result.setParentNode(null);
191:
192: // Mutation event
193: fireDOMSubtreeModifiedEvent();
194: return result;
195: }
196:
197: /**
198: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#appendChild(Node)}.
199: */
200: public Node appendChild(Node newChild) throws DOMException {
201: checkAndRemove(newChild, false);
202:
203: if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
204: Node n = newChild.getFirstChild();
205: while (n != null) {
206: Node ns = n.getNextSibling();
207: appendChild(n);
208: n = ns;
209: }
210: return newChild;
211: } else {
212: if (childNodes == null)
213: childNodes = new ChildNodes();
214:
215: // Node modification
216: ExtendedNode n = childNodes.append((ExtendedNode) newChild);
217: n.setParentNode(this );
218:
219: nodeAdded(n);
220:
221: // Mutation event
222: fireDOMNodeInsertedEvent(n);
223: fireDOMSubtreeModifiedEvent();
224: return n;
225: }
226: }
227:
228: /**
229: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#hasChildNodes()}.
230: * @return true if this node has children, false otherwise.
231: */
232: public boolean hasChildNodes() {
233: return childNodes != null && childNodes.getLength() != 0;
234: }
235:
236: /**
237: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#normalize()}.
238: */
239: public void normalize() {
240: Node p = getFirstChild();
241: if (p != null) {
242: p.normalize();
243: Node n = p.getNextSibling();
244: while (n != null) {
245: if (p.getNodeType() == TEXT_NODE
246: && n.getNodeType() == TEXT_NODE) {
247: String s = p.getNodeValue() + n.getNodeValue();
248: AbstractText at = (AbstractText) p;
249: at.setNodeValue(s);
250: removeChild(n);
251: n = p.getNextSibling();
252: } else {
253: n.normalize();
254: p = n;
255: n = n.getNextSibling();
256: }
257: }
258: }
259: }
260:
261: /**
262: * <b>DOM</b>: Implements {@link
263: * org.w3c.dom.Element#getElementsByTagName(String)}.
264: */
265: public NodeList getElementsByTagName(String name) {
266: if (name == null) {
267: return EMPTY_NODE_LIST;
268: }
269: AbstractDocument ad = getCurrentDocument();
270: ElementsByTagName result = ad.getElementsByTagName(this , name);
271: if (result == null) {
272: result = new ElementsByTagName(name);
273: ad.putElementsByTagName(this , name, result);
274: }
275: return result;
276: }
277:
278: /**
279: * <b>DOM</b>: Implements {@link
280: * org.w3c.dom.Element#getElementsByTagNameNS(String,String)}.
281: */
282: public NodeList getElementsByTagNameNS(String namespaceURI,
283: String localName) {
284: if (localName == null) {
285: return EMPTY_NODE_LIST;
286: }
287: if (namespaceURI != null && namespaceURI.length() == 0) {
288: namespaceURI = null;
289: }
290: AbstractDocument ad = getCurrentDocument();
291: ElementsByTagNameNS result = ad.getElementsByTagNameNS(this ,
292: namespaceURI, localName);
293: if (result == null) {
294: result = new ElementsByTagNameNS(namespaceURI, localName);
295: ad.putElementsByTagNameNS(this , namespaceURI, localName,
296: result);
297: }
298: return result;
299: }
300:
301: /**
302: * <b>DOM</b>: Implements {@link org.w3c.dom.Node#getTextContent()}.
303: */
304: public String getTextContent() {
305: StringBuffer sb = new StringBuffer();
306: for (Node n = getFirstChild(); n != null; n = n
307: .getNextSibling()) {
308: switch (n.getNodeType()) {
309: case COMMENT_NODE:
310: case PROCESSING_INSTRUCTION_NODE:
311: break;
312: default:
313: sb.append(((AbstractNode) n).getTextContent());
314: }
315: }
316: return sb.toString();
317: }
318:
319: /**
320: * Recursively fires a DOMNodeInsertedIntoDocument event.
321: */
322: public void fireDOMNodeInsertedIntoDocumentEvent() {
323: AbstractDocument doc = getCurrentDocument();
324: if (doc.getEventsEnabled()) {
325: super .fireDOMNodeInsertedIntoDocumentEvent();
326: for (Node n = getFirstChild(); n != null; n = n
327: .getNextSibling()) {
328: ((AbstractNode) n)
329: .fireDOMNodeInsertedIntoDocumentEvent();
330: }
331: }
332: }
333:
334: /**
335: * Recursively fires a DOMNodeRemovedFromDocument event.
336: */
337: public void fireDOMNodeRemovedFromDocumentEvent() {
338: AbstractDocument doc = getCurrentDocument();
339: if (doc.getEventsEnabled()) {
340: super .fireDOMNodeRemovedFromDocumentEvent();
341: for (Node n = getFirstChild(); n != null; n = n
342: .getNextSibling()) {
343: ((AbstractNode) n)
344: .fireDOMNodeRemovedFromDocumentEvent();
345: }
346: }
347: }
348:
349: /**
350: * Called when a child node has been added.
351: */
352: protected void nodeAdded(Node n) {
353: }
354:
355: /**
356: * Called when a child node is going to be removed.
357: */
358: protected void nodeToBeRemoved(Node n) {
359: }
360:
361: /**
362: * Deeply exports this node to the given document.
363: */
364: protected Node deepExport(Node n, AbstractDocument d) {
365: super .deepExport(n, d);
366: for (Node p = getFirstChild(); p != null; p = p
367: .getNextSibling()) {
368: Node t = ((AbstractNode) p).deepExport(p.cloneNode(false),
369: d);
370: n.appendChild(t);
371: }
372: return n;
373: }
374:
375: /**
376: * Deeply copy the fields of the current node into the given node.
377: * @param n a node of the type of this.
378: */
379: protected Node deepCopyInto(Node n) {
380: super .deepCopyInto(n);
381: for (Node p = getFirstChild(); p != null; p = p
382: .getNextSibling()) {
383: Node t = p.cloneNode(true);
384: n.appendChild(t);
385: }
386: return n;
387: }
388:
389: /**
390: * Fires a DOMSubtreeModified event.
391: */
392: protected void fireDOMSubtreeModifiedEvent() {
393: AbstractDocument doc = getCurrentDocument();
394: if (doc.getEventsEnabled()) {
395: DocumentEvent de = (DocumentEvent) doc;
396: DOMMutationEvent ev = (DOMMutationEvent) de
397: .createEvent("MutationEvents");
398: ev.initMutationEventNS(
399: XMLConstants.XML_EVENTS_NAMESPACE_URI,
400: "DOMSubtreeModified", true, // canBubbleArg
401: false, // cancelableArg
402: null, // relatedNodeArg
403: null, // prevValueArg
404: null, // newValueArg
405: null, // attrNameArg
406: MutationEvent.MODIFICATION);
407: dispatchEvent(ev);
408: }
409: }
410:
411: /**
412: * Fires a DOMNodeInserted event.
413: */
414: protected void fireDOMNodeInsertedEvent(Node node) {
415: AbstractDocument doc = getCurrentDocument();
416: if (doc.getEventsEnabled()) {
417: DocumentEvent de = (DocumentEvent) doc;
418: DOMMutationEvent ev = (DOMMutationEvent) de
419: .createEvent("MutationEvents");
420: ev.initMutationEventNS(
421: XMLConstants.XML_EVENTS_NAMESPACE_URI,
422: "DOMNodeInserted", true, // canBubbleArg
423: false, // cancelableArg
424: this , // relatedNodeArg
425: null, // prevValueArg
426: null, // newValueArg
427: null, // attrNameArg
428: MutationEvent.ADDITION);
429: AbstractNode n = (AbstractNode) node;
430: n.dispatchEvent(ev);
431: n.fireDOMNodeInsertedIntoDocumentEvent();
432: }
433: }
434:
435: /**
436: * Fires a DOMNodeRemoved event.
437: */
438: protected void fireDOMNodeRemovedEvent(Node node) {
439: AbstractDocument doc = getCurrentDocument();
440: if (doc.getEventsEnabled()) {
441: DocumentEvent de = (DocumentEvent) doc;
442: DOMMutationEvent ev = (DOMMutationEvent) de
443: .createEvent("MutationEvents");
444: ev.initMutationEventNS(
445: XMLConstants.XML_EVENTS_NAMESPACE_URI,
446: "DOMNodeRemoved", true, // canBubbleArg
447: false, // cancelableArg
448: this , // relatedNodeArg
449: null, // prevValueArg
450: null, // newValueArg
451: null, // attrNameArg
452: MutationEvent.REMOVAL);
453: AbstractNode n = (AbstractNode) node;
454: n.dispatchEvent(ev);
455: n.fireDOMNodeRemovedFromDocumentEvent();
456: }
457: }
458:
459: /**
460: * Checks the validity of a node to be inserted, and removes it from
461: * the document if needed.
462: */
463: protected void checkAndRemove(Node n, boolean replace) {
464: checkChildType(n, replace);
465:
466: if (isReadonly())
467: throw createDOMException(
468: DOMException.NO_MODIFICATION_ALLOWED_ERR,
469: "readonly.node", new Object[] {
470: new Integer(getNodeType()), getNodeName() });
471:
472: if (n.getOwnerDocument() != getCurrentDocument())
473: throw createDOMException(DOMException.WRONG_DOCUMENT_ERR,
474: "node.from.wrong.document", new Object[] {
475: new Integer(getNodeType()), getNodeName() });
476: if (this == n)
477: throw createDOMException(
478: DOMException.HIERARCHY_REQUEST_ERR, "add.self",
479: new Object[] { getNodeName() });
480:
481: Node np = n.getParentNode();
482: if (np == null)
483: return; // Already removed from tree, do nothing.
484:
485: for (Node pn = this ; pn != null; pn = pn.getParentNode()) {
486: if (pn == n)
487: throw createDOMException(
488: DOMException.HIERARCHY_REQUEST_ERR,
489: "add.ancestor", new Object[] {
490: new Integer(getNodeType()),
491: getNodeName() });
492: }
493:
494: // Remove the node from the tree
495: np.removeChild(n);
496: }
497:
498: /**
499: * To manage a list of nodes.
500: */
501: protected class ElementsByTagName implements NodeList {
502:
503: /**
504: * The table.
505: */
506: protected Node[] table;
507:
508: /**
509: * The number of nodes.
510: */
511: protected int size = -1;
512:
513: /**
514: * The name identifier.
515: */
516: protected String name;
517:
518: /**
519: * Creates a new ElementsByTagName object.
520: */
521: public ElementsByTagName(String n) {
522: name = n;
523: }
524:
525: /**
526: * <b>DOM</b>: Implements {@link NodeList#item(int)}.
527: */
528: public Node item(int index) {
529: if (size == -1) {
530: initialize();
531: }
532: if (table == null || index < 0 || index >= size) {
533: return null;
534: }
535: return table[index];
536: }
537:
538: /**
539: * <b>DOM</b>: Implements {@link NodeList#getLength()}.
540: * @return {@link #size}.
541: */
542: public int getLength() {
543: if (size == -1) {
544: initialize();
545: }
546: return size;
547: }
548:
549: /**
550: * Invalidates the list.
551: */
552: public void invalidate() {
553: size = -1;
554: }
555:
556: /**
557: * Appends a node to the list.
558: */
559: protected void append(Node n) {
560: if (table == null) {
561: table = new Node[11];
562: } else if (size == table.length - 1) {
563: Node[] t = new Node[table.length * 2 + 1];
564: System.arraycopy(table, 0, t, 0, size);
565: table = t;
566: }
567: table[size++] = n;
568: }
569:
570: /**
571: * Initializes the list.
572: */
573: protected void initialize() {
574: size = 0;
575: for (Node n = AbstractParentNode.this .getFirstChild(); n != null; n = n
576: .getNextSibling()) {
577: initialize(n);
578: }
579: }
580:
581: private void initialize(Node node) {
582: if (node.getNodeType() == ELEMENT_NODE) {
583: String nm = node.getNodeName();
584: if (name.equals("*") || name.equals(nm)) {
585: append(node);
586: }
587: }
588: for (Node n = node.getFirstChild(); n != null; n = n
589: .getNextSibling()) {
590: initialize(n);
591: }
592: }
593: }
594:
595: /**
596: * To manage a list of nodes.
597: */
598: protected class ElementsByTagNameNS implements NodeList {
599:
600: /**
601: * The table.
602: */
603: protected Node[] table;
604:
605: /**
606: * The number of nodes.
607: */
608: protected int size = -1;
609:
610: /**
611: * The namespace URI identifier.
612: */
613: protected String namespaceURI;
614:
615: /**
616: * The local name identifier.
617: */
618: protected String localName;
619:
620: /**
621: * Creates a new ElementsByTagNameNS object.
622: */
623: public ElementsByTagNameNS(String ns, String ln) {
624: namespaceURI = ns;
625: localName = ln;
626: }
627:
628: /**
629: * <b>DOM</b>: Implements {@link NodeList#item(int)}.
630: */
631: public Node item(int index) {
632: if (size == -1) {
633: initialize();
634: }
635: if (table == null || index < 0 || index > size) {
636: return null;
637: }
638: return table[index];
639: }
640:
641: /**
642: * <b>DOM</b>: Implements {@link NodeList#getLength()}.
643: * @return {@link #size}.
644: */
645: public int getLength() {
646: if (size == -1) {
647: initialize();
648: }
649: return size;
650: }
651:
652: /**
653: * Invalidates the list.
654: */
655: public void invalidate() {
656: size = -1;
657: }
658:
659: /**
660: * Appends a node to the list.
661: */
662: protected void append(Node n) {
663: if (table == null) {
664: table = new Node[11];
665: } else if (size == table.length - 1) {
666: Node[] t = new Node[table.length * 2 + 1];
667: System.arraycopy(table, 0, t, 0, size);
668: table = t;
669: }
670: table[size++] = n;
671: }
672:
673: /**
674: * Initializes the list.
675: */
676: protected void initialize() {
677: size = 0;
678: for (Node n = AbstractParentNode.this .getFirstChild(); n != null; n = n
679: .getNextSibling()) {
680: initialize(n);
681: }
682: }
683:
684: private void initialize(Node node) {
685: if (node.getNodeType() == ELEMENT_NODE) {
686: String ns = node.getNamespaceURI();
687: String nm = (ns == null) ? node.getNodeName() : node
688: .getLocalName();
689: if (nsMatch(namespaceURI, node.getNamespaceURI())
690: && (localName.equals("*") || localName
691: .equals(nm))) {
692: append(node);
693: }
694: }
695: for (Node n = node.getFirstChild(); n != null; n = n
696: .getNextSibling()) {
697: initialize(n);
698: }
699: }
700:
701: private boolean nsMatch(String s1, String s2) {
702: if (s1 == null && s2 == null) {
703: return true;
704: }
705: if (s1 == null || s2 == null) {
706: return false;
707: }
708: if (s1.equals("*")) {
709: return true;
710: }
711: return s1.equals(s2);
712: }
713: }
714:
715: /**
716: * To manage the children of this node.
717: */
718: protected class ChildNodes implements NodeList, Serializable {
719: /**
720: * The first child.
721: */
722: protected ExtendedNode firstChild;
723:
724: /**
725: * The last child.
726: */
727: protected ExtendedNode lastChild;
728:
729: /**
730: * The number of children.
731: */
732: protected int children;
733:
734: /**
735: * Creates a new ChildNodes object.
736: */
737: public ChildNodes() {
738: }
739:
740: /**
741: * <b>DOM</b>: Implements {@link org.w3c.dom.NodeList#item(int)}.
742: */
743: public Node item(int index) {
744: if (index < 0 || index >= children) {
745: return null;
746: }
747: if (index < (children >> 1)) {
748: Node n = firstChild;
749: for (int i = 0; i < index; i++) {
750: n = n.getNextSibling();
751: }
752: return n;
753: } else {
754: Node n = lastChild;
755: for (int i = children - 1; i > index; i--) {
756: n = n.getPreviousSibling();
757: }
758: return n;
759: }
760: }
761:
762: /**
763: * <b>DOM</b>: Implements {@link org.w3c.dom.NodeList#getLength()}.
764: * @return {@link #children}.
765: */
766: public int getLength() {
767: return children;
768: }
769:
770: /**
771: * Appends a node to the tree.
772: * The node is assumed not to be a DocumentFragment instance.
773: */
774: public ExtendedNode append(ExtendedNode n) {
775: if (lastChild == null) {
776: firstChild = n;
777: } else {
778: lastChild.setNextSibling(n);
779: n.setPreviousSibling(lastChild);
780: }
781: lastChild = n;
782: children++;
783: return n;
784: }
785:
786: /**
787: * Inserts a node in the tree.
788: */
789: public ExtendedNode insert(ExtendedNode n, ExtendedNode r) {
790: if (r == null) {
791: return append(n);
792: }
793:
794: if (r == firstChild) {
795: firstChild.setPreviousSibling(n);
796: n.setNextSibling(firstChild);
797: firstChild = n;
798: children++;
799: return n;
800: }
801: if (r == lastChild) {
802: ExtendedNode ps = (ExtendedNode) r.getPreviousSibling();
803: ps.setNextSibling(n);
804: r.setPreviousSibling(n);
805: n.setNextSibling(r);
806: n.setPreviousSibling(ps);
807: children++;
808: return n;
809: }
810:
811: ExtendedNode ps = (ExtendedNode) r.getPreviousSibling();
812: if ((ps.getNextSibling() == r)
813: && (ps.getParentNode() == r.getParentNode())) {
814: ps.setNextSibling(n);
815: n.setPreviousSibling(ps);
816: n.setNextSibling(r);
817: r.setPreviousSibling(n);
818: children++;
819: return n;
820: }
821:
822: throw createDOMException(DOMException.NOT_FOUND_ERR,
823: "child.missing", new Object[] {
824: new Integer(r.getNodeType()),
825: r.getNodeName() });
826: }
827:
828: /**
829: * Replaces a node in the tree by an other.
830: */
831: public ExtendedNode replace(ExtendedNode n, ExtendedNode o) {
832: if (o == firstChild) {
833: ExtendedNode t = (ExtendedNode) firstChild
834: .getNextSibling();
835: n.setNextSibling(t);
836: if (o == lastChild) {
837: lastChild = n;
838: } else {
839: t.setPreviousSibling(n);
840: }
841: firstChild.setNextSibling(null);
842: firstChild = n;
843: return o;
844: }
845:
846: if (o == lastChild) {
847: ExtendedNode t = (ExtendedNode) lastChild
848: .getPreviousSibling();
849: n.setPreviousSibling(t);
850: t.setNextSibling(n);
851: lastChild.setPreviousSibling(null);
852: lastChild = n;
853: return o;
854: }
855:
856: ExtendedNode ps = (ExtendedNode) o.getPreviousSibling();
857: ExtendedNode ns = (ExtendedNode) o.getNextSibling();
858: if ((ps.getNextSibling() == o)
859: && (ns.getPreviousSibling() == o)
860: && (ps.getParentNode() == o.getParentNode())
861: && (ns.getParentNode() == o.getParentNode())) {
862:
863: ps.setNextSibling(n);
864: n.setPreviousSibling(ps);
865: n.setNextSibling(ns);
866: ns.setPreviousSibling(n);
867: o.setPreviousSibling(null);
868: o.setNextSibling(null);
869: return o;
870: }
871: throw createDOMException(DOMException.NOT_FOUND_ERR,
872: "child.missing", new Object[] {
873: new Integer(o.getNodeType()),
874: o.getNodeName() });
875: }
876:
877: /**
878: * Removes the given node from the tree.
879: */
880: public ExtendedNode remove(ExtendedNode n) {
881: if (n == firstChild) {
882: if (n == lastChild) {
883: firstChild = null;
884: lastChild = null;
885: children--;
886: return n;
887: }
888: firstChild = (ExtendedNode) firstChild.getNextSibling();
889: firstChild.setPreviousSibling(null);
890: n.setNextSibling(null);
891: children--;
892: return n;
893: }
894:
895: if (n == lastChild) {
896: lastChild = (ExtendedNode) lastChild
897: .getPreviousSibling();
898: lastChild.setNextSibling(null);
899: n.setPreviousSibling(null);
900: children--;
901: return n;
902: }
903:
904: ExtendedNode ps = (ExtendedNode) n.getPreviousSibling();
905: ExtendedNode ns = (ExtendedNode) n.getNextSibling();
906: if ((ps.getNextSibling() == n)
907: && (ns.getPreviousSibling() == n)
908: && (ps.getParentNode() == n.getParentNode())
909: && (ns.getParentNode() == n.getParentNode())) {
910: ps.setNextSibling(ns);
911: ns.setPreviousSibling(ps);
912: n.setPreviousSibling(null);
913: n.setNextSibling(null);
914: children--;
915: return n;
916: }
917: throw createDOMException(DOMException.NOT_FOUND_ERR,
918: "child.missing", new Object[] {
919: new Integer(n.getNodeType()),
920: n.getNodeName() });
921: }
922: }
923: }
|