Source for gnu.xml.dom.DomNamedNodeMap

   1: /* DomNamedNodeMap.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.DOMException;
  41: import org.w3c.dom.NamedNodeMap;
  42: import org.w3c.dom.Node;
  43: 
  44: /**
  45:  * <p> "NamedNodeMap" implementation. </p>
  46:  * Used mostly to hold element attributes, but sometimes also
  47:  * to list notations or entities.
  48:  *
  49:  * @author David Brownell
  50:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  51:  */
  52: public class DomNamedNodeMap
  53:   implements NamedNodeMap
  54: {
  55: 
  56:   final DomNode owner;
  57:   final short type;
  58: 
  59:   DomNode first;
  60:   int length;
  61:   boolean readonly;
  62: 
  63:   // package private
  64:   DomNamedNodeMap(DomNode owner, short type)
  65:   {
  66:     this.owner = owner;
  67:     this.type = type;
  68:   }
  69: 
  70:   /**
  71:    * Exposes the internal "readonly" flag.  In DOM, all NamedNodeMap
  72:    * objects found in a DocumentType object are read-only (after
  73:    * they are fully constructed), and those holding attributes of
  74:    * a readonly element will also be readonly.
  75:    */
  76:   public final boolean isReadonly()
  77:   {
  78:     return readonly;
  79:   }
  80: 
  81:   /**
  82:    * Sets the internal "readonly" flag so the node and its
  83:    * children can't be changed.
  84:    */
  85:   public void makeReadonly()
  86:   {
  87:     readonly = true;
  88:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
  89:       {
  90:         ctx.makeReadonly();
  91:       }
  92:   }
  93: 
  94:   /**
  95:    * <b>DOM L1</b>
  96:    * Returns the named item from the map, or null; names are just
  97:    * the nodeName property.
  98:    */
  99:   public Node getNamedItem(String name)
 100:   {
 101:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
 102:       {
 103:         if (ctx.getNodeName().equals(name))
 104:           {
 105:             return ctx;
 106:           }
 107:       }
 108:     return null;
 109:   }
 110: 
 111:   /**
 112:    * <b>DOM L2</b>
 113:    * Returns the named item from the map, or null; names are the
 114:    * localName and namespaceURI properties, ignoring any prefix.
 115:    */
 116:   public Node getNamedItemNS(String namespaceURI, String localName)
 117:   {
 118:     if ("".equals(namespaceURI))
 119:       {
 120:         namespaceURI = null;
 121:       }
 122:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
 123:       {
 124:         String name = ctx.getLocalName();
 125:         if ((localName == null && name == null) ||
 126:             (localName != null && localName.equals(name)))
 127:           {
 128:             String uri = ctx.getNamespaceURI();
 129:             if ("".equals(uri))
 130:               {
 131:                 uri = null;
 132:               }
 133:             if ((namespaceURI == null && uri == null) ||
 134:                 (namespaceURI != null && namespaceURI.equals(uri)))
 135:               {
 136:                 return ctx;
 137:               }
 138:           }
 139:       }
 140:     return null;
 141:   }
 142: 
 143:   /**
 144:    * <b>DOM L1</b>
 145:    * Stores the named item into the map, optionally overwriting
 146:    * any existing node with that name.  The name used is just
 147:    * the nodeName attribute.
 148:    */
 149:   public Node setNamedItem(Node arg)
 150:   {
 151:     return setNamedItem(arg, false, false);
 152:   }
 153: 
 154:   /**
 155:    * <b>DOM L2</b>
 156:    * Stores the named item into the map, optionally overwriting
 157:    * any existing node with that fully qualified name.  The name
 158:    * used incorporates the localName and namespaceURI properties,
 159:    * and ignores any prefix.
 160:    */
 161:   public Node setNamedItemNS(Node arg)
 162:   {
 163:     return setNamedItem(arg, true, false);
 164:   }
 165: 
 166:   Node setNamedItem(Node arg, boolean ns, boolean cloning)
 167:   {
 168:     if (readonly)
 169:       {
 170:         throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
 171:       }
 172: 
 173:     DomNode node = (DomNode) arg;
 174:     if (!cloning && node.owner != owner.owner)
 175:       {
 176:         throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR);
 177:       }
 178:     if (node.nodeType != type)
 179:       {
 180:         throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR);
 181:       }
 182:     if (node.nodeType == Node.ATTRIBUTE_NODE)
 183:       {
 184:         DomNode element = node.parent;
 185:         if (element != null && element != owner)
 186:           {
 187:             throw new DomDOMException(DOMException.INUSE_ATTRIBUTE_ERR);
 188:           }
 189:         node.parent = owner;
 190:         node.depth = owner.depth + 1;
 191:       }
 192: 
 193:     String nodeName = node.getNodeName();
 194:     String localName = ns ? node.getLocalName() : null;
 195:     String namespaceURI = ns ? node.getNamespaceURI() : null;
 196:     if ("".equals(namespaceURI))
 197:       {
 198:         namespaceURI = null;
 199:       }
 200: 
 201:     // maybe attribute ADDITION events (?)
 202:     DomNode last = null;
 203:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
 204:       {
 205:         boolean test = false;
 206:         if (ns)
 207:           {
 208:             String tln = ctx.getLocalName();
 209:             if (tln == null)
 210:               {
 211:                 tln = ctx.getNodeName();
 212:               }
 213:             if (tln.equals(localName))
 214:               {
 215:                 String tu = ctx.getNamespaceURI();
 216:                 if ((tu == null && namespaceURI == null) ||
 217:                     (tu != null && tu.equals(namespaceURI)))
 218:                   {
 219:                     test = true;
 220:                   }
 221:               }
 222:           }
 223:         else
 224:           {
 225:             test = ctx.getNodeName().equals(nodeName);
 226:           }
 227:         if (test)
 228:           {
 229:             // replace
 230:             node.previous = ctx.previous;
 231:             node.next = ctx.next;
 232:             if (ctx.previous != null)
 233:               {
 234:                 ctx.previous.next = node;
 235:               }
 236:             if (ctx.next != null)
 237:               {
 238:                 ctx.next.previous = node;
 239:               }
 240:             if (first == ctx)
 241:               {
 242:                 first = node;
 243:               }
 244:             reparent(node, nodeName, ctx.index);
 245:             ctx.parent = null;
 246:             ctx.next = null;
 247:             ctx.previous = null;
 248:             ctx.setDepth(0);
 249:             ctx.index = 0;
 250:             return ctx;
 251:           }
 252:         last = ctx;
 253:       }
 254:     // append
 255:     if (last != null)
 256:       {
 257:         last.next = node;
 258:         node.previous = last;
 259:       }
 260:     else
 261:       {
 262:         first = node;
 263:       }
 264:     length++;
 265:     reparent(node, nodeName, 0);
 266:     return null;
 267:   }
 268: 
 269:   void reparent(DomNode node, String nodeName, int i)
 270:   {
 271:     node.parent = owner;
 272:     node.setDepth(owner.depth + 1);
 273:     // index renumbering
 274:     for (DomNode ctx = node; ctx != null; ctx = ctx.next)
 275:       {
 276:         ctx.index = i++;
 277:       }
 278:     // cache xml:space
 279:     boolean xmlSpace = "xml:space".equals(nodeName);
 280:     if (xmlSpace && owner instanceof DomElement)
 281:       {
 282:         ((DomElement) owner).xmlSpace = node.getNodeValue();
 283:       }
 284:   }
 285: 
 286:   /**
 287:    * <b>DOM L1</b>
 288:    * Removes the named item from the map, or reports an exception;
 289:    * names are just the nodeName property.
 290:    */
 291:   public Node removeNamedItem(String name)
 292:   {
 293:     return removeNamedItem(null, name, false);
 294:   }
 295: 
 296:   /**
 297:    * <b>DOM L2</b>
 298:    * Removes the named item from the map, or reports an exception;
 299:    * names are the localName and namespaceURI properties.
 300:    */
 301:   public Node removeNamedItemNS(String namespaceURI, String localName)
 302:   {
 303:     return removeNamedItem(namespaceURI, localName, true);
 304:   }
 305: 
 306:   Node removeNamedItem(String uri, String name, boolean ns)
 307:   {
 308:     if (readonly)
 309:       {
 310:         throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
 311:       }
 312: 
 313:     // report attribute REMOVAL event?
 314: 
 315:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
 316:       {
 317:         boolean test = false;
 318:         String nodeName = ctx.getNodeName();
 319:         if (ns)
 320:           {
 321:             String tln = ctx.getLocalName();
 322:             if (name != null && name.equals(tln))
 323:               {
 324:                 String tu = ctx.getNamespaceURI();
 325:                 if ((tu == null && uri == null) ||
 326:                     (tu != null && tu.equals(uri)))
 327:                   {
 328:                     test = true;
 329:                   }
 330:               }
 331:           }
 332:         else
 333:           {
 334:             test = nodeName.equals(name);
 335:           }
 336:         if (test)
 337:           {
 338:             // uncache xml:space
 339:             boolean xmlSpace = "xml:space".equals(nodeName);
 340:             if (xmlSpace && owner instanceof DomElement)
 341:               {
 342:                 ((DomElement) owner).xmlSpace = "";
 343:               }
 344:             // is this a default attribute?
 345:             if (ctx.nodeType == Node.ATTRIBUTE_NODE)
 346:               {
 347:                 String def = getDefaultValue(ctx.getNodeName());
 348:                 if (def != null)
 349:                   {
 350:                     ctx.setNodeValue(def);
 351:                     ((DomAttr) ctx).setSpecified(false);
 352:                     return null;
 353:                   }
 354:               }
 355:             // remove
 356:             if (ctx == first)
 357:               {
 358:                 first = ctx.next;
 359:               }
 360:             if (ctx.previous != null)
 361:               {
 362:                 ctx.previous.next = ctx.next;
 363:               }
 364:             if (ctx.next != null)
 365:               {
 366:                 ctx.next.previous = ctx.previous;
 367:               }
 368:             length--;
 369:             ctx.previous = null;
 370:             ctx.next = null;
 371:             ctx.parent = null;
 372:             ctx.setDepth(0);
 373:             ctx.index = 0;
 374:             return ctx;
 375:           }
 376:       }
 377:     throw new DomDOMException(DOMException.NOT_FOUND_ERR);
 378:   }
 379: 
 380:   String getDefaultValue(String name)
 381:   {
 382:     DomDoctype doctype = (DomDoctype) owner.owner.getDoctype();
 383:     if (doctype == null)
 384:       {
 385:         return null;
 386:       }
 387:     DTDAttributeTypeInfo info =
 388:       doctype.getAttributeTypeInfo(owner.getNodeName(), name);
 389:     if (info == null)
 390:       {
 391:         return null;
 392:       }
 393:     return info.value;
 394:   }
 395: 
 396:   /**
 397:    * <b>DOM L1</b>
 398:    * Returns the indexed item from the map, or null.
 399:    */
 400:   public Node item(int index)
 401:   {
 402:     DomNode ctx = first;
 403:     int count = 0;
 404:     while (ctx != null && count < index)
 405:       {
 406:         ctx = ctx.next;
 407:         count++;
 408:       }
 409:     return ctx;
 410:   }
 411: 
 412:   /**
 413:    * <b>DOM L1</b>
 414:    * Returns the length of the map.
 415:    */
 416:   public int getLength()
 417:   {
 418:     return length;
 419:   }
 420: 
 421: }