0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.xerces.dom;
0019:
0020: import org.w3c.dom.Attr;
0021: import org.w3c.dom.DOMException;
0022: import org.w3c.dom.Element;
0023: import org.w3c.dom.NamedNodeMap;
0024: import org.w3c.dom.Node;
0025: import org.w3c.dom.NodeList;
0026: import org.w3c.dom.Text;
0027:
0028: import org.w3c.dom.TypeInfo;
0029: import org.apache.xerces.util.URI;
0030:
0031: /**
0032: * Elements represent most of the "markup" and structure of the
0033: * document. They contain both the data for the element itself
0034: * (element name and attributes), and any contained nodes, including
0035: * document text (as children).
0036: * <P>
0037: * Elements may have Attributes associated with them; the API for this is
0038: * defined in Node, but the function is implemented here. In general, XML
0039: * applications should retrive Attributes as Nodes, since they may contain
0040: * entity references and hence be a fairly complex sub-tree. HTML users will
0041: * be dealing with simple string values, and convenience methods are provided
0042: * to work in terms of Strings.
0043: * <P>
0044: * ElementImpl does not support Namespaces. ElementNSImpl, which inherits from
0045: * it, does.
0046: * @see ElementNSImpl
0047: *
0048: * @xerces.internal
0049: *
0050: * @author Arnaud Le Hors, IBM
0051: * @author Joe Kesselman, IBM
0052: * @author Andy Clark, IBM
0053: * @author Ralf Pfeiffer, IBM
0054: * @version $Id: ElementImpl.java 449328 2006-09-23 22:58:23Z mrglavas $
0055: * @since PR-DOM-Level-1-19980818.
0056: */
0057: public class ElementImpl extends ParentNode implements Element,
0058: TypeInfo {
0059:
0060: //
0061: // Constants
0062: //
0063:
0064: /** Serialization version. */
0065: static final long serialVersionUID = 3717253516652722278L;
0066: //
0067: // Data
0068: //
0069:
0070: /** Element name. */
0071: protected String name;
0072:
0073: /** Attributes. */
0074: protected AttributeMap attributes;
0075:
0076: //
0077: // Constructors
0078: //
0079:
0080: /** Factory constructor. */
0081: public ElementImpl(CoreDocumentImpl ownerDoc, String name) {
0082: super (ownerDoc);
0083: this .name = name;
0084: needsSyncData(true); // synchronizeData will initialize attributes
0085: }
0086:
0087: // for ElementNSImpl
0088: protected ElementImpl() {
0089: }
0090:
0091: // Support for DOM Level 3 renameNode method.
0092: // Note: This only deals with part of the pb. CoreDocumentImpl
0093: // does all the work.
0094: void rename(String name) {
0095: if (needsSyncData()) {
0096: synchronizeData();
0097: }
0098: this .name = name;
0099: reconcileDefaultAttributes();
0100: }
0101:
0102: //
0103: // Node methods
0104: //
0105:
0106: /**
0107: * A short integer indicating what type of node this is. The named
0108: * constants for this value are defined in the org.w3c.dom.Node interface.
0109: */
0110: public short getNodeType() {
0111: return Node.ELEMENT_NODE;
0112: }
0113:
0114: /**
0115: * Returns the element name
0116: */
0117: public String getNodeName() {
0118: if (needsSyncData()) {
0119: synchronizeData();
0120: }
0121: return name;
0122: }
0123:
0124: /**
0125: * Retrieve all the Attributes as a set. Note that this API is inherited
0126: * from Node rather than specified on Element; in fact only Elements will
0127: * ever have Attributes, but they want to allow folks to "blindly" operate
0128: * on the tree as a set of Nodes.
0129: */
0130: public NamedNodeMap getAttributes() {
0131:
0132: if (needsSyncData()) {
0133: synchronizeData();
0134: }
0135: if (attributes == null) {
0136: attributes = new AttributeMap(this , null);
0137: }
0138: return attributes;
0139:
0140: } // getAttributes():NamedNodeMap
0141:
0142: /**
0143: * Return a duplicate copy of this Element. Note that its children
0144: * will not be copied unless the "deep" flag is true, but Attributes
0145: * are <i>always</i> replicated.
0146: *
0147: * @see org.w3c.dom.Node#cloneNode(boolean)
0148: */
0149: public Node cloneNode(boolean deep) {
0150:
0151: ElementImpl newnode = (ElementImpl) super .cloneNode(deep);
0152: // Replicate NamedNodeMap rather than sharing it.
0153: if (attributes != null) {
0154: newnode.attributes = (AttributeMap) attributes
0155: .cloneMap(newnode);
0156: }
0157: return newnode;
0158:
0159: } // cloneNode(boolean):Node
0160:
0161: /**
0162: * DOM Level 3 WD - Experimental.
0163: * Retrieve baseURI
0164: */
0165: public String getBaseURI() {
0166:
0167: if (needsSyncData()) {
0168: synchronizeData();
0169: }
0170: // Absolute base URI is computed according to
0171: // XML Base (http://www.w3.org/TR/xmlbase/#granularity)
0172: // 1. The base URI specified by an xml:base attribute on the element,
0173: // if one exists
0174: if (attributes != null) {
0175: Attr attrNode = (Attr) attributes.getNamedItem("xml:base");
0176: if (attrNode != null) {
0177: String uri = attrNode.getNodeValue();
0178: if (uri.length() != 0) {// attribute value is always empty string
0179: try {
0180: uri = new URI(uri).toString();
0181: } catch (org.apache.xerces.util.URI.MalformedURIException e) {
0182: // This may be a relative URI.
0183:
0184: // Make any parentURI into a URI object to use with the URI(URI, String) constructor
0185: String parentBaseURI = (this .ownerNode != null) ? this .ownerNode
0186: .getBaseURI()
0187: : null;
0188: if (parentBaseURI != null) {
0189: try {
0190: uri = new URI(new URI(parentBaseURI),
0191: uri).toString();
0192: } catch (org.apache.xerces.util.URI.MalformedURIException ex) {
0193: // This should never happen: parent should have checked the URI and returned null if invalid.
0194: return null;
0195: }
0196: return uri;
0197: }
0198: return null;
0199: }
0200: return uri;
0201: }
0202: }
0203: }
0204:
0205: // 2.the base URI of the element's parent element within the
0206: // document or external entity, if one exists
0207: // 3. the base URI of the document entity or external entity
0208: // containing the element
0209:
0210: // ownerNode serves as a parent or as document
0211: String baseURI = (this .ownerNode != null) ? this .ownerNode
0212: .getBaseURI() : null;
0213: //base URI of parent element is not null
0214: if (baseURI != null) {
0215: try {
0216: //return valid absolute base URI
0217: return new URI(baseURI).toString();
0218: } catch (org.apache.xerces.util.URI.MalformedURIException e) {
0219: return null;
0220: }
0221: }
0222: return null;
0223: } //getBaseURI
0224:
0225: /**
0226: * NON-DOM
0227: * set the ownerDocument of this node, its children, and its attributes
0228: */
0229: protected void setOwnerDocument(CoreDocumentImpl doc) {
0230: super .setOwnerDocument(doc);
0231: if (attributes != null) {
0232: attributes.setOwnerDocument(doc);
0233: }
0234: }
0235:
0236: //
0237: // Element methods
0238: //
0239:
0240: /**
0241: * Look up a single Attribute by name. Returns the Attribute's
0242: * string value, or an empty string (NOT null!) to indicate that the
0243: * name did not map to a currently defined attribute.
0244: * <p>
0245: * Note: Attributes may contain complex node trees. This method
0246: * returns the "flattened" string obtained from Attribute.getValue().
0247: * If you need the structure information, see getAttributeNode().
0248: */
0249: public String getAttribute(String name) {
0250:
0251: if (needsSyncData()) {
0252: synchronizeData();
0253: }
0254: if (attributes == null) {
0255: return "";
0256: }
0257: Attr attr = (Attr) (attributes.getNamedItem(name));
0258: return (attr == null) ? "" : attr.getValue();
0259:
0260: } // getAttribute(String):String
0261:
0262: /**
0263: * Look up a single Attribute by name. Returns the Attribute Node,
0264: * so its complete child tree is available. This could be important in
0265: * XML, where the string rendering may not be sufficient information.
0266: * <p>
0267: * If no matching attribute is available, returns null.
0268: */
0269: public Attr getAttributeNode(String name) {
0270:
0271: if (needsSyncData()) {
0272: synchronizeData();
0273: }
0274: if (attributes == null) {
0275: return null;
0276: }
0277: return (Attr) attributes.getNamedItem(name);
0278:
0279: } // getAttributeNode(String):Attr
0280:
0281: /**
0282: * Returns a NodeList of all descendent nodes (children,
0283: * grandchildren, and so on) which are Elements and which have the
0284: * specified tag name.
0285: * <p>
0286: * Note: NodeList is a "live" view of the DOM. Its contents will
0287: * change as the DOM changes, and alterations made to the NodeList
0288: * will be reflected in the DOM.
0289: *
0290: * @param tagname The type of element to gather. To obtain a list of
0291: * all elements no matter what their names, use the wild-card tag
0292: * name "*".
0293: *
0294: * @see DeepNodeListImpl
0295: */
0296: public NodeList getElementsByTagName(String tagname) {
0297: return new DeepNodeListImpl(this , tagname);
0298: }
0299:
0300: /**
0301: * Returns the name of the Element. Note that Element.nodeName() is
0302: * defined to also return the tag name.
0303: * <p>
0304: * This is case-preserving in XML. HTML should uppercasify it on the
0305: * way in.
0306: */
0307: public String getTagName() {
0308: if (needsSyncData()) {
0309: synchronizeData();
0310: }
0311: return name;
0312: }
0313:
0314: /**
0315: * In "normal form" (as read from a source file), there will never be two
0316: * Text children in succession. But DOM users may create successive Text
0317: * nodes in the course of manipulating the document. Normalize walks the
0318: * sub-tree and merges adjacent Texts, as if the DOM had been written out
0319: * and read back in again. This simplifies implementation of higher-level
0320: * functions that may want to assume that the document is in standard form.
0321: * <p>
0322: * To normalize a Document, normalize its top-level Element child.
0323: * <p>
0324: * As of PR-DOM-Level-1-19980818, CDATA -- despite being a subclass of
0325: * Text -- is considered "markup" and will _not_ be merged either with
0326: * normal Text or with other CDATASections.
0327: */
0328: public void normalize() {
0329: // No need to normalize if already normalized.
0330: if (isNormalized()) {
0331: return;
0332: }
0333: if (needsSyncChildren()) {
0334: synchronizeChildren();
0335: }
0336: ChildNode kid, next;
0337: for (kid = firstChild; kid != null; kid = next) {
0338: next = kid.nextSibling;
0339:
0340: // If kid is a text node, we need to check for one of two
0341: // conditions:
0342: // 1) There is an adjacent text node
0343: // 2) There is no adjacent text node, but kid is
0344: // an empty text node.
0345: if (kid.getNodeType() == Node.TEXT_NODE) {
0346: // If an adjacent text node, merge it with kid
0347: if (next != null
0348: && next.getNodeType() == Node.TEXT_NODE) {
0349: ((Text) kid).appendData(next.getNodeValue());
0350: removeChild(next);
0351: next = kid; // Don't advance; there might be another.
0352: } else {
0353: // If kid is empty, remove it
0354: if (kid.getNodeValue() == null
0355: || kid.getNodeValue().length() == 0) {
0356: removeChild(kid);
0357: }
0358: }
0359: }
0360:
0361: // Otherwise it might be an Element, which is handled recursively
0362: else if (kid.getNodeType() == Node.ELEMENT_NODE) {
0363: kid.normalize();
0364: }
0365: }
0366:
0367: // We must also normalize all of the attributes
0368: if (attributes != null) {
0369: for (int i = 0; i < attributes.getLength(); ++i) {
0370: Node attr = attributes.item(i);
0371: attr.normalize();
0372: }
0373: }
0374:
0375: // changed() will have occurred when the removeChild() was done,
0376: // so does not have to be reissued.
0377:
0378: isNormalized(true);
0379: } // normalize()
0380:
0381: /**
0382: * Remove the named attribute from this Element. If the removed
0383: * Attribute has a default value, it is immediately replaced thereby.
0384: * <P>
0385: * The default logic is actually implemented in NamedNodeMapImpl.
0386: * PR-DOM-Level-1-19980818 doesn't fully address the DTD, so some
0387: * of this behavior is likely to change in future versions. ?????
0388: * <P>
0389: * Note that this call "succeeds" even if no attribute by this name
0390: * existed -- unlike removeAttributeNode, which will throw a not-found
0391: * exception in that case.
0392: *
0393: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
0394: * readonly.
0395: */
0396: public void removeAttribute(String name) {
0397:
0398: if (ownerDocument.errorChecking && isReadOnly()) {
0399: String msg = DOMMessageFormatter.formatMessage(
0400: DOMMessageFormatter.DOM_DOMAIN,
0401: "NO_MODIFICATION_ALLOWED_ERR", null);
0402: throw new DOMException(
0403: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0404: }
0405:
0406: if (needsSyncData()) {
0407: synchronizeData();
0408: }
0409:
0410: if (attributes == null) {
0411: return;
0412: }
0413:
0414: attributes.safeRemoveNamedItem(name);
0415:
0416: } // removeAttribute(String)
0417:
0418: /**
0419: * Remove the specified attribute/value pair. If the removed
0420: * Attribute has a default value, it is immediately replaced.
0421: * <p>
0422: * NOTE: Specifically removes THIS NODE -- not the node with this
0423: * name, nor the node with these contents. If the specific Attribute
0424: * object passed in is not stored in this Element, we throw a
0425: * DOMException. If you really want to remove an attribute by name,
0426: * use removeAttribute().
0427: *
0428: * @return the Attribute object that was removed.
0429: * @throws DOMException(NOT_FOUND_ERR) if oldattr is not an attribute of
0430: * this Element.
0431: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
0432: * readonly.
0433: */
0434: public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
0435:
0436: if (ownerDocument.errorChecking && isReadOnly()) {
0437: String msg = DOMMessageFormatter.formatMessage(
0438: DOMMessageFormatter.DOM_DOMAIN,
0439: "NO_MODIFICATION_ALLOWED_ERR", null);
0440: throw new DOMException(
0441: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0442: }
0443:
0444: if (needsSyncData()) {
0445: synchronizeData();
0446: }
0447:
0448: if (attributes == null) {
0449: String msg = DOMMessageFormatter.formatMessage(
0450: DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR",
0451: null);
0452: throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
0453: }
0454: return (Attr) attributes.removeItem(oldAttr, true);
0455:
0456: } // removeAttributeNode(Attr):Attr
0457:
0458: /**
0459: * Add a new name/value pair, or replace the value of the existing
0460: * attribute having that name.
0461: *
0462: * Note: this method supports only the simplest kind of Attribute,
0463: * one whose value is a string contained in a single Text node.
0464: * If you want to assert a more complex value (which XML permits,
0465: * though HTML doesn't), see setAttributeNode().
0466: *
0467: * The attribute is created with specified=true, meaning it's an
0468: * explicit value rather than inherited from the DTD as a default.
0469: * Again, setAttributeNode can be used to achieve other results.
0470: *
0471: * @throws DOMException(INVALID_NAME_ERR) if the name is not acceptable.
0472: * (Attribute factory will do that test for us.)
0473: *
0474: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
0475: * readonly.
0476: */
0477: public void setAttribute(String name, String value) {
0478:
0479: if (ownerDocument.errorChecking && isReadOnly()) {
0480: String msg = DOMMessageFormatter.formatMessage(
0481: DOMMessageFormatter.DOM_DOMAIN,
0482: "NO_MODIFICATION_ALLOWED_ERR", null);
0483: throw new DOMException(
0484: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0485: }
0486:
0487: if (needsSyncData()) {
0488: synchronizeData();
0489: }
0490:
0491: Attr newAttr = getAttributeNode(name);
0492: if (newAttr == null) {
0493: newAttr = getOwnerDocument().createAttribute(name);
0494:
0495: if (attributes == null) {
0496: attributes = new AttributeMap(this , null);
0497: }
0498:
0499: newAttr.setNodeValue(value);
0500: attributes.setNamedItem(newAttr);
0501: } else {
0502: newAttr.setNodeValue(value);
0503: }
0504:
0505: } // setAttribute(String,String)
0506:
0507: /**
0508: * Add a new attribute/value pair, or replace the value of the
0509: * existing attribute with that name.
0510: * <P>
0511: * This method allows you to add an Attribute that has already been
0512: * constructed, and hence avoids the limitations of the simple
0513: * setAttribute() call. It can handle attribute values that have
0514: * arbitrarily complex tree structure -- in particular, those which
0515: * had entity references mixed into their text.
0516: *
0517: * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object
0518: * has already been assigned to another Element.
0519: */
0520: public Attr setAttributeNode(Attr newAttr) throws DOMException {
0521:
0522: if (needsSyncData()) {
0523: synchronizeData();
0524: }
0525:
0526: if (ownerDocument.errorChecking) {
0527: if (isReadOnly()) {
0528: String msg = DOMMessageFormatter.formatMessage(
0529: DOMMessageFormatter.DOM_DOMAIN,
0530: "NO_MODIFICATION_ALLOWED_ERR", null);
0531: throw new DOMException(
0532: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0533: }
0534:
0535: if (newAttr.getOwnerDocument() != ownerDocument) {
0536: String msg = DOMMessageFormatter.formatMessage(
0537: DOMMessageFormatter.DOM_DOMAIN,
0538: "WRONG_DOCUMENT_ERR", null);
0539: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
0540: msg);
0541: }
0542: }
0543:
0544: if (attributes == null) {
0545: attributes = new AttributeMap(this , null);
0546: }
0547: // This will throw INUSE if necessary
0548: return (Attr) attributes.setNamedItem(newAttr);
0549:
0550: } // setAttributeNode(Attr):Attr
0551:
0552: //
0553: // DOM2: Namespace methods
0554: //
0555:
0556: /**
0557: * Introduced in DOM Level 2. <p>
0558: *
0559: * Retrieves an attribute value by local name and namespace URI.
0560: *
0561: * @param namespaceURI
0562: * The namespace URI of the attribute to
0563: * retrieve.
0564: * @param localName The local name of the attribute to retrieve.
0565: * @return String The Attr value as a string, or empty string
0566: * if that attribute
0567: * does not have a specified or default value.
0568: * @since WD-DOM-Level-2-19990923
0569: */
0570: public String getAttributeNS(String namespaceURI, String localName) {
0571:
0572: if (needsSyncData()) {
0573: synchronizeData();
0574: }
0575:
0576: if (attributes == null) {
0577: return "";
0578: }
0579:
0580: Attr attr = (Attr) (attributes.getNamedItemNS(namespaceURI,
0581: localName));
0582: return (attr == null) ? "" : attr.getValue();
0583:
0584: } // getAttributeNS(String,String):String
0585:
0586: /**
0587: * Introduced in DOM Level 2. <p>
0588: *
0589: * Adds a new attribute.
0590: * If the given namespaceURI is null or an empty string and the
0591: * qualifiedName has a prefix that is "xml", the new attribute is bound to
0592: * the predefined namespace "http://www.w3.org/XML/1998/namespace"
0593: * [Namespaces]. If an attribute with the same local name and namespace
0594: * URI is already present on the element, its prefix is changed to be the
0595: * prefix part of the qualifiedName, and its value is changed to be the
0596: * value parameter. This value is a simple string, it is not parsed as it
0597: * is being set. So any markup (such as syntax to be recognized as an
0598: * entity reference) is treated as literal text, and needs to be
0599: * appropriately escaped by the implementation when it is written out. In
0600: * order to assign an attribute value that contains entity references, the
0601: * user must create an Attr node plus any Text and EntityReference nodes,
0602: * build the appropriate subtree, and use setAttributeNodeNS or
0603: * setAttributeNode to assign it as the value of an attribute.
0604: *
0605: * @param namespaceURI The namespace URI of the attribute to create
0606: * or alter.
0607: * @param qualifiedName The qualified name of the attribute to create or
0608: * alter.
0609: * @param value The value to set in string form.
0610: * @throws INVALID_CHARACTER_ERR: Raised if the specified
0611: * name contains an invalid character.
0612: *
0613: * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this
0614: * node is readonly.
0615: *
0616: * @throws NAMESPACE_ERR: Raised if the qualifiedName
0617: * has a prefix that is "xml" and the namespaceURI
0618: * is neither null nor an empty string nor
0619: * "http://www.w3.org/XML/1998/namespace", or if
0620: * the qualifiedName has a prefix that is "xmlns"
0621: * but the namespaceURI is neither null nor an
0622: * empty string, or if if the qualifiedName has a
0623: * prefix different from "xml" and "xmlns" and the
0624: * namespaceURI is null or an empty string.
0625: * @since WD-DOM-Level-2-19990923
0626: */
0627: public void setAttributeNS(String namespaceURI,
0628: String qualifiedName, String value) {
0629: if (ownerDocument.errorChecking && isReadOnly()) {
0630: String msg = DOMMessageFormatter.formatMessage(
0631: DOMMessageFormatter.DOM_DOMAIN,
0632: "NO_MODIFICATION_ALLOWED_ERR", null);
0633: throw new DOMException(
0634: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0635: }
0636: if (needsSyncData()) {
0637: synchronizeData();
0638: }
0639: int index = qualifiedName.indexOf(':');
0640: String prefix, localName;
0641: if (index < 0) {
0642: prefix = null;
0643: localName = qualifiedName;
0644: } else {
0645: prefix = qualifiedName.substring(0, index);
0646: localName = qualifiedName.substring(index + 1);
0647: }
0648: Attr newAttr = getAttributeNodeNS(namespaceURI, localName);
0649: if (newAttr == null) {
0650: // REVISIT: this is not efficient, we are creating twice the same
0651: // strings for prefix and localName.
0652: newAttr = getOwnerDocument().createAttributeNS(
0653: namespaceURI, qualifiedName);
0654: if (attributes == null) {
0655: attributes = new AttributeMap(this , null);
0656: }
0657: newAttr.setNodeValue(value);
0658: attributes.setNamedItemNS(newAttr);
0659: } else {
0660: if (newAttr instanceof AttrNSImpl) {
0661: // change prefix and value
0662: ((AttrNSImpl) newAttr).name = (prefix != null) ? (prefix
0663: + ":" + localName)
0664: : localName;
0665: } else {
0666: // This case may happen if user calls:
0667: // elem.setAttribute("name", "value");
0668: // elem.setAttributeNS(null, "name", "value");
0669: // This case is not defined by the DOM spec, we choose
0670: // to create a new attribute in this case and remove an old one from the tree
0671: // note this might cause events to be propagated or user data to be lost
0672: newAttr = new AttrNSImpl(
0673: (CoreDocumentImpl) getOwnerDocument(),
0674: namespaceURI, qualifiedName, localName);
0675: attributes.setNamedItemNS(newAttr);
0676: }
0677:
0678: newAttr.setNodeValue(value);
0679: }
0680:
0681: } // setAttributeNS(String,String,String)
0682:
0683: /**
0684: * Introduced in DOM Level 2. <p>
0685: *
0686: * Removes an attribute by local name and namespace URI. If the removed
0687: * attribute has a default value it is immediately replaced.
0688: * The replacing attribute has the same namespace URI and local name,
0689: * as well as the original prefix.<p>
0690: *
0691: * @param namespaceURI The namespace URI of the attribute to remove.
0692: *
0693: * @param localName The local name of the attribute to remove.
0694: * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this
0695: * node is readonly.
0696: * @since WD-DOM-Level-2-19990923
0697: */
0698: public void removeAttributeNS(String namespaceURI, String localName) {
0699:
0700: if (ownerDocument.errorChecking && isReadOnly()) {
0701: String msg = DOMMessageFormatter.formatMessage(
0702: DOMMessageFormatter.DOM_DOMAIN,
0703: "NO_MODIFICATION_ALLOWED_ERR", null);
0704: throw new DOMException(
0705: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0706: }
0707:
0708: if (needsSyncData()) {
0709: synchronizeData();
0710: }
0711:
0712: if (attributes == null) {
0713: return;
0714: }
0715:
0716: attributes.safeRemoveNamedItemNS(namespaceURI, localName);
0717:
0718: } // removeAttributeNS(String,String)
0719:
0720: /**
0721: * Retrieves an Attr node by local name and namespace URI.
0722: *
0723: * @param namespaceURI The namespace URI of the attribute to
0724: * retrieve.
0725: * @param localName The local name of the attribute to retrieve.
0726: * @return Attr The Attr node with the specified attribute
0727: * local name and namespace
0728: * URI or null if there is no such attribute.
0729: * @since WD-DOM-Level-2-19990923
0730: */
0731: public Attr getAttributeNodeNS(String namespaceURI, String localName) {
0732:
0733: if (needsSyncData()) {
0734: synchronizeData();
0735: }
0736: if (attributes == null) {
0737: return null;
0738: }
0739: return (Attr) attributes
0740: .getNamedItemNS(namespaceURI, localName);
0741:
0742: } // getAttributeNodeNS(String,String):Attr
0743:
0744: /**
0745: * Introduced in DOM Level 2. <p>
0746: *
0747: * Adds a new attribute. If an attribute with that local name and
0748: * namespace URI is already present in the element, it is replaced
0749: * by the new one.
0750: *
0751: * @param newAttr The Attr node to add to the attribute list. When
0752: * the Node has no namespaceURI, this method behaves
0753: * like setAttributeNode.
0754: * @return Attr If the newAttr attribute replaces an existing attribute
0755: * with the same local name and namespace URI, the *
0756: * previously existing Attr node is returned, otherwise
0757: * null is returned.
0758: * @throws WRONG_DOCUMENT_ERR: Raised if newAttr
0759: * was created from a different document than the one that
0760: * created the element.
0761: *
0762: * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if
0763: * this node is readonly.
0764: *
0765: * @throws INUSE_ATTRIBUTE_ERR: Raised if newAttr is
0766: * already an attribute of another Element object. The
0767: * DOM user must explicitly clone Attr nodes to re-use
0768: * them in other elements.
0769: * @since WD-DOM-Level-2-19990923
0770: */
0771: public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
0772:
0773: if (needsSyncData()) {
0774: synchronizeData();
0775: }
0776: if (ownerDocument.errorChecking) {
0777: if (isReadOnly()) {
0778: String msg = DOMMessageFormatter.formatMessage(
0779: DOMMessageFormatter.DOM_DOMAIN,
0780: "NO_MODIFICATION_ALLOWED_ERR", null);
0781: throw new DOMException(
0782: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0783: }
0784: if (newAttr.getOwnerDocument() != ownerDocument) {
0785: String msg = DOMMessageFormatter.formatMessage(
0786: DOMMessageFormatter.DOM_DOMAIN,
0787: "WRONG_DOCUMENT_ERR", null);
0788: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
0789: msg);
0790: }
0791: }
0792:
0793: if (attributes == null) {
0794: attributes = new AttributeMap(this , null);
0795: }
0796: // This will throw INUSE if necessary
0797: return (Attr) attributes.setNamedItemNS(newAttr);
0798:
0799: } // setAttributeNodeNS(Attr):Attr
0800:
0801: /**
0802: * NON-DOM: sets attribute node for this element
0803: */
0804: protected int setXercesAttributeNode(Attr attr) {
0805:
0806: if (needsSyncData()) {
0807: synchronizeData();
0808: }
0809:
0810: if (attributes == null) {
0811: attributes = new AttributeMap(this , null);
0812: }
0813: return attributes.addItem(attr);
0814:
0815: }
0816:
0817: /**
0818: * NON-DOM: get inded of an attribute
0819: */
0820: protected int getXercesAttribute(String namespaceURI,
0821: String localName) {
0822:
0823: if (needsSyncData()) {
0824: synchronizeData();
0825: }
0826: if (attributes == null) {
0827: return -1;
0828: }
0829: return attributes.getNamedItemIndex(namespaceURI, localName);
0830:
0831: }
0832:
0833: /**
0834: * Introduced in DOM Level 2.
0835: */
0836: public boolean hasAttributes() {
0837: if (needsSyncData()) {
0838: synchronizeData();
0839: }
0840: return (attributes != null && attributes.getLength() != 0);
0841: }
0842:
0843: /**
0844: * Introduced in DOM Level 2.
0845: */
0846: public boolean hasAttribute(String name) {
0847: return getAttributeNode(name) != null;
0848: }
0849:
0850: /**
0851: * Introduced in DOM Level 2.
0852: */
0853: public boolean hasAttributeNS(String namespaceURI, String localName) {
0854: return getAttributeNodeNS(namespaceURI, localName) != null;
0855: }
0856:
0857: /**
0858: * Introduced in DOM Level 2. <p>
0859: *
0860: * Returns a NodeList of all the Elements with a given local name and
0861: * namespace URI in the order in which they would be encountered in a
0862: * preorder traversal of the Document tree, starting from this node.
0863: *
0864: * @param namespaceURI The namespace URI of the elements to match
0865: * on. The special value "*" matches all
0866: * namespaces. When it is null or an empty
0867: * string, this method behaves like
0868: * getElementsByTagName.
0869: * @param localName The local name of the elements to match on.
0870: * The special value "*" matches all local names.
0871: * @return NodeList A new NodeList object containing all the matched
0872: * Elements.
0873: * @since WD-DOM-Level-2-19990923
0874: */
0875: public NodeList getElementsByTagNameNS(String namespaceURI,
0876: String localName) {
0877: return new DeepNodeListImpl(this , namespaceURI, localName);
0878: }
0879:
0880: /**
0881: * DOM Level 3 WD- Experimental.
0882: * Override inherited behavior from NodeImpl and ParentNode to check on
0883: * attributes
0884: */
0885: public boolean isEqualNode(Node arg) {
0886: if (!super .isEqualNode(arg)) {
0887: return false;
0888: }
0889: boolean hasAttrs = hasAttributes();
0890: if (hasAttrs != ((Element) arg).hasAttributes()) {
0891: return false;
0892: }
0893: if (hasAttrs) {
0894: NamedNodeMap map1 = getAttributes();
0895: NamedNodeMap map2 = ((Element) arg).getAttributes();
0896: int len = map1.getLength();
0897: if (len != map2.getLength()) {
0898: return false;
0899: }
0900: for (int i = 0; i < len; i++) {
0901: Node n1 = map1.item(i);
0902: if (n1.getLocalName() == null) { // DOM Level 1 Node
0903: Node n2 = map2.getNamedItem(n1.getNodeName());
0904: if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
0905: return false;
0906: }
0907: } else {
0908: Node n2 = map2.getNamedItemNS(n1.getNamespaceURI(),
0909: n1.getLocalName());
0910: if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
0911: return false;
0912: }
0913: }
0914: }
0915: }
0916: return true;
0917: }
0918:
0919: /**
0920: * DOM Level 3: register the given attribute node as an ID attribute
0921: */
0922: public void setIdAttributeNode(Attr at, boolean makeId) {
0923: if (needsSyncData()) {
0924: synchronizeData();
0925: }
0926: if (ownerDocument.errorChecking) {
0927: if (isReadOnly()) {
0928: String msg = DOMMessageFormatter.formatMessage(
0929: DOMMessageFormatter.DOM_DOMAIN,
0930: "NO_MODIFICATION_ALLOWED_ERR", null);
0931: throw new DOMException(
0932: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0933: }
0934:
0935: if (at.getOwnerElement() != this ) {
0936: String msg = DOMMessageFormatter.formatMessage(
0937: DOMMessageFormatter.DOM_DOMAIN,
0938: "NOT_FOUND_ERR", null);
0939: throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
0940: }
0941: }
0942: ((AttrImpl) at).isIdAttribute(makeId);
0943: if (!makeId) {
0944: ownerDocument.removeIdentifier(at.getValue());
0945: } else {
0946: ownerDocument.putIdentifier(at.getValue(), this );
0947: }
0948: }
0949:
0950: /**
0951: * DOM Level 3: register the given attribute node as an ID attribute
0952: */
0953: public void setIdAttribute(String name, boolean makeId) {
0954: if (needsSyncData()) {
0955: synchronizeData();
0956: }
0957: Attr at = getAttributeNode(name);
0958:
0959: if (at == null) {
0960: String msg = DOMMessageFormatter.formatMessage(
0961: DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR",
0962: null);
0963: throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
0964: }
0965:
0966: if (ownerDocument.errorChecking) {
0967: if (isReadOnly()) {
0968: String msg = DOMMessageFormatter.formatMessage(
0969: DOMMessageFormatter.DOM_DOMAIN,
0970: "NO_MODIFICATION_ALLOWED_ERR", null);
0971: throw new DOMException(
0972: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
0973: }
0974:
0975: if (at.getOwnerElement() != this ) {
0976: String msg = DOMMessageFormatter.formatMessage(
0977: DOMMessageFormatter.DOM_DOMAIN,
0978: "NOT_FOUND_ERR", null);
0979: throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
0980: }
0981: }
0982:
0983: ((AttrImpl) at).isIdAttribute(makeId);
0984: if (!makeId) {
0985: ownerDocument.removeIdentifier(at.getValue());
0986: } else {
0987: ownerDocument.putIdentifier(at.getValue(), this );
0988: }
0989: }
0990:
0991: /**
0992: * DOM Level 3: register the given attribute node as an ID attribute
0993: */
0994: public void setIdAttributeNS(String namespaceURI, String localName,
0995: boolean makeId) {
0996: if (needsSyncData()) {
0997: synchronizeData();
0998: }
0999: Attr at = getAttributeNodeNS(namespaceURI, localName);
1000:
1001: if (at == null) {
1002: String msg = DOMMessageFormatter.formatMessage(
1003: DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR",
1004: null);
1005: throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
1006: }
1007:
1008: if (ownerDocument.errorChecking) {
1009: if (isReadOnly()) {
1010: String msg = DOMMessageFormatter.formatMessage(
1011: DOMMessageFormatter.DOM_DOMAIN,
1012: "NO_MODIFICATION_ALLOWED_ERR", null);
1013: throw new DOMException(
1014: DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
1015: }
1016:
1017: if (at.getOwnerElement() != this ) {
1018: String msg = DOMMessageFormatter.formatMessage(
1019: DOMMessageFormatter.DOM_DOMAIN,
1020: "NOT_FOUND_ERR", null);
1021: throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
1022: }
1023: }
1024: ((AttrImpl) at).isIdAttribute(makeId);
1025: if (!makeId) {
1026: ownerDocument.removeIdentifier(at.getValue());
1027: } else {
1028: ownerDocument.putIdentifier(at.getValue(), this );
1029: }
1030: }
1031:
1032: /**
1033: * @see org.w3c.dom.TypeInfo#getTypeName()
1034: */
1035: public String getTypeName() {
1036: return null;
1037: }
1038:
1039: /**
1040: * @see org.w3c.dom.TypeInfo#getTypeNamespace()
1041: */
1042: public String getTypeNamespace() {
1043: return null;
1044: }
1045:
1046: /**
1047: * Introduced in DOM Level 3. <p>
1048: * Checks if a type is derived from another by restriction. See:
1049: * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom
1050: *
1051: * @param typeNamespaceArg
1052: * The namspace of the ancestor type declaration
1053: * @param typeNameArg
1054: * The name of the ancestor type declaration
1055: * @param derivationMethod
1056: * The derivation method
1057: *
1058: * @return boolean True if the type is derived by restriciton for the
1059: * reference type
1060: */
1061: public boolean isDerivedFrom(String typeNamespaceArg,
1062: String typeNameArg, int derivationMethod) {
1063:
1064: return false;
1065: }
1066:
1067: /**
1068: * Method getSchemaTypeInfo.
1069: * @return TypeInfo
1070: */
1071: public TypeInfo getSchemaTypeInfo() {
1072: if (needsSyncData()) {
1073: synchronizeData();
1074: }
1075: return this ;
1076: }
1077:
1078: //
1079: // Public methods
1080: //
1081:
1082: /**
1083: * NON-DOM: Subclassed to flip the attributes' readonly switch as well.
1084: * @see NodeImpl#setReadOnly
1085: */
1086: public void setReadOnly(boolean readOnly, boolean deep) {
1087: super .setReadOnly(readOnly, deep);
1088: if (attributes != null) {
1089: attributes.setReadOnly(readOnly, true);
1090: }
1091: }
1092:
1093: //
1094: // Protected methods
1095: //
1096:
1097: /** Synchronizes the data (name and value) for fast nodes. */
1098: protected void synchronizeData() {
1099:
1100: // no need to sync in the future
1101: needsSyncData(false);
1102:
1103: // we don't want to generate any event for this so turn them off
1104: boolean orig = ownerDocument.getMutationEvents();
1105: ownerDocument.setMutationEvents(false);
1106:
1107: // attributes
1108: setupDefaultAttributes();
1109:
1110: // set mutation events flag back to its original value
1111: ownerDocument.setMutationEvents(orig);
1112:
1113: } // synchronizeData()
1114:
1115: // support for DOM Level 3 renameNode method
1116: // @param el The element from which to take the attributes
1117: void moveSpecifiedAttributes(ElementImpl el) {
1118: if (needsSyncData()) {
1119: synchronizeData();
1120: }
1121: if (el.hasAttributes()) {
1122: if (attributes == null) {
1123: attributes = new AttributeMap(this , null);
1124: }
1125: attributes.moveSpecifiedAttributes(el.attributes);
1126: }
1127: }
1128:
1129: /** Setup the default attributes. */
1130: protected void setupDefaultAttributes() {
1131: NamedNodeMapImpl defaults = getDefaultAttributes();
1132: if (defaults != null) {
1133: attributes = new AttributeMap(this , defaults);
1134: }
1135: }
1136:
1137: /** Reconcile default attributes. */
1138: protected void reconcileDefaultAttributes() {
1139: if (attributes != null) {
1140: NamedNodeMapImpl defaults = getDefaultAttributes();
1141: attributes.reconcileDefaults(defaults);
1142: }
1143: }
1144:
1145: /** Get the default attributes. */
1146: protected NamedNodeMapImpl getDefaultAttributes() {
1147:
1148: DocumentTypeImpl doctype = (DocumentTypeImpl) ownerDocument
1149: .getDoctype();
1150: if (doctype == null) {
1151: return null;
1152: }
1153: ElementDefinitionImpl eldef = (ElementDefinitionImpl) doctype
1154: .getElements().getNamedItem(getNodeName());
1155: if (eldef == null) {
1156: return null;
1157: }
1158: return (NamedNodeMapImpl) eldef.getAttributes();
1159:
1160: } // getDefaultAttributes()
1161:
1162: } // class ElementImpl
|