1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.dom;
  58. import java.io.Serializable;
  59. import java.io.IOException;
  60. import java.io.ObjectInputStream;
  61. import java.io.ObjectOutputStream;
  62. import org.w3c.dom.DOMException;
  63. import org.w3c.dom.Document;
  64. import org.w3c.dom.Node;
  65. import org.w3c.dom.NodeList;
  66. import org.w3c.dom.UserDataHandler;
  67. /**
  68. * ParentNode inherits from ChildNode and adds the capability of having child
  69. * nodes. Not every node in the DOM can have children, so only nodes that can
  70. * should inherit from this class and pay the price for it.
  71. * <P>
  72. * ParentNode, just like NodeImpl, also implements NodeList, so it can
  73. * return itself in response to the getChildNodes() query. This eliminiates
  74. * the need for a separate ChildNodeList object. Note that this is an
  75. * IMPLEMENTATION DETAIL; applications should _never_ assume that
  76. * this identity exists. On the other hand, subclasses may need to override
  77. * this, in case of conflicting names. This is the case for the classes
  78. * HTMLSelectElementImpl and HTMLFormElementImpl of the HTML DOM.
  79. * <P>
  80. * While we have a direct reference to the first child, the last child is
  81. * stored as the previous sibling of the first child. First child nodes are
  82. * marked as being so, and getNextSibling hides this fact.
  83. * <P>Note: Not all parent nodes actually need to also be a child. At some
  84. * point we used to have ParentNode inheriting from NodeImpl and another class
  85. * called ChildAndParentNode that inherited from ChildNode. But due to the lack
  86. * of multiple inheritance a lot of code had to be duplicated which led to a
  87. * maintenance nightmare. At the same time only a few nodes (Document,
  88. * DocumentFragment, Entity, and Attribute) cannot be a child so the gain in
  89. * memory wasn't really worth it. The only type for which this would be the
  90. * case is Attribute, but we deal with there in another special way, so this is
  91. * not applicable.
  92. * <p>
  93. * This class doesn't directly support mutation events, however, it notifies
  94. * the document when mutations are performed so that the document class do so.
  95. *
  96. * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  97. * AttrImpl, be careful to keep these two classes in sync!
  98. *
  99. * @author Arnaud Le Hors, IBM
  100. * @author Joe Kesselman, IBM
  101. * @author Andy Clark, IBM
  102. * @version $Id: ParentNode.java,v 1.41 2004/02/10 17:09:45 elena Exp $
  103. */
  104. public abstract class ParentNode
  105. extends ChildNode {
  106. /** Serialization version. */
  107. static final long serialVersionUID = 2815829867152120872L;
  108. /** Owner document. */
  109. protected CoreDocumentImpl ownerDocument;
  110. /** First child. */
  111. protected ChildNode firstChild = null;
  112. // transients
  113. /** NodeList cache */
  114. protected transient NodeListCache fNodeListCache = null;
  115. //
  116. // Constructors
  117. //
  118. /**
  119. * No public constructor; only subclasses of ParentNode should be
  120. * instantiated, and those normally via a Document's factory methods
  121. */
  122. protected ParentNode(CoreDocumentImpl ownerDocument) {
  123. super(ownerDocument);
  124. this.ownerDocument = ownerDocument;
  125. }
  126. /** Constructor for serialization. */
  127. public ParentNode() {}
  128. //
  129. // NodeList methods
  130. //
  131. /**
  132. * Returns a duplicate of a given node. You can consider this a
  133. * generic "copy constructor" for nodes. The newly returned object should
  134. * be completely independent of the source object's subtree, so changes
  135. * in one after the clone has been made will not affect the other.
  136. * <p>
  137. * Example: Cloning a Text node will copy both the node and the text it
  138. * contains.
  139. * <p>
  140. * Example: Cloning something that has children -- Element or Attr, for
  141. * example -- will _not_ clone those children unless a "deep clone"
  142. * has been requested. A shallow clone of an Attr node will yield an
  143. * empty Attr of the same name.
  144. * <p>
  145. * NOTE: Clones will always be read/write, even if the node being cloned
  146. * is read-only, to permit applications using only the DOM API to obtain
  147. * editable copies of locked portions of the tree.
  148. */
  149. public Node cloneNode(boolean deep) {
  150. if (needsSyncChildren()) {
  151. synchronizeChildren();
  152. }
  153. ParentNode newnode = (ParentNode) super.cloneNode(deep);
  154. // set owner document
  155. newnode.ownerDocument = ownerDocument;
  156. // Need to break the association w/ original kids
  157. newnode.firstChild = null;
  158. // invalidate cache for children NodeList
  159. newnode.fNodeListCache = null;
  160. // Then, if deep, clone the kids too.
  161. if (deep) {
  162. for (ChildNode child = firstChild;
  163. child != null;
  164. child = child.nextSibling) {
  165. newnode.appendChild(child.cloneNode(true));
  166. }
  167. }
  168. return newnode;
  169. } // cloneNode(boolean):Node
  170. /**
  171. * Find the Document that this Node belongs to (the document in
  172. * whose context the Node was created). The Node may or may not
  173. * currently be part of that Document's actual contents.
  174. */
  175. public Document getOwnerDocument() {
  176. return ownerDocument;
  177. }
  178. /**
  179. * same as above but returns internal type and this one is not overridden
  180. * by CoreDocumentImpl to return null
  181. */
  182. CoreDocumentImpl ownerDocument() {
  183. return ownerDocument;
  184. }
  185. /**
  186. * NON-DOM
  187. * set the ownerDocument of this node and its children
  188. */
  189. void setOwnerDocument(CoreDocumentImpl doc) {
  190. if (needsSyncChildren()) {
  191. synchronizeChildren();
  192. }
  193. super.setOwnerDocument(doc);
  194. ownerDocument = doc;
  195. for (ChildNode child = firstChild;
  196. child != null; child = child.nextSibling) {
  197. child.setOwnerDocument(doc);
  198. }
  199. }
  200. /**
  201. * Test whether this node has any children. Convenience shorthand
  202. * for (Node.getFirstChild()!=null)
  203. */
  204. public boolean hasChildNodes() {
  205. if (needsSyncChildren()) {
  206. synchronizeChildren();
  207. }
  208. return firstChild != null;
  209. }
  210. /**
  211. * Obtain a NodeList enumerating all children of this node. If there
  212. * are none, an (initially) empty NodeList is returned.
  213. * <p>
  214. * NodeLists are "live"; as children are added/removed the NodeList
  215. * will immediately reflect those changes. Also, the NodeList refers
  216. * to the actual nodes, so changes to those nodes made via the DOM tree
  217. * will be reflected in the NodeList and vice versa.
  218. * <p>
  219. * In this implementation, Nodes implement the NodeList interface and
  220. * provide their own getChildNodes() support. Other DOMs may solve this
  221. * differently.
  222. */
  223. public NodeList getChildNodes() {
  224. if (needsSyncChildren()) {
  225. synchronizeChildren();
  226. }
  227. return this;
  228. } // getChildNodes():NodeList
  229. /** The first child of this Node, or null if none. */
  230. public Node getFirstChild() {
  231. if (needsSyncChildren()) {
  232. synchronizeChildren();
  233. }
  234. return firstChild;
  235. } // getFirstChild():Node
  236. /** The last child of this Node, or null if none. */
  237. public Node getLastChild() {
  238. if (needsSyncChildren()) {
  239. synchronizeChildren();
  240. }
  241. return lastChild();
  242. } // getLastChild():Node
  243. final ChildNode lastChild() {
  244. // last child is stored as the previous sibling of first child
  245. return firstChild != null ? firstChild.previousSibling : null;
  246. }
  247. final void lastChild(ChildNode node) {
  248. // store lastChild as previous sibling of first child
  249. if (firstChild != null) {
  250. firstChild.previousSibling = node;
  251. }
  252. }
  253. /**
  254. * Move one or more node(s) to our list of children. Note that this
  255. * implicitly removes them from their previous parent.
  256. *
  257. * @param newChild The Node to be moved to our subtree. As a
  258. * convenience feature, inserting a DocumentNode will instead insert
  259. * all its children.
  260. *
  261. * @param refChild Current child which newChild should be placed
  262. * immediately before. If refChild is null, the insertion occurs
  263. * after all existing Nodes, like appendChild().
  264. *
  265. * @return newChild, in its new state (relocated, or emptied in the case of
  266. * DocumentNode.)
  267. *
  268. * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  269. * type that shouldn't be a child of this node, or if newChild is an
  270. * ancestor of this node.
  271. *
  272. * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  273. * different owner document than we do.
  274. *
  275. * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
  276. * this node.
  277. *
  278. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  279. * read-only.
  280. */
  281. public Node insertBefore(Node newChild, Node refChild)
  282. throws DOMException {
  283. // Tail-call; optimizer should be able to do good things with.
  284. return internalInsertBefore(newChild, refChild, false);
  285. } // insertBefore(Node,Node):Node
  286. /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  287. * to control which mutation events are spawned. This version of the
  288. * insertBefore operation allows us to do so. It is not intended
  289. * for use by application programs.
  290. */
  291. Node internalInsertBefore(Node newChild, Node refChild, boolean replace)
  292. throws DOMException {
  293. boolean errorChecking = ownerDocument.errorChecking;
  294. if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
  295. // SLOW BUT SAFE: We could insert the whole subtree without
  296. // juggling so many next/previous pointers. (Wipe out the
  297. // parent's child-list, patch the parent pointers, set the
  298. // ends of the list.) But we know some subclasses have special-
  299. // case behavior they add to insertBefore(), so we don't risk it.
  300. // This approch also takes fewer bytecodes.
  301. // NOTE: If one of the children is not a legal child of this
  302. // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
  303. // have been transferred. (Alternative behaviors would be to
  304. // reparent up to the first failure point or reparent all those
  305. // which are acceptable to the target node, neither of which is
  306. // as robust. PR-DOM-0818 isn't entirely clear on which it
  307. // recommends?????
  308. // No need to check kids for right-document; if they weren't,
  309. // they wouldn't be kids of that DocFrag.
  310. if (errorChecking) {
  311. for (Node kid = newChild.getFirstChild(); // Prescan
  312. kid != null; kid = kid.getNextSibling()) {
  313. if (!ownerDocument.isKidOK(this, kid)) {
  314. throw new DOMException(
  315. DOMException.HIERARCHY_REQUEST_ERR,
  316. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
  317. }
  318. }
  319. }
  320. while (newChild.hasChildNodes()) {
  321. insertBefore(newChild.getFirstChild(), refChild);
  322. }
  323. return newChild;
  324. }
  325. if (newChild == refChild) {
  326. // stupid case that must be handled as a no-op triggering events...
  327. refChild = refChild.getNextSibling();
  328. removeChild(newChild);
  329. insertBefore(newChild, refChild);
  330. return newChild;
  331. }
  332. if (needsSyncChildren()) {
  333. synchronizeChildren();
  334. }
  335. if (errorChecking) {
  336. if (isReadOnly()) {
  337. throw new DOMException(
  338. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  339. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
  340. }
  341. if (newChild.getOwnerDocument() != ownerDocument && newChild != ownerDocument) {
  342. throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
  343. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
  344. }
  345. if (!ownerDocument.isKidOK(this, newChild)) {
  346. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
  347. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
  348. }
  349. // refChild must be a child of this node (or null)
  350. if (refChild != null && refChild.getParentNode() != this) {
  351. throw new DOMException(DOMException.NOT_FOUND_ERR,
  352. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
  353. }
  354. // Prevent cycles in the tree
  355. // newChild cannot be ancestor of this Node,
  356. // and actually cannot be this
  357. boolean treeSafe = true;
  358. for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
  359. {
  360. treeSafe = newChild != a;
  361. }
  362. if(!treeSafe) {
  363. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
  364. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
  365. }
  366. }
  367. // notify document
  368. ownerDocument.insertingNode(this, replace);
  369. // Convert to internal type, to avoid repeated casting
  370. ChildNode newInternal = (ChildNode)newChild;
  371. Node oldparent = newInternal.parentNode();
  372. if (oldparent != null) {
  373. oldparent.removeChild(newInternal);
  374. }
  375. // Convert to internal type, to avoid repeated casting
  376. ChildNode refInternal = (ChildNode)refChild;
  377. // Attach up
  378. newInternal.ownerNode = this;
  379. newInternal.isOwned(true);
  380. // Attach before and after
  381. // Note: firstChild.previousSibling == lastChild!!
  382. if (firstChild == null) {
  383. // this our first and only child
  384. firstChild = newInternal;
  385. newInternal.isFirstChild(true);
  386. newInternal.previousSibling = newInternal;
  387. }
  388. else {
  389. if (refInternal == null) {
  390. // this is an append
  391. ChildNode lastChild = firstChild.previousSibling;
  392. lastChild.nextSibling = newInternal;
  393. newInternal.previousSibling = lastChild;
  394. firstChild.previousSibling = newInternal;
  395. }
  396. else {
  397. // this is an insert
  398. if (refChild == firstChild) {
  399. // at the head of the list
  400. firstChild.isFirstChild(false);
  401. newInternal.nextSibling = firstChild;
  402. newInternal.previousSibling = firstChild.previousSibling;
  403. firstChild.previousSibling = newInternal;
  404. firstChild = newInternal;
  405. newInternal.isFirstChild(true);
  406. }
  407. else {
  408. // somewhere in the middle
  409. ChildNode prev = refInternal.previousSibling;
  410. newInternal.nextSibling = refInternal;
  411. prev.nextSibling = newInternal;
  412. refInternal.previousSibling = newInternal;
  413. newInternal.previousSibling = prev;
  414. }
  415. }
  416. }
  417. changed();
  418. // update cached length if we have any
  419. if (fNodeListCache != null) {
  420. if (fNodeListCache.fLength != -1) {
  421. fNodeListCache.fLength++;
  422. }
  423. if (fNodeListCache.fChildIndex != -1) {
  424. // if we happen to insert just before the cached node, update
  425. // the cache to the new node to match the cached index
  426. if (fNodeListCache.fChild == refInternal) {
  427. fNodeListCache.fChild = newInternal;
  428. } else {
  429. // otherwise just invalidate the cache
  430. fNodeListCache.fChildIndex = -1;
  431. }
  432. }
  433. }
  434. // notify document
  435. ownerDocument.insertedNode(this, newInternal, replace);
  436. checkNormalizationAfterInsert(newInternal);
  437. return newChild;
  438. } // internalInsertBefore(Node,Node,boolean):Node
  439. /**
  440. * Remove a child from this Node. The removed child's subtree
  441. * remains intact so it may be re-inserted elsewhere.
  442. *
  443. * @return oldChild, in its new state (removed).
  444. *
  445. * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  446. * this node.
  447. *
  448. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  449. * read-only.
  450. */
  451. public Node removeChild(Node oldChild)
  452. throws DOMException {
  453. // Tail-call, should be optimizable
  454. return internalRemoveChild(oldChild, false);
  455. } // removeChild(Node) :Node
  456. /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  457. * to control which mutation events are spawned. This version of the
  458. * removeChild operation allows us to do so. It is not intended
  459. * for use by application programs.
  460. */
  461. Node internalRemoveChild(Node oldChild, boolean replace)
  462. throws DOMException {
  463. CoreDocumentImpl ownerDocument = ownerDocument();
  464. if (ownerDocument.errorChecking) {
  465. if (isReadOnly()) {
  466. throw new DOMException(
  467. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  468. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
  469. }
  470. if (oldChild != null && oldChild.getParentNode() != this) {
  471. throw new DOMException(DOMException.NOT_FOUND_ERR,
  472. DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
  473. }
  474. }
  475. ChildNode oldInternal = (ChildNode) oldChild;
  476. // notify document
  477. ownerDocument.removingNode(this, oldInternal, replace);
  478. // update cached length if we have any
  479. if (fNodeListCache != null) {
  480. if (fNodeListCache.fLength != -1) {
  481. fNodeListCache.fLength--;
  482. }
  483. if (fNodeListCache.fChildIndex != -1) {
  484. // if the removed node is the cached node
  485. // move the cache to its (soon former) previous sibling
  486. if (fNodeListCache.fChild == oldInternal) {
  487. fNodeListCache.fChildIndex--;
  488. fNodeListCache.fChild = oldInternal.previousSibling();
  489. } else {
  490. // otherwise just invalidate the cache
  491. fNodeListCache.fChildIndex = -1;
  492. }
  493. }
  494. }
  495. // Patch linked list around oldChild
  496. // Note: lastChild == firstChild.previousSibling
  497. if (oldInternal == firstChild) {
  498. // removing first child
  499. oldInternal.isFirstChild(false);
  500. firstChild = oldInternal.nextSibling;
  501. if (firstChild != null) {
  502. firstChild.isFirstChild(true);
  503. firstChild.previousSibling = oldInternal.previousSibling;
  504. }
  505. } else {
  506. ChildNode prev = oldInternal.previousSibling;
  507. ChildNode next = oldInternal.nextSibling;
  508. prev.nextSibling = next;
  509. if (next == null) {
  510. // removing last child
  511. firstChild.previousSibling = prev;
  512. } else {
  513. // removing some other child in the middle
  514. next.previousSibling = prev;
  515. }
  516. }
  517. // Save previous sibling for normalization checking.
  518. ChildNode oldPreviousSibling = oldInternal.previousSibling();
  519. // Remove oldInternal's references to tree
  520. oldInternal.ownerNode = ownerDocument;
  521. oldInternal.isOwned(false);
  522. oldInternal.nextSibling = null;
  523. oldInternal.previousSibling = null;
  524. changed();
  525. // notify document
  526. ownerDocument.removedNode(this, replace);
  527. checkNormalizationAfterRemove(oldPreviousSibling);
  528. return oldInternal;
  529. } // internalRemoveChild(Node,boolean):Node
  530. /**
  531. * Make newChild occupy the location that oldChild used to
  532. * have. Note that newChild will first be removed from its previous
  533. * parent, if any. Equivalent to inserting newChild before oldChild,
  534. * then removing oldChild.
  535. *
  536. * @return oldChild, in its new state (removed).
  537. *
  538. * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  539. * type that shouldn't be a child of this node, or if newChild is
  540. * one of our ancestors.
  541. *
  542. * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  543. * different owner document than we do.
  544. *
  545. * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  546. * this node.
  547. *
  548. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  549. * read-only.
  550. */
  551. public Node replaceChild(Node newChild, Node oldChild)
  552. throws DOMException {
  553. // If Mutation Events are being generated, this operation might
  554. // throw aggregate events twice when modifying an Attr -- once
  555. // on insertion and once on removal. DOM Level 2 does not specify
  556. // this as either desirable or undesirable, but hints that
  557. // aggregations should be issued only once per user request.
  558. // notify document
  559. ownerDocument.replacingNode(this);
  560. internalInsertBefore(newChild, oldChild, true);
  561. if (newChild != oldChild) {
  562. internalRemoveChild(oldChild, true);
  563. }
  564. // notify document
  565. ownerDocument.replacedNode(this);
  566. return oldChild;
  567. }
  568. /*
  569. * Get Node text content
  570. * @since DOM Level 3
  571. */
  572. public String getTextContent() throws DOMException {
  573. Node child = getFirstChild();
  574. if (child != null) {
  575. Node next = child.getNextSibling();
  576. if (next == null) {
  577. return hasTextContent(child) ? ((NodeImpl) child).getTextContent() : "";
  578. }
  579. if (fBufferStr == null){
  580. fBufferStr = new StringBuffer();
  581. }
  582. else {
  583. fBufferStr.setLength(0);
  584. }
  585. getTextContent(fBufferStr);
  586. return fBufferStr.toString();
  587. }
  588. return "";
  589. }
  590. // internal method taking a StringBuffer in parameter
  591. void getTextContent(StringBuffer buf) throws DOMException {
  592. Node child = getFirstChild();
  593. while (child != null) {
  594. if (hasTextContent(child)) {
  595. ((NodeImpl) child).getTextContent(buf);
  596. }
  597. child = child.getNextSibling();
  598. }
  599. }
  600. // internal method returning whether to take the given node's text content
  601. final boolean hasTextContent(Node child) {
  602. return child.getNodeType() != Node.COMMENT_NODE &&
  603. child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE &&
  604. (child.getNodeType() != Node.TEXT_NODE ||
  605. ((TextImpl) child).isIgnorableWhitespace() == false);
  606. }
  607. /*
  608. * Set Node text content
  609. * @since DOM Level 3
  610. */
  611. public void setTextContent(String textContent)
  612. throws DOMException {
  613. // get rid of any existing children
  614. Node child;
  615. while ((child = getFirstChild()) != null) {
  616. removeChild(child);
  617. }
  618. // create a Text node to hold the given content
  619. if (textContent != null && textContent.length() != 0){
  620. appendChild(ownerDocument().createTextNode(textContent));
  621. }
  622. }
  623. //
  624. // NodeList methods
  625. //
  626. /**
  627. * Count the immediate children of this node. Use to implement
  628. * NodeList.getLength().
  629. * @return int
  630. */
  631. private int nodeListGetLength() {
  632. if (fNodeListCache == null) {
  633. // get rid of trivial cases
  634. if (firstChild == null) {
  635. return 0;
  636. }
  637. if (firstChild == lastChild()) {
  638. return 1;
  639. }
  640. // otherwise request a cache object
  641. fNodeListCache = ownerDocument.getNodeListCache(this);
  642. }
  643. if (fNodeListCache.fLength == -1) { // is the cached length invalid ?
  644. int l;
  645. ChildNode n;
  646. // start from the cached node if we have one
  647. if (fNodeListCache.fChildIndex != -1 &&
  648. fNodeListCache.fChild != null) {
  649. l = fNodeListCache.fChildIndex;
  650. n = fNodeListCache.fChild;
  651. } else {
  652. n = firstChild;
  653. l = 0;
  654. }
  655. while (n != null) {
  656. l++;
  657. n = n.nextSibling;
  658. }
  659. fNodeListCache.fLength = l;
  660. }
  661. return fNodeListCache.fLength;
  662. } // nodeListGetLength():int
  663. /**
  664. * NodeList method: Count the immediate children of this node
  665. * @return int
  666. */
  667. public int getLength() {
  668. return nodeListGetLength();
  669. }
  670. /**
  671. * Return the Nth immediate child of this node, or null if the index is
  672. * out of bounds. Use to implement NodeList.item().
  673. * @param index int
  674. */
  675. private Node nodeListItem(int index) {
  676. if (fNodeListCache == null) {
  677. // get rid of trivial case
  678. if (firstChild == lastChild()) {
  679. return index == 0 ? firstChild : null;
  680. }
  681. // otherwise request a cache object
  682. fNodeListCache = ownerDocument.getNodeListCache(this);
  683. }
  684. int i = fNodeListCache.fChildIndex;
  685. ChildNode n = fNodeListCache.fChild;
  686. boolean firstAccess = true;
  687. // short way
  688. if (i != -1 && n != null) {
  689. firstAccess = false;
  690. if (i < index) {
  691. while (i < index && n != null) {
  692. i++;
  693. n = n.nextSibling;
  694. }
  695. }
  696. else if (i > index) {
  697. while (i > index && n != null) {
  698. i--;
  699. n = n.previousSibling();
  700. }
  701. }
  702. }
  703. else {
  704. // long way
  705. n = firstChild;
  706. for (i = 0; i < index && n != null; i++) {
  707. n = n.nextSibling;
  708. }
  709. }
  710. // release cache if reaching last child or first child
  711. if (!firstAccess && (n == firstChild || n == lastChild())) {
  712. fNodeListCache.fChildIndex = -1;
  713. fNodeListCache.fChild = null;
  714. ownerDocument.freeNodeListCache(fNodeListCache);
  715. // we can keep using the cache until it is actually reused
  716. // fNodeListCache will be nulled by the pool (document) if that
  717. // happens.
  718. // fNodeListCache = null;
  719. }
  720. else {
  721. // otherwise update it
  722. fNodeListCache.fChildIndex = i;
  723. fNodeListCache.fChild = n;
  724. }
  725. return n;
  726. } // nodeListItem(int):Node
  727. /**
  728. * NodeList method: Return the Nth immediate child of this node, or
  729. * null if the index is out of bounds.
  730. * @return org.w3c.dom.Node
  731. * @param index int
  732. */
  733. public Node item(int index) {
  734. return nodeListItem(index);
  735. } // item(int):Node
  736. /**
  737. * Create a NodeList to access children that is use by subclass elements
  738. * that have methods named getLength() or item(int). ChildAndParentNode
  739. * optimizes getChildNodes() by implementing NodeList itself. However if
  740. * a subclass Element implements methods with the same name as the NodeList
  741. * methods, they will override the actually methods in this class.
  742. * <p>
  743. * To use this method, the subclass should implement getChildNodes() and
  744. * have it call this method. The resulting NodeList instance maybe
  745. * shared and cached in a transient field, but the cached value must be
  746. * cleared if the node is cloned.
  747. */
  748. protected final NodeList getChildNodesUnoptimized() {
  749. if (needsSyncChildren()) {
  750. synchronizeChildren();
  751. }
  752. return new NodeList() {
  753. /**
  754. * @see NodeList.getLength()
  755. */
  756. public int getLength() {
  757. return nodeListGetLength();
  758. } // getLength():int
  759. /**
  760. * @see NodeList.item(int)
  761. */
  762. public Node item(int index) {
  763. return nodeListItem(index);
  764. } // item(int):Node
  765. };
  766. } // getChildNodesUnoptimized():NodeList
  767. //
  768. // DOM2: methods, getters, setters
  769. //
  770. /**
  771. * Override default behavior to call normalize() on this Node's
  772. * children. It is up to implementors or Node to override normalize()
  773. * to take action.
  774. */
  775. public void normalize() {
  776. // No need to normalize if already normalized.
  777. if (isNormalized()) {
  778. return;
  779. }
  780. if (needsSyncChildren()) {
  781. synchronizeChildren();
  782. }
  783. ChildNode kid;
  784. for (kid = firstChild; kid != null; kid = kid.nextSibling) {
  785. kid.normalize();
  786. }
  787. isNormalized(true);
  788. }
  789. /**
  790. * DOM Level 3 WD- Experimental.
  791. * Override inherited behavior from NodeImpl to support deep equal.
  792. */
  793. public boolean isEqualNode(Node arg) {
  794. if (!super.isEqualNode(arg)) {
  795. return false;
  796. }
  797. // there are many ways to do this test, and there isn't any way
  798. // better than another. Performance may vary greatly depending on
  799. // the implementations involved. This one should work fine for us.
  800. Node child1 = getFirstChild();
  801. Node child2 = arg.getFirstChild();
  802. while (child1 != null && child2 != null) {
  803. if (!((NodeImpl) child1).isEqualNode(child2)) {
  804. return false;
  805. }
  806. child1 = child1.getNextSibling();
  807. child2 = child2.getNextSibling();
  808. }
  809. if (child1 != child2) {
  810. return false;
  811. }
  812. return true;
  813. }
  814. //
  815. // Public methods
  816. //
  817. /**
  818. * Override default behavior so that if deep is true, children are also
  819. * toggled.
  820. * @see Node
  821. * <P>
  822. * Note: this will not change the state of an EntityReference or its
  823. * children, which are always read-only.
  824. */
  825. public void setReadOnly(boolean readOnly, boolean deep) {
  826. super.setReadOnly(readOnly, deep);
  827. if (deep) {
  828. if (needsSyncChildren()) {
  829. synchronizeChildren();
  830. }
  831. // Recursively set kids
  832. for (ChildNode mykid = firstChild;
  833. mykid != null;
  834. mykid = mykid.nextSibling) {
  835. if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
  836. mykid.setReadOnly(readOnly,true);
  837. }
  838. }
  839. }
  840. } // setReadOnly(boolean,boolean)
  841. //
  842. // Protected methods
  843. //
  844. /**
  845. * Override this method in subclass to hook in efficient
  846. * internal data structure.
  847. */
  848. protected void synchronizeChildren() {
  849. // By default just change the flag to avoid calling this method again
  850. needsSyncChildren(false);
  851. }
  852. /**
  853. * Checks the normalized state of this node after inserting a child.
  854. * If the inserted child causes this node to be unnormalized, then this
  855. * node is flagged accordingly.
  856. * The conditions for changing the normalized state are:
  857. * <ul>
  858. * <li>The inserted child is a text node and one of its adjacent siblings
  859. * is also a text node.
  860. * <li>The inserted child is is itself unnormalized.
  861. * </ul>
  862. *
  863. * @param insertedChild the child node that was inserted into this node
  864. *
  865. * @throws NullPointerException if the inserted child is <code>null</code>
  866. */
  867. void checkNormalizationAfterInsert(ChildNode insertedChild) {
  868. // See if insertion caused this node to be unnormalized.
  869. if (insertedChild.getNodeType() == Node.TEXT_NODE) {
  870. ChildNode prev = insertedChild.previousSibling();
  871. ChildNode next = insertedChild.nextSibling;
  872. // If an adjacent sibling of the new child is a text node,
  873. // flag this node as unnormalized.
  874. if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
  875. (next != null && next.getNodeType() == Node.TEXT_NODE)) {
  876. isNormalized(false);
  877. }
  878. }
  879. else {
  880. // If the new child is not normalized,
  881. // then this node is inherently not normalized.
  882. if (!insertedChild.isNormalized()) {
  883. isNormalized(false);
  884. }
  885. }
  886. } // checkNormalizationAfterInsert(ChildNode)
  887. /**
  888. * Checks the normalized of this node after removing a child.
  889. * If the removed child causes this node to be unnormalized, then this
  890. * node is flagged accordingly.
  891. * The conditions for changing the normalized state are:
  892. * <ul>
  893. * <li>The removed child had two adjacent siblings that were text nodes.
  894. * </ul>
  895. *
  896. * @param previousSibling the previous sibling of the removed child, or
  897. * <code>null</code>
  898. */
  899. void checkNormalizationAfterRemove(ChildNode previousSibling) {
  900. // See if removal caused this node to be unnormalized.
  901. // If the adjacent siblings of the removed child were both text nodes,
  902. // flag this node as unnormalized.
  903. if (previousSibling != null &&
  904. previousSibling.getNodeType() == Node.TEXT_NODE) {
  905. ChildNode next = previousSibling.nextSibling;
  906. if (next != null && next.getNodeType() == Node.TEXT_NODE) {
  907. isNormalized(false);
  908. }
  909. }
  910. } // checkNormalizationAfterRemove(Node)
  911. //
  912. // Serialization methods
  913. //
  914. /** Serialize object. */
  915. private void writeObject(ObjectOutputStream out) throws IOException {
  916. // synchronize chilren
  917. if (needsSyncChildren()) {
  918. synchronizeChildren();
  919. }
  920. // write object
  921. out.defaultWriteObject();
  922. } // writeObject(ObjectOutputStream)
  923. /** Deserialize object. */
  924. private void readObject(ObjectInputStream ois)
  925. throws ClassNotFoundException, IOException {
  926. // perform default deseralization
  927. ois.defaultReadObject();
  928. // hardset synchildren - so we don't try to sync - it does not make any
  929. // sense to try to synchildren when we just deserialize object.
  930. needsSyncChildren(false);
  931. } // readObject(ObjectInputStream)
  932. /*
  933. * a class to store some user data along with its handler
  934. */
  935. class UserDataRecord implements Serializable {
  936. Object fData;
  937. UserDataHandler fHandler;
  938. UserDataRecord(Object data, UserDataHandler handler) {
  939. fData = data;
  940. fHandler = handler;
  941. }
  942. }
  943. } // class ParentNode