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 org.w3c.dom.Attr;
  59. import org.w3c.dom.DOMException;
  60. import org.w3c.dom.Element;
  61. import org.w3c.dom.NamedNodeMap;
  62. import org.w3c.dom.Node;
  63. import org.w3c.dom.NodeList;
  64. import org.w3c.dom.Text;
  65. import org.w3c.dom.TypeInfo;
  66. import com.sun.org.apache.xerces.internal.util.URI;
  67. /**
  68. * Elements represent most of the "markup" and structure of the
  69. * document. They contain both the data for the element itself
  70. * (element name and attributes), and any contained nodes, including
  71. * document text (as children).
  72. * <P>
  73. * Elements may have Attributes associated with them; the API for this is
  74. * defined in Node, but the function is implemented here. In general, XML
  75. * applications should retrive Attributes as Nodes, since they may contain
  76. * entity references and hence be a fairly complex sub-tree. HTML users will
  77. * be dealing with simple string values, and convenience methods are provided
  78. * to work in terms of Strings.
  79. * <P>
  80. * ElementImpl does not support Namespaces. ElementNSImpl, which inherits from
  81. * it, does.
  82. * @see ElementNSImpl
  83. *
  84. * @author Arnaud Le Hors, IBM
  85. * @author Joe Kesselman, IBM
  86. * @author Andy Clark, IBM
  87. * @author Ralf Pfeiffer, IBM
  88. * @version $Id: ElementImpl.java,v 1.64 2004/01/18 17:39:36 elena Exp $
  89. * @since PR-DOM-Level-1-19980818.
  90. */
  91. public class ElementImpl
  92. extends ParentNode
  93. implements Element {
  94. //
  95. // Constants
  96. //
  97. /** Serialization version. */
  98. static final long serialVersionUID = 3717253516652722278L;
  99. //
  100. // Data
  101. //
  102. /** Element name. */
  103. protected String name;
  104. /** Attributes. */
  105. protected AttributeMap attributes;
  106. /** DOM3: type information */
  107. // REVISIT: we are losing the type information in DOM during serialization
  108. transient TypeInfo type;
  109. //
  110. // Constructors
  111. //
  112. /** Factory constructor. */
  113. public ElementImpl(CoreDocumentImpl ownerDoc, String name) {
  114. super(ownerDoc);
  115. this.name = name;
  116. needsSyncData(true); // synchronizeData will initialize attributes
  117. }
  118. // for ElementNSImpl
  119. protected ElementImpl() {}
  120. // Support for DOM Level 3 renameNode method.
  121. // Note: This only deals with part of the pb. CoreDocumentImpl
  122. // does all the work.
  123. void rename(String name) {
  124. if (needsSyncData()) {
  125. synchronizeData();
  126. }
  127. this.name = name;
  128. reconcileDefaultAttributes();
  129. }
  130. //
  131. // Node methods
  132. //
  133. /**
  134. * A short integer indicating what type of node this is. The named
  135. * constants for this value are defined in the org.w3c.dom.Node interface.
  136. */
  137. public short getNodeType() {
  138. return Node.ELEMENT_NODE;
  139. }
  140. /**
  141. * Returns the element name
  142. */
  143. public String getNodeName() {
  144. if (needsSyncData()) {
  145. synchronizeData();
  146. }
  147. return name;
  148. }
  149. /**
  150. * Retrieve all the Attributes as a set. Note that this API is inherited
  151. * from Node rather than specified on Element; in fact only Elements will
  152. * ever have Attributes, but they want to allow folks to "blindly" operate
  153. * on the tree as a set of Nodes.
  154. */
  155. public NamedNodeMap getAttributes() {
  156. if (needsSyncData()) {
  157. synchronizeData();
  158. }
  159. if (attributes == null) {
  160. attributes = new AttributeMap(this, null);
  161. }
  162. return attributes;
  163. } // getAttributes():NamedNodeMap
  164. /**
  165. * Return a duplicate copy of this Element. Note that its children
  166. * will not be copied unless the "deep" flag is true, but Attributes
  167. * are <i>always</i> replicated.
  168. *
  169. * @see org.w3c.dom.Node#cloneNode(boolean)
  170. */
  171. public Node cloneNode(boolean deep) {
  172. ElementImpl newnode = (ElementImpl) super.cloneNode(deep);
  173. // Replicate NamedNodeMap rather than sharing it.
  174. if (attributes != null) {
  175. newnode.attributes = (AttributeMap) attributes.cloneMap(newnode);
  176. }
  177. return newnode;
  178. } // cloneNode(boolean):Node
  179. /**
  180. * DOM Level 3 WD - Experimental.
  181. * Retrieve baseURI
  182. */
  183. public String getBaseURI() {
  184. if (needsSyncData()) {
  185. synchronizeData();
  186. }
  187. // Absolute base URI is computed according to
  188. // XML Base (http://www.w3.org/TR/xmlbase/#granularity)
  189. // 1. The base URI specified by an xml:base attribute on the element,
  190. // if one exists
  191. if (attributes != null) {
  192. Attr attrNode = (Attr)attributes.getNamedItem("xml:base");
  193. if (attrNode != null) {
  194. String uri = attrNode.getNodeValue();
  195. if (uri.length() != 0 ) {// attribute value is always empty string
  196. try {
  197. uri = new URI(uri).toString();
  198. }
  199. catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
  200. return null;
  201. }
  202. return uri;
  203. }
  204. }
  205. }
  206. // 2.the base URI of the element's parent element within the
  207. // document or external entity, if one exists
  208. // 3. the base URI of the document entity or external entity
  209. // containing the element
  210. // ownerNode serves as a parent or as document
  211. String baseURI = (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null ;
  212. //base URI of parent element is not null
  213. if(baseURI != null){
  214. try {
  215. //return valid absolute base URI
  216. return new URI(baseURI).toString();
  217. }
  218. catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
  219. return null;
  220. }
  221. }
  222. return null;
  223. } //getBaseURI
  224. /**
  225. * NON-DOM
  226. * set the ownerDocument of this node, its children, and its attributes
  227. */
  228. void setOwnerDocument(CoreDocumentImpl doc) {
  229. super.setOwnerDocument(doc);
  230. if (attributes != null) {
  231. attributes.setOwnerDocument(doc);
  232. }
  233. }
  234. //
  235. // Element methods
  236. //
  237. /**
  238. * Look up a single Attribute by name. Returns the Attribute's
  239. * string value, or an empty string (NOT null!) to indicate that the
  240. * name did not map to a currently defined attribute.
  241. * <p>
  242. * Note: Attributes may contain complex node trees. This method
  243. * returns the "flattened" string obtained from Attribute.getValue().
  244. * If you need the structure information, see getAttributeNode().
  245. */
  246. public String getAttribute(String name) {
  247. if (needsSyncData()) {
  248. synchronizeData();
  249. }
  250. if (attributes == null) {
  251. return "";
  252. }
  253. Attr attr = (Attr)(attributes.getNamedItem(name));
  254. return (attr == null) ? "" : attr.getValue();
  255. } // getAttribute(String):String
  256. /**
  257. * Look up a single Attribute by name. Returns the Attribute Node,
  258. * so its complete child tree is available. This could be important in
  259. * XML, where the string rendering may not be sufficient information.
  260. * <p>
  261. * If no matching attribute is available, returns null.
  262. */
  263. public Attr getAttributeNode(String name) {
  264. if (needsSyncData()) {
  265. synchronizeData();
  266. }
  267. if (attributes == null) {
  268. return null;
  269. }
  270. return (Attr)attributes.getNamedItem(name);
  271. } // getAttributeNode(String):Attr
  272. /**
  273. * Returns a NodeList of all descendent nodes (children,
  274. * grandchildren, and so on) which are Elements and which have the
  275. * specified tag name.
  276. * <p>
  277. * Note: NodeList is a "live" view of the DOM. Its contents will
  278. * change as the DOM changes, and alterations made to the NodeList
  279. * will be reflected in the DOM.
  280. *
  281. * @param tagname The type of element to gather. To obtain a list of
  282. * all elements no matter what their names, use the wild-card tag
  283. * name "*".
  284. *
  285. * @see DeepNodeListImpl
  286. */
  287. public NodeList getElementsByTagName(String tagname) {
  288. return new DeepNodeListImpl(this,tagname);
  289. }
  290. /**
  291. * Returns the name of the Element. Note that Element.nodeName() is
  292. * defined to also return the tag name.
  293. * <p>
  294. * This is case-preserving in XML. HTML should uppercasify it on the
  295. * way in.
  296. */
  297. public String getTagName() {
  298. if (needsSyncData()) {
  299. synchronizeData();
  300. }
  301. return name;
  302. }
  303. /**
  304. * In "normal form" (as read from a source file), there will never be two
  305. * Text children in succession. But DOM users may create successive Text
  306. * nodes in the course of manipulating the document. Normalize walks the
  307. * sub-tree and merges adjacent Texts, as if the DOM had been written out
  308. * and read back in again. This simplifies implementation of higher-level
  309. * functions that may want to assume that the document is in standard form.
  310. * <p>
  311. * To normalize a Document, normalize its top-level Element child.
  312. * <p>
  313. * As of PR-DOM-Level-1-19980818, CDATA -- despite being a subclass of
  314. * Text -- is considered "markup" and will _not_ be merged either with
  315. * normal Text or with other CDATASections.
  316. */
  317. public void normalize() {
  318. // No need to normalize if already normalized.
  319. if (isNormalized()) {
  320. return;
  321. }
  322. if (needsSyncChildren()) {
  323. synchronizeChildren();
  324. }
  325. ChildNode kid, next;
  326. for (kid = firstChild; kid != null; kid = next) {
  327. next = kid.nextSibling;
  328. // If kid is a text node, we need to check for one of two
  329. // conditions:
  330. // 1) There is an adjacent text node
  331. // 2) There is no adjacent text node, but kid is
  332. // an empty text node.
  333. if ( kid.getNodeType() == Node.TEXT_NODE )
  334. {
  335. // If an adjacent text node, merge it with kid
  336. if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
  337. {
  338. ((Text)kid).appendData(next.getNodeValue());
  339. removeChild( next );
  340. next = kid; // Don't advance; there might be another.
  341. }
  342. else
  343. {
  344. // If kid is empty, remove it
  345. if ( kid.getNodeValue().length()==0 )
  346. removeChild( kid );
  347. }
  348. }
  349. // Otherwise it might be an Element, which is handled recursively
  350. else if (kid.getNodeType() == Node.ELEMENT_NODE) {
  351. kid.normalize();
  352. }
  353. }
  354. // We must also normalize all of the attributes
  355. if ( attributes!=null )
  356. {
  357. for( int i=0; i<attributes.getLength(); ++i )
  358. {
  359. Node attr = attributes.item(i);
  360. attr.normalize();
  361. }
  362. }
  363. // changed() will have occurred when the removeChild() was done,
  364. // so does not have to be reissued.
  365. isNormalized(true);
  366. } // normalize()
  367. /**
  368. * Remove the named attribute from this Element. If the removed
  369. * Attribute has a default value, it is immediately replaced thereby.
  370. * <P>
  371. * The default logic is actually implemented in NamedNodeMapImpl.
  372. * PR-DOM-Level-1-19980818 doesn't fully address the DTD, so some
  373. * of this behavior is likely to change in future versions. ?????
  374. * <P>
  375. * Note that this call "succeeds" even if no attribute by this name
  376. * existed -- unlike removeAttributeNode, which will throw a not-found
  377. * exception in that case.
  378. *
  379. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
  380. * readonly.
  381. */
  382. public void removeAttribute(String name) {
  383. if (ownerDocument.errorChecking && isReadOnly()) {
  384. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  385. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  386. }
  387. if (needsSyncData()) {
  388. synchronizeData();
  389. }
  390. if (attributes == null) {
  391. return;
  392. }
  393. attributes.safeRemoveNamedItem(name);
  394. } // removeAttribute(String)
  395. /**
  396. * Remove the specified attribute/value pair. If the removed
  397. * Attribute has a default value, it is immediately replaced.
  398. * <p>
  399. * NOTE: Specifically removes THIS NODE -- not the node with this
  400. * name, nor the node with these contents. If the specific Attribute
  401. * object passed in is not stored in this Element, we throw a
  402. * DOMException. If you really want to remove an attribute by name,
  403. * use removeAttribute().
  404. *
  405. * @return the Attribute object that was removed.
  406. * @throws DOMException(NOT_FOUND_ERR) if oldattr is not an attribute of
  407. * this Element.
  408. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
  409. * readonly.
  410. */
  411. public Attr removeAttributeNode(Attr oldAttr)
  412. throws DOMException {
  413. if (ownerDocument.errorChecking && isReadOnly()) {
  414. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  415. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  416. }
  417. if (needsSyncData()) {
  418. synchronizeData();
  419. }
  420. if (attributes == null) {
  421. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  422. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  423. }
  424. return (Attr) attributes.removeItem(oldAttr, true);
  425. } // removeAttributeNode(Attr):Attr
  426. /**
  427. * Add a new name/value pair, or replace the value of the existing
  428. * attribute having that name.
  429. *
  430. * Note: this method supports only the simplest kind of Attribute,
  431. * one whose value is a string contained in a single Text node.
  432. * If you want to assert a more complex value (which XML permits,
  433. * though HTML doesn't), see setAttributeNode().
  434. *
  435. * The attribute is created with specified=true, meaning it's an
  436. * explicit value rather than inherited from the DTD as a default.
  437. * Again, setAttributeNode can be used to achieve other results.
  438. *
  439. * @throws DOMException(INVALID_NAME_ERR) if the name is not acceptable.
  440. * (Attribute factory will do that test for us.)
  441. *
  442. * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
  443. * readonly.
  444. */
  445. public void setAttribute(String name, String value) {
  446. if (ownerDocument.errorChecking && isReadOnly()) {
  447. String msg =
  448. DOMMessageFormatter.formatMessage(
  449. DOMMessageFormatter.DOM_DOMAIN,
  450. "NO_MODIFICATION_ALLOWED_ERR",
  451. null);
  452. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  453. }
  454. if (needsSyncData()) {
  455. synchronizeData();
  456. }
  457. Attr newAttr = getAttributeNode(name);
  458. if (newAttr == null) {
  459. newAttr = getOwnerDocument().createAttribute(name);
  460. if (attributes == null) {
  461. attributes = new AttributeMap(this, null);
  462. }
  463. newAttr.setNodeValue(value);
  464. attributes.setNamedItem(newAttr);
  465. }
  466. else {
  467. newAttr.setNodeValue(value);
  468. }
  469. } // setAttribute(String,String)
  470. /**
  471. * Add a new attribute/value pair, or replace the value of the
  472. * existing attribute with that name.
  473. * <P>
  474. * This method allows you to add an Attribute that has already been
  475. * constructed, and hence avoids the limitations of the simple
  476. * setAttribute() call. It can handle attribute values that have
  477. * arbitrarily complex tree structure -- in particular, those which
  478. * had entity references mixed into their text.
  479. *
  480. * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object
  481. * has already been assigned to another Element.
  482. */
  483. public Attr setAttributeNode(Attr newAttr)
  484. throws DOMException
  485. {
  486. if (needsSyncData()) {
  487. synchronizeData();
  488. }
  489. if (ownerDocument.errorChecking) {
  490. if (isReadOnly()) {
  491. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  492. throw new DOMException(
  493. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  494. msg);
  495. }
  496. if (newAttr.getOwnerDocument() != ownerDocument) {
  497. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
  498. throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
  499. }
  500. }
  501. if (attributes == null) {
  502. attributes = new AttributeMap(this, null);
  503. }
  504. // This will throw INUSE if necessary
  505. return (Attr) attributes.setNamedItem(newAttr);
  506. } // setAttributeNode(Attr):Attr
  507. //
  508. // DOM2: Namespace methods
  509. //
  510. /**
  511. * Introduced in DOM Level 2. <p>
  512. *
  513. * Retrieves an attribute value by local name and namespace URI.
  514. *
  515. * @param namespaceURI
  516. * The namespace URI of the attribute to
  517. * retrieve.
  518. * @param localName The local name of the attribute to retrieve.
  519. * @return String The Attr value as a string, or empty string
  520. * if that attribute
  521. * does not have a specified or default value.
  522. * @since WD-DOM-Level-2-19990923
  523. */
  524. public String getAttributeNS(String namespaceURI, String localName) {
  525. if (needsSyncData()) {
  526. synchronizeData();
  527. }
  528. if (attributes == null) {
  529. return "";
  530. }
  531. Attr attr = (Attr)(attributes.getNamedItemNS(namespaceURI, localName));
  532. return (attr == null) ? "" : attr.getValue();
  533. } // getAttributeNS(String,String):String
  534. /**
  535. * Introduced in DOM Level 2. <p>
  536. *
  537. * Adds a new attribute.
  538. * If the given namespaceURI is null or an empty string and the
  539. * qualifiedName has a prefix that is "xml", the new attribute is bound to
  540. * the predefined namespace "http://www.w3.org/XML/1998/namespace"
  541. * [Namespaces]. If an attribute with the same local name and namespace
  542. * URI is already present on the element, its prefix is changed to be the
  543. * prefix part of the qualifiedName, and its value is changed to be the
  544. * value parameter. This value is a simple string, it is not parsed as it
  545. * is being set. So any markup (such as syntax to be recognized as an
  546. * entity reference) is treated as literal text, and needs to be
  547. * appropriately escaped by the implementation when it is written out. In
  548. * order to assign an attribute value that contains entity references, the
  549. * user must create an Attr node plus any Text and EntityReference nodes,
  550. * build the appropriate subtree, and use setAttributeNodeNS or
  551. * setAttributeNode to assign it as the value of an attribute.
  552. *
  553. * @param namespaceURI The namespace URI of the attribute to create
  554. * or alter.
  555. * @param qualifiedName The qualified name of the attribute to create or
  556. * alter.
  557. * @param value The value to set in string form.
  558. * @throws INVALID_CHARACTER_ERR: Raised if the specified
  559. * name contains an invalid character.
  560. *
  561. * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this
  562. * node is readonly.
  563. *
  564. * @throws NAMESPACE_ERR: Raised if the qualifiedName
  565. * has a prefix that is "xml" and the namespaceURI
  566. * is neither null nor an empty string nor
  567. * "http://www.w3.org/XML/1998/namespace", or if
  568. * the qualifiedName has a prefix that is "xmlns"
  569. * but the namespaceURI is neither null nor an
  570. * empty string, or if if the qualifiedName has a
  571. * prefix different from "xml" and "xmlns" and the
  572. * namespaceURI is null or an empty string.
  573. * @since WD-DOM-Level-2-19990923
  574. */
  575. public void setAttributeNS(String namespaceURI,String qualifiedName,
  576. String value) {
  577. if (ownerDocument.errorChecking && isReadOnly()) {
  578. String msg =
  579. DOMMessageFormatter.formatMessage(
  580. DOMMessageFormatter.DOM_DOMAIN,
  581. "NO_MODIFICATION_ALLOWED_ERR",
  582. null);
  583. throw new DOMException(
  584. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  585. msg);
  586. }
  587. if (needsSyncData()) {
  588. synchronizeData();
  589. }
  590. int index = qualifiedName.indexOf(':');
  591. String prefix, localName;
  592. if (index < 0) {
  593. prefix = null;
  594. localName = qualifiedName;
  595. }
  596. else {
  597. prefix = qualifiedName.substring(0, index);
  598. localName = qualifiedName.substring(index + 1);
  599. }
  600. Attr newAttr = getAttributeNodeNS(namespaceURI, localName);
  601. if (newAttr == null) {
  602. // REVISIT: this is not efficient, we are creating twice the same
  603. // strings for prefix and localName.
  604. newAttr = getOwnerDocument().createAttributeNS(
  605. namespaceURI,
  606. qualifiedName);
  607. if (attributes == null) {
  608. attributes = new AttributeMap(this, null);
  609. }
  610. newAttr.setNodeValue(value);
  611. attributes.setNamedItemNS(newAttr);
  612. }
  613. else {
  614. if (newAttr instanceof AttrNSImpl){
  615. // change prefix and value
  616. ((AttrNSImpl)newAttr).name= (prefix!=null)?(prefix+":"+localName):localName;
  617. }
  618. else {
  619. // This case may happen if user calls:
  620. // elem.setAttribute("name", "value");
  621. // elem.setAttributeNS(null, "name", "value");
  622. // This case is not defined by the DOM spec, we choose
  623. // to create a new attribute in this case and remove an old one from the tree
  624. // note this might cause events to be propagated or user data to be lost
  625. newAttr = new AttrNSImpl((CoreDocumentImpl)getOwnerDocument(), namespaceURI, qualifiedName, localName);
  626. attributes.setNamedItemNS(newAttr);
  627. }
  628. newAttr.setNodeValue(value);
  629. }
  630. } // setAttributeNS(String,String,String)
  631. /**
  632. * Introduced in DOM Level 2. <p>
  633. *
  634. * Removes an attribute by local name and namespace URI. If the removed
  635. * attribute has a default value it is immediately replaced.
  636. * The replacing attribute has the same namespace URI and local name,
  637. * as well as the original prefix.<p>
  638. *
  639. * @param namespaceURI The namespace URI of the attribute to remove.
  640. *
  641. * @param localName The local name of the attribute to remove.
  642. * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this
  643. * node is readonly.
  644. * @since WD-DOM-Level-2-19990923
  645. */
  646. public void removeAttributeNS(String namespaceURI, String localName) {
  647. if (ownerDocument.errorChecking && isReadOnly()) {
  648. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  649. throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  650. }
  651. if (needsSyncData()) {
  652. synchronizeData();
  653. }
  654. if (attributes == null) {
  655. return;
  656. }
  657. attributes.safeRemoveNamedItemNS(namespaceURI, localName);
  658. } // removeAttributeNS(String,String)
  659. /**
  660. * Retrieves an Attr node by local name and namespace URI.
  661. *
  662. * @param namespaceURI The namespace URI of the attribute to
  663. * retrieve.
  664. * @param localName The local name of the attribute to retrieve.
  665. * @return Attr The Attr node with the specified attribute
  666. * local name and namespace
  667. * URI or null if there is no such attribute.
  668. * @since WD-DOM-Level-2-19990923
  669. */
  670. public Attr getAttributeNodeNS(String namespaceURI, String localName){
  671. if (needsSyncData()) {
  672. synchronizeData();
  673. }
  674. if (attributes == null) {
  675. return null;
  676. }
  677. return (Attr)attributes.getNamedItemNS(namespaceURI, localName);
  678. } // getAttributeNodeNS(String,String):Attr
  679. /**
  680. * Introduced in DOM Level 2. <p>
  681. *
  682. * Adds a new attribute. If an attribute with that local name and
  683. * namespace URI is already present in the element, it is replaced
  684. * by the new one.
  685. *
  686. * @param Attr The Attr node to add to the attribute list. When
  687. * the Node has no namespaceURI, this method behaves
  688. * like setAttributeNode.
  689. * @return Attr If the newAttr attribute replaces an existing attribute
  690. * with the same local name and namespace URI, the *
  691. * previously existing Attr node is returned, otherwise
  692. * null is returned.
  693. * @throws WRONG_DOCUMENT_ERR: Raised if newAttr
  694. * was created from a different document than the one that
  695. * created the element.
  696. *
  697. * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if
  698. * this node is readonly.
  699. *
  700. * @throws INUSE_ATTRIBUTE_ERR: Raised if newAttr is
  701. * already an attribute of another Element object. The
  702. * DOM user must explicitly clone Attr nodes to re-use
  703. * them in other elements.
  704. * @since WD-DOM-Level-2-19990923
  705. */
  706. public Attr setAttributeNodeNS(Attr newAttr)
  707. throws DOMException
  708. {
  709. if (needsSyncData()) {
  710. synchronizeData();
  711. }
  712. if (ownerDocument.errorChecking) {
  713. if (isReadOnly()) {
  714. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  715. throw new DOMException(
  716. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  717. msg);
  718. }
  719. if (newAttr.getOwnerDocument() != ownerDocument) {
  720. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
  721. throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
  722. }
  723. }
  724. if (attributes == null) {
  725. attributes = new AttributeMap(this, null);
  726. }
  727. // This will throw INUSE if necessary
  728. return (Attr) attributes.setNamedItemNS(newAttr);
  729. } // setAttributeNodeNS(Attr):Attr
  730. /**
  731. * NON-DOM: sets attribute node for this element
  732. */
  733. protected int setXercesAttributeNode (Attr attr){
  734. if (needsSyncData()) {
  735. synchronizeData();
  736. }
  737. if (attributes == null) {
  738. attributes = new AttributeMap(this, null);
  739. }
  740. return attributes.addItem(attr);
  741. }
  742. /**
  743. * NON-DOM: get inded of an attribute
  744. */
  745. protected int getXercesAttribute(String namespaceURI, String localName){
  746. if (needsSyncData()) {
  747. synchronizeData();
  748. }
  749. if (attributes == null) {
  750. return -1;
  751. }
  752. return attributes.getNamedItemIndex(namespaceURI, localName);
  753. }
  754. /**
  755. * Introduced in DOM Level 2.
  756. */
  757. public boolean hasAttributes() {
  758. if (needsSyncData()) {
  759. synchronizeData();
  760. }
  761. return (attributes != null && attributes.getLength() != 0);
  762. }
  763. /**
  764. * Introduced in DOM Level 2.
  765. */
  766. public boolean hasAttribute(String name) {
  767. return getAttributeNode(name) != null;
  768. }
  769. /**
  770. * Introduced in DOM Level 2.
  771. */
  772. public boolean hasAttributeNS(String namespaceURI, String localName) {
  773. return getAttributeNodeNS(namespaceURI, localName) != null;
  774. }
  775. /**
  776. * Introduced in DOM Level 2. <p>
  777. *
  778. * Returns a NodeList of all the Elements with a given local name and
  779. * namespace URI in the order in which they would be encountered in a
  780. * preorder traversal of the Document tree, starting from this node.
  781. *
  782. * @param namespaceURI The namespace URI of the elements to match
  783. * on. The special value "*" matches all
  784. * namespaces. When it is null or an empty
  785. * string, this method behaves like
  786. * getElementsByTagName.
  787. * @param localName The local name of the elements to match on.
  788. * The special value "*" matches all local names.
  789. * @return NodeList A new NodeList object containing all the matched
  790. * Elements.
  791. * @since WD-DOM-Level-2-19990923
  792. */
  793. public NodeList getElementsByTagNameNS(String namespaceURI,
  794. String localName) {
  795. return new DeepNodeListImpl(this, namespaceURI, localName);
  796. }
  797. /**
  798. * DOM Level 3 WD- Experimental.
  799. * Override inherited behavior from NodeImpl and ParentNode to check on
  800. * attributes
  801. */
  802. public boolean isEqualNode(Node arg) {
  803. if (!super.isEqualNode(arg)) {
  804. return false;
  805. }
  806. boolean hasAttrs = hasAttributes();
  807. if (hasAttrs != ((Element) arg).hasAttributes()) {
  808. return false;
  809. }
  810. if (hasAttrs) {
  811. NamedNodeMap map1 = getAttributes();
  812. NamedNodeMap map2 = ((Element) arg).getAttributes();
  813. int len = map1.getLength();
  814. if (len != map2.getLength()) {
  815. return false;
  816. }
  817. for (int i = 0; i < len; i++) {
  818. Node n1 = map1.item(i);
  819. if (n1.getLocalName() == null) { // DOM Level 1 Node
  820. Node n2 = map2.getNamedItem(n1.getNodeName());
  821. if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
  822. return false;
  823. }
  824. }
  825. else {
  826. Node n2 = map2.getNamedItemNS(n1.getNamespaceURI(),
  827. n1.getLocalName());
  828. if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
  829. return false;
  830. }
  831. }
  832. }
  833. }
  834. return true;
  835. }
  836. /**
  837. * DOM Level 3: register the given attribute node as an ID attribute
  838. */
  839. public void setIdAttributeNode(Attr at, boolean makeId) {
  840. if (needsSyncData()) {
  841. synchronizeData();
  842. }
  843. if (ownerDocument.errorChecking) {
  844. if (isReadOnly()) {
  845. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  846. throw new DOMException(
  847. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  848. msg);
  849. }
  850. if (at.getOwnerElement() != this) {
  851. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  852. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  853. }
  854. }
  855. ((AttrImpl) at).isIdAttribute(makeId);
  856. if (!makeId) {
  857. ownerDocument.removeIdentifier(at.getValue());
  858. }
  859. else {
  860. ownerDocument.putIdentifier(at.getValue(), this);
  861. }
  862. }
  863. /**
  864. * DOM Level 3: register the given attribute node as an ID attribute
  865. */
  866. public void setIdAttribute(String name, boolean makeId) {
  867. if (needsSyncData()) {
  868. synchronizeData();
  869. }
  870. Attr at = getAttributeNode(name);
  871. if( at == null){
  872. String msg = DOMMessageFormatter.formatMessage(
  873. DOMMessageFormatter.DOM_DOMAIN,
  874. "NOT_FOUND_ERR", null);
  875. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  876. }
  877. if (ownerDocument.errorChecking) {
  878. if (isReadOnly()) {
  879. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  880. throw new DOMException(
  881. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  882. msg);
  883. }
  884. if (at.getOwnerElement() != this) {
  885. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  886. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  887. }
  888. }
  889. ((AttrImpl) at).isIdAttribute(makeId);
  890. if (!makeId) {
  891. ownerDocument.removeIdentifier(at.getValue());
  892. }
  893. else {
  894. ownerDocument.putIdentifier(at.getValue(), this);
  895. }
  896. }
  897. /**
  898. * DOM Level 3: register the given attribute node as an ID attribute
  899. */
  900. public void setIdAttributeNS(String namespaceURI, String localName,
  901. boolean makeId) {
  902. if (needsSyncData()) {
  903. synchronizeData();
  904. }
  905. Attr at = getAttributeNodeNS(namespaceURI, localName);
  906. if( at == null){
  907. String msg = DOMMessageFormatter.formatMessage(
  908. DOMMessageFormatter.DOM_DOMAIN,
  909. "NOT_FOUND_ERR", null);
  910. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  911. }
  912. if (ownerDocument.errorChecking) {
  913. if (isReadOnly()) {
  914. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  915. throw new DOMException(
  916. DOMException.NO_MODIFICATION_ALLOWED_ERR,
  917. msg);
  918. }
  919. if (at.getOwnerElement() != this) {
  920. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  921. throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  922. }
  923. }
  924. ((AttrImpl) at).isIdAttribute(makeId);
  925. if (!makeId) {
  926. ownerDocument.removeIdentifier(at.getValue());
  927. }
  928. else {
  929. ownerDocument.putIdentifier(at.getValue(), this);
  930. }
  931. }
  932. /**
  933. * NON-DOM: setting type used by the DOM parser
  934. * @see NodeImpl#setReadOnly
  935. */
  936. public void setType(TypeInfo type) {
  937. this.type = type;
  938. }
  939. /**
  940. * Method getSchemaTypeInfo.
  941. * @return TypeInfo
  942. */
  943. public TypeInfo getSchemaTypeInfo(){
  944. if (needsSyncData()) {
  945. synchronizeData();
  946. }
  947. return type;
  948. }
  949. //
  950. // Public methods
  951. //
  952. /**
  953. * NON-DOM: Subclassed to flip the attributes' readonly switch as well.
  954. * @see NodeImpl#setReadOnly
  955. */
  956. public void setReadOnly(boolean readOnly, boolean deep) {
  957. super.setReadOnly(readOnly,deep);
  958. if (attributes != null) {
  959. attributes.setReadOnly(readOnly,true);
  960. }
  961. }
  962. //
  963. // Protected methods
  964. //
  965. /** Synchronizes the data (name and value) for fast nodes. */
  966. protected void synchronizeData() {
  967. // no need to sync in the future
  968. needsSyncData(false);
  969. // we don't want to generate any event for this so turn them off
  970. boolean orig = ownerDocument.getMutationEvents();
  971. ownerDocument.setMutationEvents(false);
  972. // attributes
  973. setupDefaultAttributes();
  974. // set mutation events flag back to its original value
  975. ownerDocument.setMutationEvents(orig);
  976. } // synchronizeData()
  977. // support for DOM Level 3 renameNode method
  978. // @param el The element from which to take the attributes
  979. void moveSpecifiedAttributes(ElementImpl el) {
  980. if (needsSyncData()) {
  981. synchronizeData();
  982. }
  983. if (el.hasAttributes()) {
  984. if (attributes == null) {
  985. attributes = new AttributeMap(this, null);
  986. }
  987. attributes.moveSpecifiedAttributes(el.attributes);
  988. }
  989. }
  990. /** Setup the default attributes. */
  991. protected void setupDefaultAttributes() {
  992. NamedNodeMapImpl defaults = getDefaultAttributes();
  993. if (defaults != null) {
  994. attributes = new AttributeMap(this, defaults);
  995. }
  996. }
  997. /** Reconcile default attributes. */
  998. protected void reconcileDefaultAttributes() {
  999. if (attributes != null) {
  1000. NamedNodeMapImpl defaults = getDefaultAttributes();
  1001. attributes.reconcileDefaults(defaults);
  1002. }
  1003. }
  1004. /** Get the default attributes. */
  1005. protected NamedNodeMapImpl getDefaultAttributes() {
  1006. DocumentTypeImpl doctype =
  1007. (DocumentTypeImpl) ownerDocument.getDoctype();
  1008. if (doctype == null) {
  1009. return null;
  1010. }
  1011. ElementDefinitionImpl eldef =
  1012. (ElementDefinitionImpl)doctype.getElements()
  1013. .getNamedItem(getNodeName());
  1014. if (eldef == null) {
  1015. return null;
  1016. }
  1017. return (NamedNodeMapImpl) eldef.getAttributes();
  1018. } // getDefaultAttributes()
  1019. } // class ElementImpl