| /* 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., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 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; |
| } |
| |
| } |
| |