Frames | No Frames |
1: /* DomAttr.java -- 2: Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package gnu.xml.dom; 39: 40: import gnu.java.lang.CPStringBuilder; 41: 42: import org.w3c.dom.Attr; 43: import org.w3c.dom.DOMException; 44: import org.w3c.dom.Element; 45: import org.w3c.dom.Node; 46: import org.w3c.dom.TypeInfo; 47: import org.w3c.dom.events.MutationEvent; 48: 49: 50: /** 51: * <p> "Attr" implementation. In DOM, attributes cost quite a lot of 52: * memory because their values are complex structures rather than just 53: * simple strings. To reduce your costs, avoid having more than one 54: * child of an attribute; stick to a single Text node child, and ignore 55: * even that by using the attribute's "nodeValue" property.</p> 56: * 57: * <p> As a bit of general advice, only look at attribute modification 58: * events through the DOMAttrModified event (sent to the associated 59: * element). Implementations are not guaranteed to report other events 60: * in the same order, so you're very likely to write nonportable code if 61: * you monitor events at the "children of Attr" level.</p> 62: * 63: * <p> At this writing, not all attribute modifications will cause the 64: * DOMAttrModified event to be triggered ... only the ones using the string 65: * methods (setNodeValue, setValue, and Element.setAttribute) to modify 66: * those values. That is, if you manipulate those children directly, 67: * elements won't get notified that attribute values have changed. 68: * The natural fix for that will report other modifications, but won't 69: * be able to expose "previous" attribute value; it'll need to be cached 70: * or something (at which point why bother using child nodes). </p> 71: * 72: * <p><em>You are strongly advised not to use "children" of any attribute 73: * nodes you work with.</em> </p> 74: * 75: * @author David Brownell 76: * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> 77: */ 78: public class DomAttr 79: extends DomNsNode 80: implements Attr 81: { 82: 83: private boolean specified; 84: private String value; // string value cache 85: 86: /** 87: * Constructs an Attr node associated with the specified document. 88: * The "specified" flag is initialized to true, since this DOM has 89: * no current "back door" mechanisms to manage default values so 90: * that every value must effectively be "specified". 91: * 92: * <p>This constructor should only be invoked by a Document as part of 93: * its createAttribute functionality, or through a subclass which is 94: * similarly used in a "Sub-DOM" style layer. 95: * 96: * @param owner The document with which this node is associated 97: * @param namespaceURI Combined with the local part of the name, 98: * this is used to uniquely identify a type of attribute 99: * @param name Name of this attribute, which may include a prefix 100: */ 101: protected DomAttr(DomDocument owner, String namespaceURI, String name) 102: { 103: super(ATTRIBUTE_NODE, owner, namespaceURI, name); 104: specified = true; 105: length = 1; 106: 107: // XXX register self to get insertion/removal events 108: // and character data change events and when they happen, 109: // report self-mutation 110: } 111: 112: /** 113: * Constructs an Attr node associated with the specified document. 114: * The "specified" flag is initialized to true, since this DOM has 115: * no current "back door" mechanisms to manage default values so 116: * that every value must effectively be "specified". 117: * 118: * <p>This constructor should only be invoked by a Document as part of 119: * its createAttribute functionality, or through a subclass which is 120: * similarly used in a "Sub-DOM" style layer. 121: * <p> 122: * With this constructor, the prefix and local part are given explicitly 123: * rather than being computed. This allows them to be explicitly set to 124: * {@code null} as required by {@link Document#createAttribute(String)}. 125: * </p> 126: * 127: * @param owner The document with which this node is associated 128: * @param namespaceURI Combined with the local part of the name, 129: * this is used to uniquely identify a type of attribute 130: * @param name Name of this attribute, which may include a prefix 131: * @param prefix the namespace prefix of the name. May be {@code null}. 132: * @param localName the local part of the name. May be {@code null}. 133: */ 134: protected DomAttr(DomDocument owner, String namespaceURI, String name, 135: String prefix, String localName) 136: { 137: super(ATTRIBUTE_NODE, owner, namespaceURI, name, prefix, localName); 138: specified = true; 139: length = 1; 140: } 141: 142: /** 143: * <b>DOM L1</b> 144: * Returns the attribute name (same as getNodeName) 145: */ 146: public final String getName() 147: { 148: return getNodeName(); 149: } 150: 151: /** 152: * <b>DOM L1</b> 153: * Returns true if a parser reported this was in the source text. 154: */ 155: public final boolean getSpecified() 156: { 157: return specified; 158: } 159: 160: /** 161: * Records whether this attribute was in the source text. 162: */ 163: public final void setSpecified(boolean value) 164: { 165: specified = value; 166: } 167: 168: /** 169: * <b>DOM L1</b> 170: * Returns the attribute value, with character and entity 171: * references substituted. 172: * <em>NOTE: entity refs as children aren't currently handled.</em> 173: */ 174: public String getNodeValue() 175: { 176: // If we have a simple node-value, use that 177: if (first == null) 178: { 179: return (value == null) ? "" : value; 180: } 181: // Otherwise collect child node-values 182: CPStringBuilder buf = new CPStringBuilder(); 183: for (DomNode ctx = first; ctx != null; ctx = ctx.next) 184: { 185: switch (ctx.nodeType) 186: { 187: case Node.TEXT_NODE: 188: buf.append(ctx.getNodeValue()); 189: break; 190: case Node.ENTITY_REFERENCE_NODE: 191: // TODO 192: break; 193: } 194: } 195: return buf.toString(); 196: } 197: 198: /** 199: * <b>DOM L1</b> 200: * Assigns the value of the attribute; it will have one child, 201: * which is a text node with the specified value (same as 202: * setNodeValue). 203: */ 204: public final void setValue(String value) 205: { 206: setNodeValue(value); 207: } 208: 209: /** 210: * <b>DOM L1</b> 211: * Returns the value of the attribute as a non-null string; same 212: * as getNodeValue. 213: * <em>NOTE: entity refs as children aren't currently handled.</em> 214: */ 215: public final String getValue() 216: { 217: return getNodeValue(); 218: } 219: 220: /** 221: * <b>DOM L1</b> 222: * Assigns the attribute value; using this API, no entity or 223: * character references will exist. 224: * Causes a DOMAttrModified mutation event to be sent. 225: */ 226: public void setNodeValue(String value) 227: { 228: if (readonly) 229: { 230: throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR); 231: } 232: if (value == null) 233: { 234: value = ""; 235: } 236: String oldValue = getNodeValue(); 237: while (last != null) 238: { 239: removeChild(last); 240: } 241: // don't create a new node just for this... 242: /* 243: Node text = owner.createTextNode(value); 244: appendChild(text); 245: */ 246: this.value = value; 247: length = 1; 248: specified = true; 249: 250: mutating(oldValue, value, MutationEvent.MODIFICATION); 251: } 252: 253: public final Node getFirstChild() 254: { 255: // Create a child text node if necessary 256: if (first == null) 257: { 258: length = 0; 259: Node text = owner.createTextNode((value == null) ? "" : value); 260: appendChild(text); 261: } 262: return first; 263: } 264: 265: public final Node getLastChild() 266: { 267: // Create a child text node if necessary 268: if (last == null) 269: { 270: length = 0; 271: Node text = owner.createTextNode((value == null) ? "" : value); 272: appendChild(text); 273: } 274: return last; 275: } 276: 277: public Node item(int index) 278: { 279: // Create a child text node if necessary 280: if (first == null) 281: { 282: length = 0; 283: Node text = owner.createTextNode((value == null) ? "" : value); 284: appendChild(text); 285: } 286: return super.item(index); 287: } 288: 289: /** 290: * <b>DOM L2</b> 291: * Returns the element with which this attribute is associated. 292: */ 293: public final Element getOwnerElement() 294: { 295: return (Element) parent; 296: } 297: 298: public final Node getNextSibling() 299: { 300: return null; 301: } 302: 303: public final Node getPreviousSibling() 304: { 305: return null; 306: } 307: 308: public Node getParentNode() 309: { 310: return null; 311: } 312: 313: /** 314: * Records the element with which this attribute is associated. 315: */ 316: public final void setOwnerElement(Element e) 317: { 318: if (parent != null) 319: { 320: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR); 321: } 322: if (!(e instanceof DomElement)) 323: { 324: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR); 325: } 326: parent = (DomElement) e; 327: depth = parent.depth + 1; 328: } 329: 330: /** 331: * The base URI of an Attr is always <code>null</code>. 332: */ 333: public final String getBaseURI() 334: { 335: return null; 336: } 337: 338: /** 339: * Shallow clone of the attribute, breaking all ties with any 340: * elements. 341: */ 342: public Object clone() 343: { 344: DomAttr retval = (DomAttr) super.clone(); 345: retval.specified = true; 346: return retval; 347: } 348: 349: private void mutating(String oldValue, String newValue, short why) 350: { 351: if (!reportMutations || parent == null || equal(newValue, oldValue)) 352: { 353: return; 354: } 355: 356: // EVENT: DOMAttrModified, target = parent, 357: // prev/new values provided, also attr name 358: MutationEvent event; 359: 360: event = (MutationEvent) createEvent ("MutationEvents"); 361: event.initMutationEvent ("DOMAttrModified", 362: true /* bubbles */, false /* nocancel */, 363: null, oldValue, newValue, getNodeName (), why); 364: parent.dispatchEvent (event); 365: } 366: 367: // DOM Level 3 methods 368: 369: public TypeInfo getSchemaTypeInfo() 370: { 371: if (parent != null) 372: { 373: // DTD implementation 374: DomDoctype doctype = (DomDoctype) parent.owner.getDoctype(); 375: if (doctype != null) 376: { 377: return doctype.getAttributeTypeInfo(parent.getNodeName(), 378: getNodeName()); 379: } 380: // TODO XML Schema implementation 381: } 382: return null; 383: } 384: 385: public boolean isId() 386: { 387: if (parent != null) 388: { 389: DomDoctype doctype = (DomDoctype) parent.owner.getDoctype(); 390: if (doctype != null) 391: { 392: DTDAttributeTypeInfo info = 393: doctype.getAttributeTypeInfo(parent.getNodeName(), 394: getNodeName()); 395: if (info != null && "ID".equals(info.type)) 396: { 397: return true; 398: } 399: } 400: DomElement element = (DomElement) parent; 401: if (element.userIdAttrs != null && 402: element.userIdAttrs.contains(this)) 403: { 404: return true; 405: } 406: // TODO XML Schema implementation 407: } 408: return false; 409: } 410: 411: }