Source for gnu.xml.dom.DomAttr

   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:    */
  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:    */
 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:    */
 123:   public final boolean getSpecified()
 124:   {
 125:     return specified;
 126:   }
 127:   
 128:   /**
 129:    * Records whether this attribute was in the source text.
 130:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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: 
 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: 
 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: 
 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:    */
 261:   public final Element getOwnerElement()
 262:   {
 263:     return (Element) parent;
 264:   }
 265: 
 266:   public final Node getNextSibling()
 267:   {
 268:     return null;
 269:   }
 270: 
 271:   public final Node getPreviousSibling()
 272:   {
 273:     return null;
 274:   }
 275: 
 276:   public Node getParentNode()
 277:   {
 278:     return null;
 279:   }
 280: 
 281:   /**
 282:    * Records the element with which this attribute is associated.
 283:    */
 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:    */
 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:    */
 310:   public Object clone()
 311:   {
 312:     DomAttr retval = (DomAttr) super.clone();
 313:     retval.specified = true;
 314:     return retval;
 315:   }
 316:     
 317:   private void mutating(String oldValue, String newValue, short why)
 318:   {
 319:     if (!reportMutations || parent == null || equal(newValue, oldValue))
 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:   
 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: 
 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: }