/* * Copyright (c) 2002-2008 Gargoyle Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gargoylesoftware.htmlunit.xml; import java.io.PrintWriter; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.map.ListOrderedMap; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.StringEscapeUtils; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.w3c.dom.TypeInfo; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.html.DomAttr; import com.gargoylesoftware.htmlunit.html.DomElement; import com.gargoylesoftware.htmlunit.javascript.NamedNodeMap; /** * An XML element. * * @version $Revision$ * @author Ahmed Ashour * @author Marc Guillemot */ public class XmlElement extends DomElement implements Element { private static final long serialVersionUID = -8119109851558707854L; /** Constant meaning that the specified attribute was not defined. */ public static final String ATTRIBUTE_NOT_DEFINED = new String(""); /** The map holding the namespaces, keyed by URI. */ private Map namespaces_ = new HashMap(); /** The map holding the attributes, keyed by name. */ private Map attributes_; /** * Create an instance of a DOM node that can have a namespace. * * @param namespaceURI the URI that identifies an XML namespace * @param qualifiedName the qualified name of the element type to instantiate * @param page the page that contains this element * @param attributes the attributes of this element */ protected XmlElement(final String namespaceURI, final String qualifiedName, final Page page, final Map attributes) { super(namespaceURI, qualifiedName, page); attributes_ = attributes; for (final DomAttr attr : attributes.values()) { attr.setParentNode(this); } } /** * {@inheritDoc} */ @Override public short getNodeType() { return org.w3c.dom.Node.ELEMENT_NODE; } /** * @return the same value as returned by {@link #getTagName()}, */ @Override public String getNodeName() { return getTagName(); } /** * Returns the tag name of this element. * @return the tag name of this element */ public String getTagName() { if (getNamespaceURI() == null) { return getLocalName(); } return getQualifiedName(); } /** * Returns the value of the specified attribute or an empty string. If the * result is an empty string then it will be {@link #ATTRIBUTE_NOT_DEFINED} * * @param attributeName the name of the attribute * @return the value of the attribute or {@link #ATTRIBUTE_NOT_DEFINED} */ public final String getAttributeValue(final String attributeName) { final DomAttr attr = attributes_.get(attributeName); if (attr != null) { return attr.getNodeValue(); } return ATTRIBUTE_NOT_DEFINED; } /** * Returns the map holding the attributes, keyed by name. * @return the attributes map */ public Map getAttributesMap() { return attributes_; } /** * Sets the value of the attribute specified by name. * * @param attributeName the name of the attribute * @param attributeValue the value of the attribute */ public final void setAttribute(final String attributeName, final String attributeValue) { setAttributeValue(null, attributeName, attributeValue); } /** * Sets the value of the attribute specified by namespace and qualified name. * * @param namespaceURI the URI that identifies an XML namespace * @param qualifiedName the qualified name (prefix:local) of the attribute * @param attributeValue the value of the attribute */ public final void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue) { setAttributeValue(namespaceURI, qualifiedName, attributeValue); } /** * Sets the value of the specified attribute. * * @param attributeName the name of the attribute * @param attributeValue the value of the attribute */ public final void setAttributeValue(final String attributeName, final String attributeValue) { setAttributeValue(null, attributeName, attributeValue); } /** * Sets the value of the specified attribute. * * @param namespaceURI the URI that identifies an XML namespace * @param qualifiedName the qualified name of the attribute * @param attributeValue the value of the attribute */ public final void setAttributeValue(final String namespaceURI, final String qualifiedName, final String attributeValue) { final String value = attributeValue; if (attributes_ == Collections.EMPTY_MAP) { attributes_ = createAttributeMap(1); } final DomAttr newAttr = addAttributeToMap((XmlPage) getPage(), attributes_, namespaceURI, qualifiedName, value); if (namespaceURI != null) { namespaces_.put(namespaceURI, newAttr.getPrefix()); } attributes_.put(newAttr.getName(), newAttr); } /** * Removes an attribute specified by name from this element. * @param attributeName the attribute attributeName */ public final void removeAttribute(final String attributeName) { attributes_.remove(attributeName.toLowerCase()); } /** * Removes an attribute specified by namespace and local name from this element. * @param namespaceURI the URI that identifies an XML namespace * @param localName the name within the namespace */ public final void removeAttributeNS(final String namespaceURI, final String localName) { removeAttribute(getQualifiedName(namespaceURI, localName)); } /** * Returns the qualified name (prefix:local) for the namespace and local name. * * @param namespaceURI the URI that identifies an XML namespace * @param localName the name within the namespace * @return the qualified name or just local name if the namespace is not fully defined */ private String getQualifiedName(final String namespaceURI, final String localName) { final String qualifiedName; if (namespaceURI != null) { final String prefix = namespaces_.get(namespaceURI); if (prefix != null) { qualifiedName = prefix + ':' + localName; } else { qualifiedName = localName; } } else { qualifiedName = localName; } return qualifiedName; } /** * Create an attribute map as needed by HtmlElement. This is just used by the element factories. * @param attributeCount the initial number of attributes to be added to the map * @return the attribute map */ @SuppressWarnings("unchecked") static Map createAttributeMap(final int attributeCount) { return ListOrderedMap.decorate(new HashMap(attributeCount)); // preserve insertion order } /** * Add an attribute to the attribute map. This is just used by the element factories. * @param attributeMap the attribute map where the attribute will be added * @param namespaceURI the URI that identifies an XML namespace * @param qualifiedName the qualified name of the attribute * @param value the value of the attribute */ static DomAttr addAttributeToMap(final XmlPage page, final Map attributeMap, final String namespaceURI, final String qualifiedName, final String value) { final DomAttr newAttr = new DomAttr(page, namespaceURI, qualifiedName, value); attributeMap.put(qualifiedName, newAttr); return newAttr; } /** * {@inheritDoc} */ @Override protected void printXml(final String indent, final PrintWriter printWriter) { final boolean hasChildren = (getFirstChild() != null); printWriter.print(indent + "<"); printOpeningTagContentAsXml(printWriter); if (!hasChildren) { printWriter.println("/>"); } else { printWriter.println(">"); printChildrenAsXml(indent, printWriter); printWriter.println(indent + ""); } } /** * Prints the content between "<" and ">" (or "/>") in the output of the tag name * and its attributes in XML format. * @param printWriter the writer to print in */ protected void printOpeningTagContentAsXml(final PrintWriter printWriter) { printWriter.print(getTagName()); for (final String name : attributes_.keySet()) { printWriter.print(" "); printWriter.print(name); printWriter.print("=\""); printWriter.print(StringEscapeUtils.escapeXml(attributes_.get(name).getNodeValue())); printWriter.print("\""); } } /** * {@inheritDoc} */ @Override public org.w3c.dom.NamedNodeMap getAttributes() { return new NamedNodeMap(this); } /** * {@inheritDoc} */ public String getAttribute(final String name) { final DomAttr attr = attributes_.get(name); if (attr != null) { return attr.getValue(); } return ""; } /** * {@inheritDoc} * Not yet implemented. */ public String getAttributeNS(final String namespaceURI, final String localName) throws DOMException { throw new UnsupportedOperationException("XmlElement.getAttributeNS is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public Attr getAttributeNode(final String name) { throw new UnsupportedOperationException("XmlElement.getAttributeNode is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public Attr getAttributeNodeNS(final String namespaceURI, final String localName) throws DOMException { throw new UnsupportedOperationException("XmlElement.getAttributeNodeNS is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public NodeList getElementsByTagName(final String name) { throw new UnsupportedOperationException("XmlElement.getElementsByTagName is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public NodeList getElementsByTagNameNS(final String namespace, final String name) { throw new UnsupportedOperationException("XmlElement.getElementsByTagNameNS is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public TypeInfo getSchemaTypeInfo() { throw new UnsupportedOperationException("XmlElement.getSchemaTypeInfo is not yet implemented."); } /** * {@inheritDoc} */ public boolean hasAttribute(final String name) { return attributes_.containsKey(name); } /** * {@inheritDoc} * Not yet implemented. */ public boolean hasAttributeNS(final String namespaceURI, final String localName) throws DOMException { throw new UnsupportedOperationException("XmlElement.hasAttributeNS is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public Attr removeAttributeNode(final Attr oldAttr) throws DOMException { throw new UnsupportedOperationException("XmlElement.removeAttributeNode is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public Attr setAttributeNode(final Attr newAttr) throws DOMException { throw new UnsupportedOperationException("XmlElement.setAttributeNode is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public Attr setAttributeNodeNS(final Attr newAttr) throws DOMException { throw new UnsupportedOperationException("XmlElement.setAttributeNodeNS is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public void setIdAttribute(final String name, final boolean isId) throws DOMException { throw new UnsupportedOperationException("XmlElement.setIdAttribute is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public void setIdAttributeNS(final String namespaceURI, final String localName, final boolean isId) throws DOMException { throw new UnsupportedOperationException("XmlElement.setIdAttributeNS is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ public void setIdAttributeNode(final Attr idAttr, final boolean isId) throws DOMException { throw new UnsupportedOperationException("XmlElement.setIdAttributeNode is not yet implemented."); } /** * Just for debug purposes. * {@inheritDoc} */ @Override public String toString() { return ClassUtils.getShortClassName(getClass()) + "[<" + getTagName() + " ...>]"; } /** * {@inheritDoc} */ @Override public boolean hasAttributes() { return !attributes_.isEmpty(); } }