1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2004 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.IOException;
  59. import java.io.ObjectInputStream;
  60. import java.io.ObjectOutputStream;
  61. import org.w3c.dom.Attr;
  62. import org.w3c.dom.DOMException;
  63. import org.w3c.dom.Element;
  64. import org.w3c.dom.Node;
  65. import org.w3c.dom.NodeList;
  66. import org.w3c.dom.Text;
  67. import org.w3c.dom.TypeInfo;
  68. /**
  69. * Attribute represents an XML-style attribute of an
  70. * Element. Typically, the allowable values are controlled by its
  71. * declaration in the Document Type Definition (DTD) governing this
  72. * kind of document.
  73. * <P>
  74. * If the attribute has not been explicitly assigned a value, but has
  75. * been declared in the DTD, it will exist and have that default. Only
  76. * if neither the document nor the DTD specifies a value will the
  77. * Attribute really be considered absent and have no value; in that
  78. * case, querying the attribute will return null.
  79. * <P>
  80. * Attributes may have multiple children that contain their data. (XML
  81. * allows attributes to contain entity references, and tokenized
  82. * attribute types such as NMTOKENS may have a child for each token.)
  83. * For convenience, the Attribute object's getValue() method returns
  84. * the string version of the attribute's value.
  85. * <P>
  86. * Attributes are not children of the Elements they belong to, in the
  87. * usual sense, and have no valid Parent reference. However, the spec
  88. * says they _do_ belong to a specific Element, and an INUSE exception
  89. * is to be thrown if the user attempts to explicitly share them
  90. * between elements.
  91. * <P>
  92. * Note that Elements do not permit attributes to appear to be shared
  93. * (see the INUSE exception), so this object's mutability is
  94. * officially not an issue.
  95. * <p>
  96. * Note: The ownerNode attribute is used to store the Element the Attr
  97. * node is associated with. Attr nodes do not have parent nodes.
  98. * Besides, the getOwnerElement() method can be used to get the element node
  99. * this attribute is associated with.
  100. * <P>
  101. * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from
  102. * it, does.
  103. *
  104. * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from
  105. * NodeImpl and provide its own implementation of the ParentNode's behavior.
  106. * The reason is that we now try and avoid to always create a Text node to
  107. * hold the value of an attribute. The DOM spec requires it, so we still have
  108. * to do it in case getFirstChild() is called for instance. The reason
  109. * attribute values are stored as a list of nodes is so that they can carry
  110. * more than a simple string. They can also contain EntityReference nodes.
  111. * However, most of the times people only have a single string that they only
  112. * set and get through Element.set/getAttribute or Attr.set/getValue. In this
  113. * new version, the Attr node has a value pointer which can either be the
  114. * String directly or a pointer to the first ChildNode. A flag tells which one
  115. * it currently is. Note that while we try to stick with the direct String as
  116. * much as possible once we've switched to a node there is no going back. This
  117. * is because we have no way to know whether the application keeps referring to
  118. * the node we once returned.
  119. * <p> The gain in memory varies on the density of attributes in the document.
  120. * But in the tests I've run I've seen up to 12% of memory gain. And the good
  121. * thing is that it also leads to a slight gain in speed because we allocate
  122. * fewer objects! I mean, that's until we have to actually create the node...
  123. * <p>
  124. * To avoid too much duplicated code, I got rid of ParentNode and renamed
  125. * ChildAndParentNode, which I never really liked, to ParentNode for
  126. * simplicity, this doesn't make much of a difference in memory usage because
  127. * there are only very few objects that are only a Parent. This is only true
  128. * now because AttrImpl now inherits directly from NodeImpl and has its own
  129. * implementation of the ParentNode's node behavior. So there is still some
  130. * duplicated code there.
  131. * <p>
  132. * This class doesn't directly support mutation events, however, it notifies
  133. * the document when mutations are performed so that the document class do so.
  134. *
  135. * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  136. * ParentNode, be careful to keep these two classes in sync!
  137. *
  138. * @see AttrNSImpl
  139. *
  140. * @author Arnaud Le Hors, IBM
  141. * @author Joe Kesselman, IBM
  142. * @author Andy Clark, IBM
  143. * @version $Id: AttrImpl.java,v 1.55 2004/02/16 05:34:38 mrglavas Exp $
  144. * @since PR-DOM-Level-1-19980818.
  145. *
  146. */
  147. public class AttrImpl
  148. extends NodeImpl
  149. implements Attr {
  150. //
  151. // Constants
  152. //
  153. /** Serialization version. */
  154. static final long serialVersionUID = 7277707688218972102L;
  155. /** DTD namespace. **/
  156. static final String DTD_URI = "http://www.w3.org/TR/REC-xml";
  157. //
  158. // Data
  159. //
  160. /** This can either be a String or the first child node. */
  161. protected Object value = null;
  162. /** Attribute name. */
  163. protected String name;
  164. /** Type information */
  165. // REVISIT: we are losing the type information in DOM during serialization
  166. transient org.w3c.dom.TypeInfo type;
  167. protected static TextImpl textNode = null;
  168. //
  169. // Constructors
  170. //
  171. /**
  172. * Attribute has no public constructor. Please use the factory
  173. * method in the Document class.
  174. */
  175. protected AttrImpl(CoreDocumentImpl ownerDocument, String name) {
  176. super(ownerDocument);
  177. this.name = name;
  178. /** False for default attributes. */
  179. isSpecified(true);
  180. hasStringValue(true);
  181. }
  182. // for AttrNSImpl
  183. protected AttrImpl() {}
  184. // Support for DOM Level 3 renameNode method.
  185. // Note: This only deals with part of the pb. It is expected to be
  186. // called after the Attr has been detached for one thing.
  187. // CoreDocumentImpl does all the work.
  188. void rename(String name) {
  189. if (needsSyncData()) {
  190. synchronizeData();
  191. }
  192. this.name = name;
  193. }
  194. // create a real text node as child if we don't have one yet
  195. protected void makeChildNode() {
  196. if (hasStringValue()) {
  197. if (value != null) {
  198. TextImpl text =
  199. (TextImpl) ownerDocument().createTextNode((String) value);
  200. value = text;
  201. text.isFirstChild(true);
  202. text.previousSibling = text;
  203. text.ownerNode = this;
  204. text.isOwned(true);
  205. }
  206. hasStringValue(false);
  207. }
  208. }
  209. /**
  210. * NON-DOM
  211. * set the ownerDocument of this node and its children
  212. */
  213. void setOwnerDocument(CoreDocumentImpl doc) {
  214. if (needsSyncChildren()) {
  215. synchronizeChildren();
  216. }
  217. super.setOwnerDocument(doc);
  218. if (!hasStringValue()) {
  219. for (ChildNode child = (ChildNode) value;
  220. child != null; child = child.nextSibling) {
  221. child.setOwnerDocument(doc);
  222. }
  223. }
  224. }
  225. /**
  226. * NON-DOM: set the type of this attribute to be ID type.
  227. *
  228. * @param id
  229. */
  230. public void setIdAttribute(boolean id){
  231. if (needsSyncData()) {
  232. synchronizeData();
  233. }
  234. isIdAttribute(id);
  235. }
  236. /** DOM Level 3: isId*/
  237. public boolean isId(){
  238. // REVISIT: should an attribute that is not in the tree return
  239. // isID true?
  240. return isIdAttribute();
  241. }
  242. //
  243. // Node methods
  244. //
  245. public Node cloneNode(boolean deep) {
  246. if (needsSyncChildren()) {
  247. synchronizeChildren();
  248. }
  249. AttrImpl clone = (AttrImpl) super.cloneNode(deep);
  250. // take care of case where there are kids
  251. if (!clone.hasStringValue()) {
  252. // Need to break the association w/ original kids
  253. clone.value = null;
  254. // Cloning an Attribute always clones its children,
  255. // since they represent its value, no matter whether this
  256. // is a deep clone or not
  257. for (Node child = (Node) value; child != null;
  258. child = child.getNextSibling()) {
  259. clone.appendChild(child.cloneNode(true));
  260. }
  261. }
  262. clone.isSpecified(true);
  263. return clone;
  264. }
  265. /**
  266. * A short integer indicating what type of node this is. The named
  267. * constants for this value are defined in the org.w3c.dom.Node interface.
  268. */
  269. public short getNodeType() {
  270. return Node.ATTRIBUTE_NODE;
  271. }
  272. /**
  273. * Returns the attribute name
  274. */
  275. public String getNodeName() {
  276. if (needsSyncData()) {
  277. synchronizeData();
  278. }
  279. return name;
  280. }
  281. /**
  282. * Implicit in the rerouting of getNodeValue to getValue is the
  283. * need to redefine setNodeValue, for symmetry's sake. Note that
  284. * since we're explicitly providing a value, Specified should be set
  285. * true.... even if that value equals the default.
  286. */
  287. public void setNodeValue(String value) throws DOMException {
  288. setValue(value);
  289. }
  290. /**
  291. * @see org.w3c.dom.TypeInfo#getTypeName()
  292. */
  293. public String getTypeName() {
  294. if(type != null)
  295. return type.getTypeName();
  296. return null;
  297. }
  298. /**
  299. * @see org.w3c.dom.TypeInfo#getTypeNamespace()
  300. */
  301. public String getTypeNamespace() {
  302. if (type != null) {
  303. return DTD_URI;
  304. }
  305. return null;
  306. }
  307. /**
  308. * Method getSchemaTypeInfo.
  309. * @return TypeInfo
  310. */
  311. public TypeInfo getSchemaTypeInfo(){
  312. if (needsSyncData()) {
  313. synchronizeData();
  314. }
  315. return type;
  316. }
  317. /**
  318. * In Attribute objects, NodeValue is considered a synonym for
  319. * Value.
  320. *
  321. * @see #getValue()
  322. */
  323. public String getNodeValue() {
  324. return getValue();
  325. }
  326. //
  327. // Attr methods
  328. //
  329. /**
  330. * In Attributes, NodeName is considered a synonym for the
  331. * attribute's Name
  332. */
  333. public String getName() {
  334. if (needsSyncData()) {
  335. synchronizeData();
  336. }
  337. return name;
  338. } // getName():String
  339. /**
  340. * The DOM doesn't clearly define what setValue(null) means. I've taken it
  341. * as "remove all children", which from outside should appear
  342. * similar to setting it to the empty string.
  343. */
  344. public void setValue(String newvalue) {
  345. if (isReadOnly()) {
  346. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  347. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  348. }
  349. CoreDocumentImpl ownerDocument = ownerDocument();
  350. Element ownerElement = getOwnerElement();
  351. String oldvalue = "";
  352. if (needsSyncData()) {
  353. synchronizeData();
  354. }
  355. if (needsSyncChildren()) {
  356. synchronizeChildren();
  357. }
  358. if (value != null) {
  359. if (ownerDocument.getMutationEvents()) {
  360. // Can no longer just discard the kids; they may have
  361. // event listeners waiting for them to disconnect.
  362. if (hasStringValue()) {
  363. oldvalue = (String) value;
  364. // create an actual text node as our child so
  365. // that we can use it in the event
  366. if (textNode == null) {
  367. textNode = (TextImpl)
  368. ownerDocument.createTextNode((String) value);
  369. }
  370. else {
  371. textNode.data = (String) value;
  372. }
  373. value = textNode;
  374. textNode.isFirstChild(true);
  375. textNode.previousSibling = textNode;
  376. textNode.ownerNode = this;
  377. textNode.isOwned(true);
  378. hasStringValue(false);
  379. internalRemoveChild(textNode, true);
  380. }
  381. else {
  382. oldvalue = getValue();
  383. while (value != null) {
  384. internalRemoveChild((Node) value, true);
  385. }
  386. }
  387. }
  388. else {
  389. if (hasStringValue()) {
  390. oldvalue = (String) value;
  391. }
  392. else {
  393. // simply discard children if any
  394. oldvalue = getValue();
  395. // remove ref from first child to last child
  396. ChildNode firstChild = (ChildNode) value;
  397. firstChild.previousSibling = null;
  398. firstChild.isFirstChild(false);
  399. firstChild.ownerNode = ownerDocument;
  400. }
  401. // then remove ref to current value
  402. value = null;
  403. needsSyncChildren(false);
  404. }
  405. if (isIdAttribute() && ownerElement != null) {
  406. ownerDocument.removeIdentifier(oldvalue);
  407. }
  408. }
  409. // Create and add the new one, generating only non-aggregate events
  410. // (There are no listeners on the new Text, but there may be
  411. // capture/bubble listeners on the Attr.
  412. // Note that aggregate events are NOT dispatched here,
  413. // since we need to combine the remove and insert.
  414. isSpecified(true);
  415. if (ownerDocument.getMutationEvents()) {
  416. // if there are any event handlers create a real node
  417. internalInsertBefore(ownerDocument.createTextNode(newvalue),
  418. null, true);
  419. hasStringValue(false);
  420. // notify document
  421. ownerDocument.modifiedAttrValue(this, oldvalue);
  422. } else {
  423. // directly store the string
  424. value = newvalue;
  425. hasStringValue(true);
  426. changed();
  427. }
  428. if (isIdAttribute() && ownerElement != null) {
  429. ownerDocument.putIdentifier(newvalue, ownerElement);
  430. }
  431. } // setValue(String)
  432. /**
  433. * The "string value" of an Attribute is its text representation,
  434. * which in turn is a concatenation of the string values of its children.
  435. */
  436. public String getValue() {
  437. if (needsSyncData()) {
  438. synchronizeData();
  439. }
  440. if (needsSyncChildren()) {
  441. synchronizeChildren();
  442. }
  443. if (value == null) {
  444. return "";
  445. }
  446. if (hasStringValue()) {
  447. return (String) value;
  448. }
  449. ChildNode firstChild = ((ChildNode) value);
  450. String data = null;
  451. if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){
  452. data = ((EntityReferenceImpl)firstChild).getEntityRefValue();
  453. }
  454. else {
  455. data = firstChild.getNodeValue();
  456. }
  457. ChildNode node = firstChild.nextSibling;
  458. if (node == null || data == null) return (data == null)?"":data;
  459. StringBuffer value = new StringBuffer(data);
  460. while (node != null) {
  461. if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE){
  462. data = ((EntityReferenceImpl)node).getEntityRefValue();
  463. if (data == null) return "";
  464. value.append(data);
  465. }
  466. else {
  467. value.append(node.getNodeValue());
  468. }
  469. node = node.nextSibling;
  470. }
  471. return value.toString();
  472. } // getValue():String
  473. /**
  474. * The "specified" flag is true if and only if this attribute's
  475. * value was explicitly specified in the original document. Note that
  476. * the implementation, not the user, is in charge of this
  477. * property. If the user asserts an Attribute value (even if it ends
  478. * up having the same value as the default), it is considered a
  479. * specified attribute. If you really want to revert to the default,
  480. * delete the attribute from the Element, and the Implementation will
  481. * re-assert the default (if any) in its place, with the appropriate
  482. * specified=false setting.
  483. */
  484. public boolean getSpecified() {
  485. if (needsSyncData()) {
  486. synchronizeData();
  487. }
  488. return isSpecified();
  489. } // getSpecified():boolean
  490. //
  491. // Attr2 methods
  492. //
  493. /**
  494. * Returns the element node that this attribute is associated with,
  495. * or null if the attribute has not been added to an element.
  496. *
  497. * @see #getOwnerElement
  498. *
  499. * @deprecated Previous working draft of DOM Level 2. New method
  500. * is <tt>getOwnerElement()</tt>.
  501. */
  502. public Element getElement() {
  503. // if we have an owner, ownerNode is our ownerElement, otherwise it's
  504. // our ownerDocument and we don't have an ownerElement
  505. return (Element) (isOwned() ? ownerNode : null);
  506. }
  507. /**
  508. * Returns the element node that this attribute is associated with,
  509. * or null if the attribute has not been added to an element.
  510. *
  511. * @since WD-DOM-Level-2-19990719
  512. */
  513. public Element getOwnerElement() {
  514. // if we have an owner, ownerNode is our ownerElement, otherwise it's
  515. // our ownerDocument and we don't have an ownerElement
  516. return (Element) (isOwned() ? ownerNode : null);
  517. }
  518. public void normalize() {
  519. // No need to normalize if already normalized or
  520. // if value is kept as a String.
  521. if (isNormalized() || hasStringValue())
  522. return;
  523. Node kid, next;
  524. ChildNode firstChild = (ChildNode)value;
  525. for (kid = firstChild; kid != null; kid = next) {
  526. next = kid.getNextSibling();
  527. // If kid is a text node, we need to check for one of two
  528. // conditions:
  529. // 1) There is an adjacent text node
  530. // 2) There is no adjacent text node, but kid is
  531. // an empty text node.
  532. if ( kid.getNodeType() == Node.TEXT_NODE )
  533. {
  534. // If an adjacent text node, merge it with kid
  535. if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
  536. {
  537. ((Text)kid).appendData(next.getNodeValue());
  538. removeChild( next );
  539. next = kid; // Don't advance; there might be another.
  540. }
  541. else
  542. {
  543. // If kid is empty, remove it
  544. if ( kid.getNodeValue().length()==0 )
  545. removeChild( kid );
  546. }
  547. }
  548. }
  549. isNormalized(true);
  550. } // normalize()
  551. //
  552. // Public methods
  553. //
  554. /** NON-DOM, for use by parser */
  555. public void setSpecified(boolean arg) {
  556. if (needsSyncData()) {
  557. synchronizeData();
  558. }
  559. isSpecified(arg);
  560. } // setSpecified(boolean)
  561. /**
  562. * NON-DOM: used by the parser
  563. * @param type
  564. */
  565. public void setType (org.w3c.dom.TypeInfo type){
  566. this.type = type;
  567. }
  568. //
  569. // Object methods
  570. //
  571. /** NON-DOM method for debugging convenience */
  572. public String toString() {
  573. return getName() + "=" + "\"" + getValue() + "\"";
  574. }
  575. /**
  576. * Test whether this node has any children. Convenience shorthand
  577. * for (Node.getFirstChild()!=null)
  578. */
  579. public boolean hasChildNodes() {
  580. if (needsSyncChildren()) {
  581. synchronizeChildren();
  582. }
  583. return value != null;
  584. }
  585. /**
  586. * Obtain a NodeList enumerating all children of this node. If there
  587. * are none, an (initially) empty NodeList is returned.
  588. * <p>
  589. * NodeLists are "live"; as children are added/removed the NodeList
  590. * will immediately reflect those changes. Also, the NodeList refers
  591. * to the actual nodes, so changes to those nodes made via the DOM tree
  592. * will be reflected in the NodeList and vice versa.
  593. * <p>
  594. * In this implementation, Nodes implement the NodeList interface and
  595. * provide their own getChildNodes() support. Other DOMs may solve this
  596. * differently.
  597. */
  598. public NodeList getChildNodes() {
  599. // JKESS: KNOWN ISSUE HERE
  600. if (needsSyncChildren()) {
  601. synchronizeChildren();
  602. }
  603. return this;
  604. } // getChildNodes():NodeList
  605. /** The first child of this Node, or null if none. */
  606. public Node getFirstChild() {
  607. if (needsSyncChildren()) {
  608. synchronizeChildren();
  609. }
  610. makeChildNode();
  611. return (Node) value;
  612. } // getFirstChild():Node
  613. /** The last child of this Node, or null if none. */
  614. public Node getLastChild() {
  615. if (needsSyncChildren()) {
  616. synchronizeChildren();
  617. }
  618. return lastChild();
  619. } // getLastChild():Node
  620. final ChildNode lastChild() {
  621. // last child is stored as the previous sibling of first child
  622. makeChildNode();
  623. return value != null ? ((ChildNode) value).previousSibling : null;
  624. }
  625. final void lastChild(ChildNode node) {
  626. // store lastChild as previous sibling of first child
  627. if (value != null) {
  628. ((ChildNode) value).previousSibling = node;
  629. }
  630. }
  631. /**
  632. * Move one or more node(s) to our list of children. Note that this
  633. * implicitly removes them from their previous parent.
  634. *
  635. * @param newChild The Node to be moved to our subtree. As a
  636. * convenience feature, inserting a DocumentNode will instead insert
  637. * all its children.
  638. *
  639. * @param refChild Current child which newChild should be placed
  640. * immediately before. If refChild is null, the insertion occurs
  641. * after all existing Nodes, like appendChild().
  642. *
  643. * @return newChild, in its new state (relocated, or emptied in the case of
  644. * DocumentNode.)
  645. *
  646. * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  647. * type that shouldn't be a child of this node, or if newChild is an
  648. * ancestor of this node.
  649. *
  650. * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  651. * different owner document than we do.
  652. *
  653. * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
  654. * this node.
  655. *
  656. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  657. * read-only.
  658. */
  659. public Node insertBefore(Node newChild, Node refChild)
  660. throws DOMException {
  661. // Tail-call; optimizer should be able to do good things with.
  662. return internalInsertBefore(newChild, refChild, false);
  663. } // insertBefore(Node,Node):Node
  664. /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  665. * to control which mutation events are spawned. This version of the
  666. * insertBefore operation allows us to do so. It is not intended
  667. * for use by application programs.
  668. */
  669. Node internalInsertBefore(Node newChild, Node refChild, boolean replace)
  670. throws DOMException {
  671. CoreDocumentImpl ownerDocument = ownerDocument();
  672. boolean errorChecking = ownerDocument.errorChecking;
  673. if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
  674. // SLOW BUT SAFE: We could insert the whole subtree without
  675. // juggling so many next/previous pointers. (Wipe out the
  676. // parent's child-list, patch the parent pointers, set the
  677. // ends of the list.) But we know some subclasses have special-
  678. // case behavior they add to insertBefore(), so we don't risk it.
  679. // This approch also takes fewer bytecodes.
  680. // NOTE: If one of the children is not a legal child of this
  681. // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
  682. // have been transferred. (Alternative behaviors would be to
  683. // reparent up to the first failure point or reparent all those
  684. // which are acceptable to the target node, neither of which is
  685. // as robust. PR-DOM-0818 isn't entirely clear on which it
  686. // recommends?????
  687. // No need to check kids for right-document; if they weren't,
  688. // they wouldn't be kids of that DocFrag.
  689. if (errorChecking) {
  690. for (Node kid = newChild.getFirstChild(); // Prescan
  691. kid != null; kid = kid.getNextSibling()) {
  692. if (!ownerDocument.isKidOK(this, kid)) {
  693. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
  694. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
  695. }
  696. }
  697. }
  698. while (newChild.hasChildNodes()) {
  699. insertBefore(newChild.getFirstChild(), refChild);
  700. }
  701. return newChild;
  702. }
  703. if (newChild == refChild) {
  704. // stupid case that must be handled as a no-op triggering events...
  705. refChild = refChild.getNextSibling();
  706. removeChild(newChild);
  707. insertBefore(newChild, refChild);
  708. return newChild;
  709. }
  710. if (needsSyncChildren()) {
  711. synchronizeChildren();
  712. }
  713. if (errorChecking) {
  714. if (isReadOnly()) {
  715. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  716. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  717. }
  718. if (newChild.getOwnerDocument() != ownerDocument) {
  719. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
  720. throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
  721. }
  722. if (!ownerDocument.isKidOK(this, newChild)) {
  723. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
  724. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
  725. }
  726. // refChild must be a child of this node (or null)
  727. if (refChild != null && refChild.getParentNode() != this) {
  728. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  729. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  730. }
  731. // Prevent cycles in the tree
  732. // newChild cannot be ancestor of this Node,
  733. // and actually cannot be this
  734. boolean treeSafe = true;
  735. for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
  736. {
  737. treeSafe = newChild != a;
  738. }
  739. if (!treeSafe) {
  740. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
  741. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
  742. }
  743. }
  744. makeChildNode(); // make sure we have a node and not a string
  745. // notify document
  746. ownerDocument.insertingNode(this, replace);
  747. // Convert to internal type, to avoid repeated casting
  748. ChildNode newInternal = (ChildNode)newChild;
  749. Node oldparent = newInternal.parentNode();
  750. if (oldparent != null) {
  751. oldparent.removeChild(newInternal);
  752. }
  753. // Convert to internal type, to avoid repeated casting
  754. ChildNode refInternal = (ChildNode) refChild;
  755. // Attach up
  756. newInternal.ownerNode = this;
  757. newInternal.isOwned(true);
  758. // Attach before and after
  759. // Note: firstChild.previousSibling == lastChild!!
  760. ChildNode firstChild = (ChildNode) value;
  761. if (firstChild == null) {
  762. // this our first and only child
  763. value = newInternal; // firstchild = newInternal;
  764. newInternal.isFirstChild(true);
  765. newInternal.previousSibling = newInternal;
  766. }
  767. else {
  768. if (refInternal == null) {
  769. // this is an append
  770. ChildNode lastChild = firstChild.previousSibling;
  771. lastChild.nextSibling = newInternal;
  772. newInternal.previousSibling = lastChild;
  773. firstChild.previousSibling = newInternal;
  774. }
  775. else {
  776. // this is an insert
  777. if (refChild == firstChild) {
  778. // at the head of the list
  779. firstChild.isFirstChild(false);
  780. newInternal.nextSibling = firstChild;
  781. newInternal.previousSibling = firstChild.previousSibling;
  782. firstChild.previousSibling = newInternal;
  783. value = newInternal; // firstChild = newInternal;
  784. newInternal.isFirstChild(true);
  785. }
  786. else {
  787. // somewhere in the middle
  788. ChildNode prev = refInternal.previousSibling;
  789. newInternal.nextSibling = refInternal;
  790. prev.nextSibling = newInternal;
  791. refInternal.previousSibling = newInternal;
  792. newInternal.previousSibling = prev;
  793. }
  794. }
  795. }
  796. changed();
  797. // notify document
  798. ownerDocument.insertedNode(this, newInternal, replace);
  799. checkNormalizationAfterInsert(newInternal);
  800. return newChild;
  801. } // internalInsertBefore(Node,Node,int):Node
  802. /**
  803. * Remove a child from this Node. The removed child's subtree
  804. * remains intact so it may be re-inserted elsewhere.
  805. *
  806. * @return oldChild, in its new state (removed).
  807. *
  808. * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  809. * this node.
  810. *
  811. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  812. * read-only.
  813. */
  814. public Node removeChild(Node oldChild)
  815. throws DOMException {
  816. // Tail-call, should be optimizable
  817. if (hasStringValue()) {
  818. // we don't have any child per say so it can't be one of them!
  819. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  820. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  821. }
  822. return internalRemoveChild(oldChild, false);
  823. } // removeChild(Node) :Node
  824. /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  825. * to control which mutation events are spawned. This version of the
  826. * removeChild operation allows us to do so. It is not intended
  827. * for use by application programs.
  828. */
  829. Node internalRemoveChild(Node oldChild, boolean replace)
  830. throws DOMException {
  831. CoreDocumentImpl ownerDocument = ownerDocument();
  832. if (ownerDocument.errorChecking) {
  833. if (isReadOnly()) {
  834. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  835. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  836. }
  837. if (oldChild != null && oldChild.getParentNode() != this) {
  838. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  839. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  840. }
  841. }
  842. ChildNode oldInternal = (ChildNode) oldChild;
  843. // notify document
  844. ownerDocument.removingNode(this, oldInternal, replace);
  845. // Patch linked list around oldChild
  846. // Note: lastChild == firstChild.previousSibling
  847. if (oldInternal == value) { // oldInternal == firstChild
  848. // removing first child
  849. oldInternal.isFirstChild(false);
  850. // next line is: firstChild = oldInternal.nextSibling
  851. value = oldInternal.nextSibling;
  852. ChildNode firstChild = (ChildNode) value;
  853. if (firstChild != null) {
  854. firstChild.isFirstChild(true);
  855. firstChild.previousSibling = oldInternal.previousSibling;
  856. }
  857. } else {
  858. ChildNode prev = oldInternal.previousSibling;
  859. ChildNode next = oldInternal.nextSibling;
  860. prev.nextSibling = next;
  861. if (next == null) {
  862. // removing last child
  863. ChildNode firstChild = (ChildNode) value;
  864. firstChild.previousSibling = prev;
  865. } else {
  866. // removing some other child in the middle
  867. next.previousSibling = prev;
  868. }
  869. }
  870. // Save previous sibling for normalization checking.
  871. ChildNode oldPreviousSibling = oldInternal.previousSibling();
  872. // Remove oldInternal's references to tree
  873. oldInternal.ownerNode = ownerDocument;
  874. oldInternal.isOwned(false);
  875. oldInternal.nextSibling = null;
  876. oldInternal.previousSibling = null;
  877. changed();
  878. // notify document
  879. ownerDocument.removedNode(this, replace);
  880. checkNormalizationAfterRemove(oldPreviousSibling);
  881. return oldInternal;
  882. } // internalRemoveChild(Node,int):Node
  883. /**
  884. * Make newChild occupy the location that oldChild used to
  885. * have. Note that newChild will first be removed from its previous
  886. * parent, if any. Equivalent to inserting newChild before oldChild,
  887. * then removing oldChild.
  888. *
  889. * @return oldChild, in its new state (removed).
  890. *
  891. * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  892. * type that shouldn't be a child of this node, or if newChild is
  893. * one of our ancestors.
  894. *
  895. * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  896. * different owner document than we do.
  897. *
  898. * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  899. * this node.
  900. *
  901. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  902. * read-only.
  903. */
  904. public Node replaceChild(Node newChild, Node oldChild)
  905. throws DOMException {
  906. makeChildNode();
  907. // If Mutation Events are being generated, this operation might
  908. // throw aggregate events twice when modifying an Attr -- once
  909. // on insertion and once on removal. DOM Level 2 does not specify
  910. // this as either desirable or undesirable, but hints that
  911. // aggregations should be issued only once per user request.
  912. // notify document
  913. CoreDocumentImpl ownerDocument = ownerDocument();
  914. ownerDocument.replacingNode(this);
  915. internalInsertBefore(newChild, oldChild, true);
  916. if (newChild != oldChild) {
  917. internalRemoveChild(oldChild, true);
  918. }
  919. // notify document
  920. ownerDocument.replacedNode(this);
  921. return oldChild;
  922. }
  923. //
  924. // NodeList methods
  925. //
  926. /**
  927. * NodeList method: Count the immediate children of this node
  928. * @return int
  929. */
  930. public int getLength() {
  931. if (hasStringValue()) {
  932. return 1;
  933. }
  934. ChildNode node = (ChildNode) value;
  935. int length = 0;
  936. for (; node != null; node = node.nextSibling) {
  937. length++;
  938. }
  939. return length;
  940. } // getLength():int
  941. /**
  942. * NodeList method: Return the Nth immediate child of this node, or
  943. * null if the index is out of bounds.
  944. * @return org.w3c.dom.Node
  945. * @param Index int
  946. */
  947. public Node item(int index) {
  948. if (hasStringValue()) {
  949. if (index != 0 || value == null) {
  950. return null;
  951. }
  952. else {
  953. makeChildNode();
  954. return (Node) value;
  955. }
  956. }
  957. ChildNode node = (ChildNode) value;
  958. for (int i = 0; i < index && node != null; i++) {
  959. node = node.nextSibling;
  960. }
  961. return node;
  962. } // item(int):Node
  963. //
  964. // DOM3
  965. //
  966. /**
  967. * DOM Level 3 WD- Experimental.
  968. * Override inherited behavior from ParentNode to support deep equal.
  969. * isEqualNode is always deep on Attr nodes.
  970. */
  971. public boolean isEqualNode(Node arg) {
  972. return super.isEqualNode(arg);
  973. }
  974. /**
  975. * DOM Level 3 WD- Experimental.
  976. * @see org.w3c.dom.TypeInfo#isDerivedFrom()
  977. */
  978. public boolean isDerivedFrom(String typeNamespaceArg,
  979. String typeNameArg,
  980. int derivationMethod) {
  981. return false;
  982. }
  983. //
  984. // Public methods
  985. //
  986. /**
  987. * Override default behavior so that if deep is true, children are also
  988. * toggled.
  989. * @see Node
  990. * <P>
  991. * Note: this will not change the state of an EntityReference or its
  992. * children, which are always read-only.
  993. */
  994. public void setReadOnly(boolean readOnly, boolean deep) {
  995. super.setReadOnly(readOnly, deep);
  996. if (deep) {
  997. if (needsSyncChildren()) {
  998. synchronizeChildren();
  999. }
  1000. if (hasStringValue()) {
  1001. return;
  1002. }
  1003. // Recursively set kids
  1004. for (ChildNode mykid = (ChildNode) value;
  1005. mykid != null;
  1006. mykid = mykid.nextSibling) {
  1007. if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
  1008. mykid.setReadOnly(readOnly,true);
  1009. }
  1010. }
  1011. }
  1012. } // setReadOnly(boolean,boolean)
  1013. //
  1014. // Protected methods
  1015. //
  1016. /**
  1017. * Override this method in subclass to hook in efficient
  1018. * internal data structure.
  1019. */
  1020. protected void synchronizeChildren() {
  1021. // By default just change the flag to avoid calling this method again
  1022. needsSyncChildren(false);
  1023. }
  1024. /**
  1025. * Checks the normalized state of this node after inserting a child.
  1026. * If the inserted child causes this node to be unnormalized, then this
  1027. * node is flagged accordingly.
  1028. * The conditions for changing the normalized state are:
  1029. * <ul>
  1030. * <li>The inserted child is a text node and one of its adjacent siblings
  1031. * is also a text node.
  1032. * <li>The inserted child is is itself unnormalized.
  1033. * </ul>
  1034. *
  1035. * @param insertedChild the child node that was inserted into this node
  1036. *
  1037. * @throws NullPointerException if the inserted child is <code>null</code>
  1038. */
  1039. void checkNormalizationAfterInsert(ChildNode insertedChild) {
  1040. // See if insertion caused this node to be unnormalized.
  1041. if (insertedChild.getNodeType() == Node.TEXT_NODE) {
  1042. ChildNode prev = insertedChild.previousSibling();
  1043. ChildNode next = insertedChild.nextSibling;
  1044. // If an adjacent sibling of the new child is a text node,
  1045. // flag this node as unnormalized.
  1046. if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
  1047. (next != null && next.getNodeType() == Node.TEXT_NODE)) {
  1048. isNormalized(false);
  1049. }
  1050. }
  1051. else {
  1052. // If the new child is not normalized,
  1053. // then this node is inherently not normalized.
  1054. if (!insertedChild.isNormalized()) {
  1055. isNormalized(false);
  1056. }
  1057. }
  1058. } // checkNormalizationAfterInsert(ChildNode)
  1059. /**
  1060. * Checks the normalized of this node after removing a child.
  1061. * If the removed child causes this node to be unnormalized, then this
  1062. * node is flagged accordingly.
  1063. * The conditions for changing the normalized state are:
  1064. * <ul>
  1065. * <li>The removed child had two adjacent siblings that were text nodes.
  1066. * </ul>
  1067. *
  1068. * @param previousSibling the previous sibling of the removed child, or
  1069. * <code>null</code>
  1070. */
  1071. void checkNormalizationAfterRemove(ChildNode previousSibling) {
  1072. // See if removal caused this node to be unnormalized.
  1073. // If the adjacent siblings of the removed child were both text nodes,
  1074. // flag this node as unnormalized.
  1075. if (previousSibling != null &&
  1076. previousSibling.getNodeType() == Node.TEXT_NODE) {
  1077. ChildNode next = previousSibling.nextSibling;
  1078. if (next != null && next.getNodeType() == Node.TEXT_NODE) {
  1079. isNormalized(false);
  1080. }
  1081. }
  1082. } // checkNormalizationAfterRemove(ChildNode)
  1083. //
  1084. // Serialization methods
  1085. //
  1086. /** Serialize object. */
  1087. private void writeObject(ObjectOutputStream out) throws IOException {
  1088. // synchronize chilren
  1089. if (needsSyncChildren()) {
  1090. synchronizeChildren();
  1091. }
  1092. // write object
  1093. out.defaultWriteObject();
  1094. } // writeObject(ObjectOutputStream)
  1095. /** Deserialize object. */
  1096. private void readObject(ObjectInputStream ois)
  1097. throws ClassNotFoundException, IOException {
  1098. // perform default deseralization
  1099. ois.defaultReadObject();
  1100. // hardset synchildren - so we don't try to sync -
  1101. // it does not make any sense to try to synchildren when we just
  1102. // deserialize object.
  1103. needsSyncChildren(false);
  1104. } // readObject(ObjectInputStream)
  1105. } // class AttrImpl