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 org.w3c.dom.Attr; 41 import org.w3c.dom.DOMException; 42 import org.w3c.dom.Element; 43 import org.w3c.dom.Node; 44 import org.w3c.dom.TypeInfo; 45 import org.w3c.dom.events.MutationEvent; 46 47 48 /** 49 * <p> "Attr" implementation. In DOM, attributes cost quite a lot of 50 * memory because their values are complex structures rather than just 51 * simple strings. To reduce your costs, avoid having more than one 52 * child of an attribute; stick to a single Text node child, and ignore 53 * even that by using the attribute's "nodeValue" property.</p> 54 * 55 * <p> As a bit of general advice, only look at attribute modification 56 * events through the DOMAttrModified event (sent to the associated 57 * element). Implementations are not guaranteed to report other events 58 * in the same order, so you're very likely to write nonportable code if 59 * you monitor events at the "children of Attr" level.</p> 60 * 61 * <p> At this writing, not all attribute modifications will cause the 62 * DOMAttrModified event to be triggered ... only the ones using the string 63 * methods (setNodeValue, setValue, and Element.setAttribute) to modify 64 * those values. That is, if you manipulate those children directly, 65 * elements won't get notified that attribute values have changed. 66 * The natural fix for that will report other modifications, but won't 67 * be able to expose "previous" attribute value; it'll need to be cached 68 * or something (at which point why bother using child nodes). </p> 69 * 70 * <p><em>You are strongly advised not to use "children" of any attribute 71 * nodes you work with.</em> </p> 72 * 73 * @author David Brownell 74 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> 75 */ 76 public class DomAttr 77 extends DomNsNode 78 implements Attr 79 { 80 81 private boolean specified; 82 private String value; // string value cache 83 84 /** 85 * Constructs an Attr node associated with the specified document. 86 * The "specified" flag is initialized to true, since this DOM has 87 * no current "back door" mechanisms to manage default values so 88 * that every value must effectively be "specified". 89 * 90 * <p>This constructor should only be invoked by a Document as part of 91 * its createAttribute functionality, or through a subclass which is 92 * similarly used in a "Sub-DOM" style layer. 93 * 94 * @param owner The document with which this node is associated 95 * @param namespaceURI Combined with the local part of the name, 96 * this is used to uniquely identify a type of attribute 97 * @param name Name of this attribute, which may include a prefix 98 */ DomAttr(DomDocument owner, String namespaceURI, String name)99 protected DomAttr(DomDocument owner, String namespaceURI, String name) 100 { 101 super(ATTRIBUTE_NODE, owner, namespaceURI, name); 102 specified = true; 103 length = 1; 104 105 // XXX register self to get insertion/removal events 106 // and character data change events and when they happen, 107 // report self-mutation 108 } 109 110 /** 111 * <b>DOM L1</b> 112 * Returns the attribute name (same as getNodeName) 113 */ getName()114 public final String getName() 115 { 116 return getNodeName(); 117 } 118 119 /** 120 * <b>DOM L1</b> 121 * Returns true if a parser reported this was in the source text. 122 */ getSpecified()123 public final boolean getSpecified() 124 { 125 return specified; 126 } 127 128 /** 129 * Records whether this attribute was in the source text. 130 */ setSpecified(boolean value)131 public final void setSpecified(boolean value) 132 { 133 specified = value; 134 } 135 136 /** 137 * <b>DOM L1</b> 138 * Returns the attribute value, with character and entity 139 * references substituted. 140 * <em>NOTE: entity refs as children aren't currently handled.</em> 141 */ getNodeValue()142 public String getNodeValue() 143 { 144 // If we have a simple node-value, use that 145 if (first == null) 146 { 147 return (value == null) ? "" : value; 148 } 149 // Otherwise collect child node-values 150 StringBuffer buf = new StringBuffer(); 151 for (DomNode ctx = first; ctx != null; ctx = ctx.next) 152 { 153 switch (ctx.nodeType) 154 { 155 case Node.TEXT_NODE: 156 buf.append(ctx.getNodeValue()); 157 break; 158 case Node.ENTITY_REFERENCE_NODE: 159 // TODO 160 break; 161 } 162 } 163 return buf.toString(); 164 } 165 166 /** 167 * <b>DOM L1</b> 168 * Assigns the value of the attribute; it will have one child, 169 * which is a text node with the specified value (same as 170 * setNodeValue). 171 */ setValue(String value)172 public final void setValue(String value) 173 { 174 setNodeValue(value); 175 } 176 177 /** 178 * <b>DOM L1</b> 179 * Returns the value of the attribute as a non-null string; same 180 * as getNodeValue. 181 * <em>NOTE: entity refs as children aren't currently handled.</em> 182 */ getValue()183 public final String getValue() 184 { 185 return getNodeValue(); 186 } 187 188 /** 189 * <b>DOM L1</b> 190 * Assigns the attribute value; using this API, no entity or 191 * character references will exist. 192 * Causes a DOMAttrModified mutation event to be sent. 193 */ setNodeValue(String value)194 public void setNodeValue(String value) 195 { 196 if (readonly) 197 { 198 throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR); 199 } 200 if (value == null) 201 { 202 value = ""; 203 } 204 String oldValue = getNodeValue(); 205 while (last != null) 206 { 207 removeChild(last); 208 } 209 // don't create a new node just for this... 210 /* 211 Node text = owner.createTextNode(value); 212 appendChild(text); 213 */ 214 this.value = value; 215 length = 1; 216 specified = true; 217 218 mutating(oldValue, value, MutationEvent.MODIFICATION); 219 } 220 getFirstChild()221 public final Node getFirstChild() 222 { 223 // Create a child text node if necessary 224 if (first == null) 225 { 226 length = 0; 227 Node text = owner.createTextNode((value == null) ? "" : value); 228 appendChild(text); 229 } 230 return first; 231 } 232 getLastChild()233 public final Node getLastChild() 234 { 235 // Create a child text node if necessary 236 if (last == null) 237 { 238 length = 0; 239 Node text = owner.createTextNode((value == null) ? "" : value); 240 appendChild(text); 241 } 242 return last; 243 } 244 item(int index)245 public Node item(int index) 246 { 247 // Create a child text node if necessary 248 if (first == null) 249 { 250 length = 0; 251 Node text = owner.createTextNode((value == null) ? "" : value); 252 appendChild(text); 253 } 254 return super.item(index); 255 } 256 257 /** 258 * <b>DOM L2</b> 259 * Returns the element with which this attribute is associated. 260 */ getOwnerElement()261 public final Element getOwnerElement() 262 { 263 return (Element) parent; 264 } 265 getNextSibling()266 public final Node getNextSibling() 267 { 268 return null; 269 } 270 getPreviousSibling()271 public final Node getPreviousSibling() 272 { 273 return null; 274 } 275 getParentNode()276 public Node getParentNode() 277 { 278 return null; 279 } 280 281 /** 282 * Records the element with which this attribute is associated. 283 */ setOwnerElement(Element e)284 public final void setOwnerElement(Element e) 285 { 286 if (parent != null) 287 { 288 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR); 289 } 290 if (!(e instanceof DomElement)) 291 { 292 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR); 293 } 294 parent = (DomElement) e; 295 depth = parent.depth + 1; 296 } 297 298 /** 299 * The base URI of an Attr is always <code>null</code>. 300 */ getBaseURI()301 public final String getBaseURI() 302 { 303 return null; 304 } 305 306 /** 307 * Shallow clone of the attribute, breaking all ties with any 308 * elements. 309 */ clone()310 public Object clone() 311 { 312 DomAttr retval = (DomAttr) super.clone(); 313 retval.specified = true; 314 return retval; 315 } 316 mutating(String oldValue, String newValue, short why)317 private void mutating(String oldValue, String newValue, short why) 318 { 319 if (!reportMutations || parent == null) 320 { 321 return; 322 } 323 324 // EVENT: DOMAttrModified, target = parent, 325 // prev/new values provided, also attr name 326 MutationEvent event; 327 328 event = (MutationEvent) createEvent ("MutationEvents"); 329 event.initMutationEvent ("DOMAttrModified", 330 true /* bubbles */, false /* nocancel */, 331 null, oldValue, newValue, getNodeName (), why); 332 parent.dispatchEvent (event); 333 } 334 335 // DOM Level 3 methods 336 getSchemaTypeInfo()337 public TypeInfo getSchemaTypeInfo() 338 { 339 if (parent != null) 340 { 341 // DTD implementation 342 DomDoctype doctype = (DomDoctype) parent.owner.getDoctype(); 343 if (doctype != null) 344 { 345 return doctype.getAttributeTypeInfo(parent.getNodeName(), 346 getNodeName()); 347 } 348 // TODO XML Schema implementation 349 } 350 return null; 351 } 352 isId()353 public boolean isId() 354 { 355 if (parent != null) 356 { 357 DomDoctype doctype = (DomDoctype) parent.owner.getDoctype(); 358 if (doctype != null) 359 { 360 DTDAttributeTypeInfo info = 361 doctype.getAttributeTypeInfo(parent.getNodeName(), 362 getNodeName()); 363 if (info != null && "ID".equals(info.type)) 364 { 365 return true; 366 } 367 } 368 DomElement element = (DomElement) parent; 369 if (element.userIdAttrs != null && 370 element.userIdAttrs.contains(this)) 371 { 372 return true; 373 } 374 // TODO XML Schema implementation 375 } 376 return false; 377 } 378 379 } 380 381