| /* DomAttr.java -- | |
| Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.xml.dom; | |
| import org.w3c.dom.Attr; | |
| import org.w3c.dom.DOMException; | |
| import org.w3c.dom.Element; | |
| import org.w3c.dom.Node; | |
| import org.w3c.dom.TypeInfo; | |
| import org.w3c.dom.events.MutationEvent; | |
| /** | |
| * <p> "Attr" implementation. In DOM, attributes cost quite a lot of | |
| * memory because their values are complex structures rather than just | |
| * simple strings. To reduce your costs, avoid having more than one | |
| * child of an attribute; stick to a single Text node child, and ignore | |
| * even that by using the attribute's "nodeValue" property.</p> | |
| * | |
| * <p> As a bit of general advice, only look at attribute modification | |
| * events through the DOMAttrModified event (sent to the associated | |
| * element). Implementations are not guaranteed to report other events | |
| * in the same order, so you're very likely to write nonportable code if | |
| * you monitor events at the "children of Attr" level.</p> | |
| * | |
| * <p> At this writing, not all attribute modifications will cause the | |
| * DOMAttrModified event to be triggered ... only the ones using the string | |
| * methods (setNodeValue, setValue, and Element.setAttribute) to modify | |
| * those values. That is, if you manipulate those children directly, | |
| * elements won't get notified that attribute values have changed. | |
| * The natural fix for that will report other modifications, but won't | |
| * be able to expose "previous" attribute value; it'll need to be cached | |
| * or something (at which point why bother using child nodes). </p> | |
| * | |
| * <p><em>You are strongly advised not to use "children" of any attribute | |
| * nodes you work with.</em> </p> | |
| * | |
| * @author David Brownell | |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> | |
| */ | |
| public class DomAttr | |
| extends DomNsNode | |
| implements Attr | |
| { | |
| private boolean specified; | |
| private String value; // string value cache | |
| /** | |
| * Constructs an Attr node associated with the specified document. | |
| * The "specified" flag is initialized to true, since this DOM has | |
| * no current "back door" mechanisms to manage default values so | |
| * that every value must effectively be "specified". | |
| * | |
| * <p>This constructor should only be invoked by a Document as part of | |
| * its createAttribute functionality, or through a subclass which is | |
| * similarly used in a "Sub-DOM" style layer. | |
| * | |
| * @param owner The document with which this node is associated | |
| * @param namespaceURI Combined with the local part of the name, | |
| * this is used to uniquely identify a type of attribute | |
| * @param name Name of this attribute, which may include a prefix | |
| */ | |
| protected DomAttr(DomDocument owner, String namespaceURI, String name) | |
| { | |
| super(ATTRIBUTE_NODE, owner, namespaceURI, name); | |
| specified = true; | |
| length = 1; | |
| // XXX register self to get insertion/removal events | |
| // and character data change events and when they happen, | |
| // report self-mutation | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns the attribute name (same as getNodeName) | |
| */ | |
| public final String getName() | |
| { | |
| return getNodeName(); | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns true if a parser reported this was in the source text. | |
| */ | |
| public final boolean getSpecified() | |
| { | |
| return specified; | |
| } | |
| /** | |
| * Records whether this attribute was in the source text. | |
| */ | |
| public final void setSpecified(boolean value) | |
| { | |
| specified = value; | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns the attribute value, with character and entity | |
| * references substituted. | |
| * <em>NOTE: entity refs as children aren't currently handled.</em> | |
| */ | |
| public String getNodeValue() | |
| { | |
| // If we have a simple node-value, use that | |
| if (first == null) | |
| { | |
| return (value == null) ? "" : value; | |
| } | |
| // Otherwise collect child node-values | |
| StringBuffer buf = new StringBuffer(); | |
| for (DomNode ctx = first; ctx != null; ctx = ctx.next) | |
| { | |
| switch (ctx.nodeType) | |
| { | |
| case Node.TEXT_NODE: | |
| buf.append(ctx.getNodeValue()); | |
| break; | |
| case Node.ENTITY_REFERENCE_NODE: | |
| // TODO | |
| break; | |
| } | |
| } | |
| return buf.toString(); | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Assigns the value of the attribute; it will have one child, | |
| * which is a text node with the specified value (same as | |
| * setNodeValue). | |
| */ | |
| public final void setValue(String value) | |
| { | |
| setNodeValue(value); | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns the value of the attribute as a non-null string; same | |
| * as getNodeValue. | |
| * <em>NOTE: entity refs as children aren't currently handled.</em> | |
| */ | |
| public final String getValue() | |
| { | |
| return getNodeValue(); | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Assigns the attribute value; using this API, no entity or | |
| * character references will exist. | |
| * Causes a DOMAttrModified mutation event to be sent. | |
| */ | |
| public void setNodeValue(String value) | |
| { | |
| if (readonly) | |
| { | |
| throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR); | |
| } | |
| if (value == null) | |
| { | |
| value = ""; | |
| } | |
| String oldValue = getNodeValue(); | |
| while (last != null) | |
| { | |
| removeChild(last); | |
| } | |
| // don't create a new node just for this... | |
| /* | |
| Node text = owner.createTextNode(value); | |
| appendChild(text); | |
| */ | |
| this.value = value; | |
| length = 1; | |
| specified = true; | |
| mutating(oldValue, value, MutationEvent.MODIFICATION); | |
| } | |
| public final Node getFirstChild() | |
| { | |
| // Create a child text node if necessary | |
| if (first == null) | |
| { | |
| length = 0; | |
| Node text = owner.createTextNode((value == null) ? "" : value); | |
| appendChild(text); | |
| } | |
| return first; | |
| } | |
| public final Node getLastChild() | |
| { | |
| // Create a child text node if necessary | |
| if (last == null) | |
| { | |
| length = 0; | |
| Node text = owner.createTextNode((value == null) ? "" : value); | |
| appendChild(text); | |
| } | |
| return last; | |
| } | |
| public Node item(int index) | |
| { | |
| // Create a child text node if necessary | |
| if (first == null) | |
| { | |
| length = 0; | |
| Node text = owner.createTextNode((value == null) ? "" : value); | |
| appendChild(text); | |
| } | |
| return super.item(index); | |
| } | |
| /** | |
| * <b>DOM L2</b> | |
| * Returns the element with which this attribute is associated. | |
| */ | |
| public final Element getOwnerElement() | |
| { | |
| return (Element) parent; | |
| } | |
| public final Node getNextSibling() | |
| { | |
| return null; | |
| } | |
| public final Node getPreviousSibling() | |
| { | |
| return null; | |
| } | |
| public Node getParentNode() | |
| { | |
| return null; | |
| } | |
| /** | |
| * Records the element with which this attribute is associated. | |
| */ | |
| public final void setOwnerElement(Element e) | |
| { | |
| if (parent != null) | |
| { | |
| throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR); | |
| } | |
| if (!(e instanceof DomElement)) | |
| { | |
| throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR); | |
| } | |
| parent = (DomElement) e; | |
| depth = parent.depth + 1; | |
| } | |
| /** | |
| * The base URI of an Attr is always <code>null</code>. | |
| */ | |
| public final String getBaseURI() | |
| { | |
| return null; | |
| } | |
| /** | |
| * Shallow clone of the attribute, breaking all ties with any | |
| * elements. | |
| */ | |
| public Object clone() | |
| { | |
| DomAttr retval = (DomAttr) super.clone(); | |
| retval.specified = true; | |
| return retval; | |
| } | |
| private void mutating(String oldValue, String newValue, short why) | |
| { | |
| if (!reportMutations || parent == null) | |
| { | |
| return; | |
| } | |
| // EVENT: DOMAttrModified, target = parent, | |
| // prev/new values provided, also attr name | |
| MutationEvent event; | |
| event = (MutationEvent) createEvent ("MutationEvents"); | |
| event.initMutationEvent ("DOMAttrModified", | |
| true /* bubbles */, false /* nocancel */, | |
| null, oldValue, newValue, getNodeName (), why); | |
| parent.dispatchEvent (event); | |
| } | |
| // DOM Level 3 methods | |
| public TypeInfo getSchemaTypeInfo() | |
| { | |
| if (parent != null) | |
| { | |
| // DTD implementation | |
| DomDoctype doctype = (DomDoctype) parent.owner.getDoctype(); | |
| if (doctype != null) | |
| { | |
| return doctype.getAttributeTypeInfo(parent.getNodeName(), | |
| getNodeName()); | |
| } | |
| // TODO XML Schema implementation | |
| } | |
| return null; | |
| } | |
| public boolean isId() | |
| { | |
| if (parent != null) | |
| { | |
| DomDoctype doctype = (DomDoctype) parent.owner.getDoctype(); | |
| if (doctype != null) | |
| { | |
| DTDAttributeTypeInfo info = | |
| doctype.getAttributeTypeInfo(parent.getNodeName(), | |
| getNodeName()); | |
| if (info != null && "ID".equals(info.type)) | |
| { | |
| return true; | |
| } | |
| } | |
| DomElement element = (DomElement) parent; | |
| if (element.userIdAttrs != null && | |
| element.userIdAttrs.contains(this)) | |
| { | |
| return true; | |
| } | |
| // TODO XML Schema implementation | |
| } | |
| return false; | |
| } | |
| } | |