Source for gnu.xml.dom.DomDocument

   1: /* DomDocument.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 java.util.Iterator;
  41: import javax.xml.XMLConstants;
  42: 
  43: import org.w3c.dom.Attr;
  44: import org.w3c.dom.CDATASection;
  45: import org.w3c.dom.Comment;
  46: import org.w3c.dom.Document;
  47: import org.w3c.dom.DocumentFragment;
  48: import org.w3c.dom.DocumentType;
  49: import org.w3c.dom.DOMConfiguration;
  50: import org.w3c.dom.DOMImplementation;
  51: import org.w3c.dom.DOMException;
  52: import org.w3c.dom.Element;
  53: import org.w3c.dom.Entity;
  54: import org.w3c.dom.EntityReference;
  55: import org.w3c.dom.NamedNodeMap;
  56: import org.w3c.dom.Node;
  57: import org.w3c.dom.Notation;
  58: import org.w3c.dom.ProcessingInstruction;
  59: import org.w3c.dom.Text;
  60: import org.w3c.dom.UserDataHandler;
  61: import org.w3c.dom.traversal.DocumentTraversal;
  62: import org.w3c.dom.traversal.NodeFilter;
  63: import org.w3c.dom.traversal.NodeIterator;
  64: import org.w3c.dom.traversal.TreeWalker;
  65: import org.w3c.dom.xpath.XPathEvaluator;
  66: import org.w3c.dom.xpath.XPathException;
  67: import org.w3c.dom.xpath.XPathExpression;
  68: import org.w3c.dom.xpath.XPathNSResolver;
  69: 
  70: /**
  71:  * <p> "Document" and "DocumentTraversal" implementation.
  72:  *
  73:  * <p> Note that when this checks names for legality, it uses an
  74:  * approximation of the XML rules, not the real ones.  Specifically,
  75:  * it uses Unicode rules, with sufficient tweaks to pass a majority
  76:  * of basic XML conformance tests.  (The huge XML character tables are
  77:  * hairy to implement.)
  78:  *
  79:  * @author David Brownell 
  80:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  81:  */
  82: public class DomDocument
  83:   extends DomNode
  84:   implements Document, DocumentTraversal, XPathEvaluator
  85: {
  86: 
  87:   private final DOMImplementation implementation;
  88:   private boolean checkingCharacters = true;
  89:   boolean checkingWellformedness = true;
  90: 
  91:   boolean building; // if true, skip mutation events in the tree
  92:   
  93:   DomDocumentConfiguration config;
  94: 
  95:   String inputEncoding;
  96:   String encoding;
  97:   String version = "1.0";
  98:   boolean standalone;
  99:   String systemId;
 100:   
 101:   /**
 102:    * Constructs a Document node, associating it with an instance
 103:    * of the DomImpl class.
 104:    *
 105:    * <p> Note that this constructor disables character checking.
 106:    * It is normally used when connecting a DOM to an XML parser,
 107:    * and duplicating such checks is undesirable.  When used for
 108:    * purposes other than connecting to a parser, you should
 109:    * re-enable that checking.
 110:    *
 111:    * @see #setCheckingCharacters
 112:    */
 113:   public DomDocument()
 114:   {
 115:     this(new DomImpl());
 116:   }
 117:   
 118:   /**
 119:    * Constructs a Document node, associating it with the specified
 120:    * implementation.  This should only be used in conjunction with
 121:    * a specialized implementation; it will normally be called by
 122:    * that implementation.
 123:    *
 124:    * @see DomImpl
 125:    * @see #setCheckingCharacters
 126:    */
 127:   protected DomDocument(DOMImplementation impl)
 128:   {
 129:     super(DOCUMENT_NODE, null);
 130:     implementation = impl;
 131:   }
 132: 
 133:   /**
 134:    * Sets the <code>building</code> flag.
 135:    * Mutation events in the document are not reported.
 136:    */
 137:   public void setBuilding(boolean flag)
 138:   {
 139:     building = flag;
 140:   }
 141: 
 142:   /**
 143:    * Sets whether to check for document well-formedness.
 144:    * If true, an exception will be raised if a second doctype or root
 145:    * element node is added to the document.
 146:    */
 147:   public void setCheckWellformedness(boolean flag)
 148:   {
 149:     checkingWellformedness = flag;
 150:   }
 151: 
 152:   /**
 153:    * Sets whether to check for document characters.
 154:    */
 155:   public void setCheckingCharacters(boolean flag)
 156:   {
 157:     checkingCharacters = flag;
 158:   }  
 159:   
 160:   /**
 161:    * <b>DOM L1</b>
 162:    * Returns the constant "#document".
 163:    */
 164:   final public String getNodeName()
 165:   {
 166:     return "#document";
 167:   }
 168: 
 169:   /**
 170:    * <b>DOM L1</b>
 171:    * Returns the document's root element, or null.
 172:    */
 173:   final public Element getDocumentElement()
 174:   {
 175:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
 176:       {
 177:         if (ctx.nodeType == ELEMENT_NODE)
 178:           {
 179:             return (Element) ctx;
 180:           }
 181:       }
 182:     return null;
 183:   }
 184: 
 185:   /**
 186:    * <b>DOM L1</b>
 187:    * Returns the document's DocumentType, or null.
 188:    */
 189:   final public DocumentType getDoctype()
 190:   {
 191:     for (DomNode ctx = first; ctx != null; ctx = ctx.next)
 192:       {
 193:       if (ctx.nodeType == DOCUMENT_TYPE_NODE)
 194:           {
 195:             return (DocumentType) ctx;
 196:           }
 197:       }
 198:     return null;
 199:   }
 200: 
 201:   /**
 202:    * <b>DOM L1</b>
 203:    * Returns the document's DOMImplementation.
 204:    */
 205:   final public DOMImplementation getImplementation()
 206:   {
 207:     return implementation;
 208:   }
 209: 
 210:   /**
 211:    * <b>DOM L1 (relocated in DOM L2)</b>
 212:    * Returns the element with the specified "ID" attribute, or null.
 213:    *
 214:    * <p>Returns null unless {@link Consumer} was used to populate internal
 215:    * DTD declaration information, using package-private APIs.  If that
 216:    * internal DTD information is available, the document may be searched for
 217:    * the element with that ID.
 218:    */
 219:   public Element getElementById(String id)
 220:   {
 221:     if (id == null || id.length() == 0)
 222:       {
 223:         return null;
 224:       }
 225:     DomDoctype doctype = (DomDoctype) getDoctype();
 226:     if (doctype != null && !doctype.hasIds())
 227:       {
 228:         doctype = null;
 229:       }
 230:     
 231:     // yes, this is linear in size of document.
 232:     // it'd be easy enough to maintain a hashtable.
 233:     Node current = getDocumentElement();
 234:     Node temp;
 235:     
 236:     if (current == null)
 237:       {
 238:         return null;
 239:       }
 240:     while (current != this)
 241:       {
 242:         // done?
 243:         if (current.getNodeType() == ELEMENT_NODE)
 244:           {
 245:             DomElement element = (DomElement) current;
 246:             if (element.userIdAttrs != null)
 247:               {
 248:                 for (Iterator i = element.userIdAttrs.iterator();
 249:                      i.hasNext(); )
 250:                   {
 251:                     Node idAttr = (Node) i.next();
 252:                     if (id.equals(idAttr.getNodeValue()))
 253:                       {
 254:                         return element;
 255:                       }
 256:                   }
 257:               }
 258:             if (doctype != null)
 259:               {
 260:                 DTDElementTypeInfo info =
 261:                   doctype.getElementTypeInfo(current.getNodeName());
 262:                 if (info != null &&
 263:                     id.equals(element.getAttribute(info.idAttrName)))
 264:                   {
 265:                     return element;
 266:                   }
 267:               }
 268:             // xml:id
 269:             String xmlId = element.getAttribute("xml:id");
 270:             if (xmlId == null)
 271:               {
 272:                 xmlId = element.getAttributeNS(XMLConstants.XML_NS_URI,
 273:                                                "id");
 274:               }
 275:             if (id.equals(xmlId))
 276:               {
 277:                 return element;
 278:               }
 279:           }
 280:         
 281:         // descend?
 282:         if (current.hasChildNodes())
 283:           {
 284:             current = current.getFirstChild();
 285:             continue;
 286:           }
 287:         
 288:         // lateral?
 289:         temp = current.getNextSibling();
 290:         if (temp != null)
 291:           {
 292:             current = temp;
 293:             continue;
 294:           }
 295:         
 296:         // back up ... 
 297:         do
 298:           {
 299:             temp = current.getParentNode();
 300:             if (temp == null)
 301:               {
 302:                 return null;
 303:               }
 304:             current = temp;
 305:             temp = current.getNextSibling();
 306:           }
 307:         while (temp == null);
 308:         current = temp;
 309:       }
 310:     return null;
 311:   }
 312: 
 313:   private void checkNewChild(Node newChild)
 314:   {
 315:     if (newChild.getNodeType() == ELEMENT_NODE
 316:         && getDocumentElement() != null)
 317:       {
 318:         throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
 319:                                   "document element already present: " +
 320:                                   getDocumentElement(), newChild, 0);
 321:       }
 322:     if (newChild.getNodeType() == DOCUMENT_TYPE_NODE
 323:         && getDoctype() != null)
 324:       {
 325:         throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
 326:                                   "document type already present: " +
 327:                                   getDoctype(), newChild, 0);
 328:       }
 329:   }
 330: 
 331:   /**
 332:    * <b>DOM L1</b>
 333:    * Appends the specified node to this node's list of children,
 334:    * enforcing the constraints that there be only one root element
 335:    * and one document type child.
 336:    */
 337:   public Node appendChild(Node newChild)
 338:   {
 339:     if (checkingWellformedness)
 340:       {
 341:         checkNewChild(newChild);
 342:       }
 343:     return super.appendChild(newChild);
 344:   }
 345: 
 346:   /**
 347:    * <b>DOM L1</b>
 348:    * Inserts the specified node in this node's list of children,
 349:    * enforcing the constraints that there be only one root element
 350:    * and one document type child.
 351:    */
 352:   public Node insertBefore(Node newChild, Node refChild)
 353:   {
 354:     if (checkingWellformedness)
 355:       {
 356:         checkNewChild(newChild);
 357:       }
 358:     return super.insertBefore(newChild, refChild);
 359:   }
 360: 
 361:   /**
 362:    * <b>DOM L1</b>
 363:    * Replaces the specified node in this node's list of children,
 364:    * enforcing the constraints that there be only one root element
 365:    * and one document type child.
 366:    */
 367:   public Node replaceChild(Node newChild, Node refChild)
 368:   {
 369:     if (checkingWellformedness &&
 370:         ((newChild.getNodeType() == ELEMENT_NODE &&
 371:           refChild.getNodeType() != ELEMENT_NODE) ||
 372:          (newChild.getNodeType() == DOCUMENT_TYPE_NODE &&
 373:           refChild.getNodeType() != DOCUMENT_TYPE_NODE)))
 374:       {
 375:         checkNewChild(newChild);
 376:       }
 377:     return super.replaceChild(newChild, refChild);
 378:   }
 379:  
 380:   // NOTE:  DOM can't really tell when the name of an entity,
 381:   // notation, or PI must follow the namespace rules (excluding
 382:   // colons) instead of the XML rules (which allow them without
 383:   // much restriction).  That's an API issue.  verifyXmlName
 384:   // aims to enforce the XML rules, not the namespace rules.
 385:   
 386:   /**
 387:    * Throws a DOM exception if the specified name is not a legal XML 1.0
 388:    * Name.
 389:    * @deprecated This method is deprecated and may be removed in future
 390:    * versions of GNU JAXP
 391:    */
 392:   public static void verifyXmlName(String name)
 393:   {
 394:     // XXX why is this public?
 395:     checkName(name, false);
 396:   }
 397: 
 398:   static void checkName(String name, boolean xml11)
 399:   {
 400:     if (name == null)
 401:       {
 402:         throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
 403:       }
 404:     int len = name.length();
 405:     if (len == 0)
 406:       {
 407:         throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
 408:       }
 409: 
 410:     // dog: rewritten to use the rules for XML 1.0 and 1.1
 411:     
 412:     // Name start character
 413:     char c = name.charAt(0);
 414:     if (xml11)
 415:       {
 416:         // XML 1.1
 417:         if ((c < 0x0041 || c > 0x005a) &&
 418:             (c < 0x0061 || c > 0x007a) &&
 419:             c != ':' && c != '_' &&
 420:             (c < 0x00c0 || c > 0x00d6) &&
 421:             (c < 0x00d8 || c > 0x00f6) &&
 422:             (c < 0x00f8 || c > 0x02ff) &&
 423:             (c < 0x0370 || c > 0x037d) &&
 424:             (c < 0x037f || c > 0x1fff) &&
 425:             (c < 0x200c || c > 0x200d) &&
 426:             (c < 0x2070 || c > 0x218f) &&
 427:             (c < 0x2c00 || c > 0x2fef) &&
 428:             (c < 0x3001 || c > 0xd7ff) &&
 429:             (c < 0xf900 || c > 0xfdcf) &&
 430:             (c < 0xfdf0 || c > 0xfffd) &&
 431:             (c < 0x10000 || c > 0xeffff))
 432:           {
 433:             throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
 434:                                       name, null, c);
 435:           }
 436:       }
 437:     else
 438:       {
 439:         // XML 1.0
 440:         int type = Character.getType(c);
 441:         switch (type)
 442:           {
 443:           case Character.LOWERCASE_LETTER: // Ll
 444:           case Character.UPPERCASE_LETTER: // Lu
 445:           case Character.OTHER_LETTER: // Lo
 446:           case Character.TITLECASE_LETTER: // Lt
 447:           case Character.LETTER_NUMBER: // Nl
 448:             if ((c > 0xf900 && c < 0xfffe) ||
 449:                 (c >= 0x20dd && c <= 0x20e0))
 450:               {
 451:                 // Compatibility area and Unicode 2.0 exclusions
 452:                 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
 453:                                           name, null, c);
 454:               }
 455:             break;
 456:           default:
 457:             if (c != ':' && c != '_' && (c < 0x02bb || c > 0x02c1) &&
 458:                 c != 0x0559 && c != 0x06e5 && c != 0x06e6)
 459:               {
 460:                 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
 461:                                           name, null, c);
 462:               }
 463:           }
 464:       }
 465: 
 466:     // Subsequent characters
 467:     for (int i = 1; i < len; i++)
 468:       {
 469:         c = name.charAt(i);
 470:         if (xml11)
 471:           {
 472:             // XML 1.1
 473:             if ((c < 0x0041 || c > 0x005a) &&
 474:                 (c < 0x0061 || c > 0x007a) &&
 475:                 (c < 0x0030 || c > 0x0039) &&
 476:                 c != ':' && c != '_' && c != '-' && c != '.' &&
 477:                 (c < 0x00c0 || c > 0x00d6) &&
 478:                 (c < 0x00d8 || c > 0x00f6) &&
 479:                 (c < 0x00f8 || c > 0x02ff) &&
 480:                 (c < 0x0370 || c > 0x037d) &&
 481:                 (c < 0x037f || c > 0x1fff) &&
 482:                 (c < 0x200c || c > 0x200d) &&
 483:                 (c < 0x2070 || c > 0x218f) &&
 484:                 (c < 0x2c00 || c > 0x2fef) &&
 485:                 (c < 0x3001 || c > 0xd7ff) &&
 486:                 (c < 0xf900 || c > 0xfdcf) &&
 487:                 (c < 0xfdf0 || c > 0xfffd) &&
 488:                 (c < 0x10000 || c > 0xeffff) &&
 489:                 c != 0x00b7 &&
 490:                 (c < 0x0300 || c > 0x036f) &&
 491:                 (c < 0x203f || c > 0x2040))
 492:               {
 493:                 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR, name,
 494:                                           null, c);
 495:               }
 496:           }
 497:         else
 498:           {
 499:             // XML 1.0
 500:             int type = Character.getType(c);
 501:             switch (type)
 502:               {
 503:               case Character.LOWERCASE_LETTER: // Ll
 504:               case Character.UPPERCASE_LETTER: // Lu
 505:               case Character.DECIMAL_DIGIT_NUMBER: // Nd
 506:               case Character.OTHER_LETTER: // Lo
 507:               case Character.TITLECASE_LETTER: // Lt
 508:               case Character.LETTER_NUMBER: // Nl
 509:               case Character.COMBINING_SPACING_MARK: // Mc
 510:               case Character.ENCLOSING_MARK: // Me
 511:               case Character.NON_SPACING_MARK: // Mn
 512:               case Character.MODIFIER_LETTER: // Lm
 513:                 if ((c > 0xf900 && c < 0xfffe) ||
 514:                     (c >= 0x20dd && c <= 0x20e0))
 515:                   {
 516:                     // Compatibility area and Unicode 2.0 exclusions
 517:                     throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
 518:                                               name, null, c);
 519:                   }
 520:                 break;
 521:               default:
 522:                 if (c != '-' && c != '.' && c != ':' && c != '_' &&
 523:                     c != 0x0387 && (c < 0x02bb || c > 0x02c1) &&
 524:                     c != 0x0559 && c != 0x06e5 && c != 0x06e6 && c != 0x00b7)
 525:                   {
 526:                     throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
 527:                                               name, null, c);
 528:                   }
 529:               }
 530:           }
 531:       }
 532: 
 533:     // FIXME characters with a font or compatibility decomposition (i.e.
 534:     // those with a "compatibility formatting tag" in field 5 of the
 535:     // database -- marked by field 5 beginning with a "<") are not allowed.
 536:   }
 537: 
 538:   // package private
 539:   static void checkNCName(String name, boolean xml11)
 540:   {
 541:     checkName(name, xml11);
 542:     int len = name.length();
 543:     int index = name.indexOf(':');
 544:     if (index != -1)
 545:       {
 546:         if (index == 0 || index == (len - 1) || name.lastIndexOf(':') != index)
 547:           {
 548:             throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
 549:           }
 550:       }
 551:   }
 552: 
 553:   // package private
 554:   static void checkChar(String value, boolean xml11)
 555:   {
 556:     char[] chars = value.toCharArray();
 557:     checkChar(chars, 0, chars.length, xml11);
 558:   }
 559:   
 560:   static void checkChar(char[] buf, int off, int len, boolean xml11)
 561:   {
 562:     for (int i = 0; i < len; i++)
 563:       {
 564:         char c = buf[i];
 565:         
 566:         // assume surrogate pairing checks out OK, for simplicity
 567:         if ((c >= 0x0020 && c <= 0xd7ff) ||
 568:             (c == 0x000a || c == 0x000d || c == 0x0009) ||
 569:             (c >= 0xe000 && c <= 0xfffd) ||
 570:             (c >= 0x10000 && c <= 0x10ffff))
 571:           {
 572:             continue;
 573:           }
 574:         if (xml11)
 575:           {
 576:             if ((c >= 0x0001 && c <= 0x001f) ||
 577:                 (c >= 0x007f && c <= 0x0084) ||
 578:                 (c >= 0x0086 && c <= 0x009f))
 579:               {
 580:                 continue;
 581:               }
 582:           }
 583:         throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
 584:                                   new String(buf, off, len), null, c);
 585:       }
 586:   }
 587: 
 588:   /**
 589:    * <b>DOM L1</b>
 590:    * Returns a newly created element with the specified name.
 591:    */
 592:   public Element createElement(String name)
 593:   {
 594:     Element element;
 595:     
 596:     if (checkingCharacters)
 597:       {
 598:         checkName(name, "1.1".equals(version));
 599:       }
 600:     if (name.startsWith("xml:"))
 601:       {
 602:         element = createElementNS(null, name);
 603:       }
 604:     else
 605:       {
 606:         DomElement domElement = new DomElement(this, null, name);
 607:         domElement.localName = null;
 608:         element = domElement;
 609:       }
 610:     defaultAttributes(element, name);
 611:     return element;
 612:   }
 613: 
 614:   /**
 615:    * <b>DOM L2</b>
 616:    * Returns a newly created element with the specified name
 617:    * and namespace information.
 618:    */
 619:   public Element createElementNS(String namespaceURI, String name)
 620:   {
 621:     if (checkingCharacters)
 622:       {
 623:         checkNCName(name, "1.1".equals(version));
 624:       }
 625:     
 626:     if ("".equals(namespaceURI))
 627:       {
 628:         namespaceURI = null;
 629:       }
 630:     if (name.startsWith("xml:"))
 631:       {
 632:         if (namespaceURI != null
 633:             && !XMLConstants.XML_NS_URI.equals(namespaceURI))
 634:           {
 635:             throw new DomDOMException(DOMException.NAMESPACE_ERR,
 636:                                       "xml namespace is always " +
 637:                                       XMLConstants.XML_NS_URI, this, 0);
 638:           }
 639:         namespaceURI = XMLConstants.XML_NS_URI;
 640:       }
 641:     else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
 642:              name.startsWith("xmlns:"))
 643:       {
 644:         throw new DomDOMException(DOMException.NAMESPACE_ERR,
 645:                                   "xmlns is reserved", this, 0);
 646:       }
 647:     else if (namespaceURI == null && name.indexOf(':') != -1)
 648:       {
 649:         throw new DomDOMException(DOMException.NAMESPACE_ERR,
 650:                                   "prefixed name '" + name +
 651:                                   "' needs a URI", this, 0);
 652:       }
 653:     
 654:     Element  element = new DomElement(this, namespaceURI, name);
 655:     defaultAttributes(element, name);
 656:     return element;
 657:   }
 658:   
 659:   private void defaultAttributes(Element element, String name)
 660:   {
 661:     DomDoctype doctype = (DomDoctype) getDoctype();
 662:     if (doctype == null)
 663:       {
 664:         return;
 665:       }
 666: 
 667:     // default any attributes that need it
 668:     DTDElementTypeInfo info = doctype.getElementTypeInfo(name);
 669:     if (info != null)
 670:       {
 671:         for (Iterator i = info.attributes(); i != null && i.hasNext(); )
 672:           {
 673:             DTDAttributeTypeInfo attr = (DTDAttributeTypeInfo) i.next();
 674:             DomAttr node = (DomAttr) createAttribute(attr.name);
 675:             
 676:             String value = attr.value;
 677:             if (value == null)
 678:               {
 679:                 value = "";
 680:               }
 681:             node.setValue(value);
 682:             node.setSpecified(false);
 683:             element.setAttributeNode(node);
 684:           }
 685:       }
 686:   }
 687: 
 688:   /**
 689:    * <b>DOM L1</b>
 690:    * Returns a newly created document fragment.
 691:    */
 692:   public DocumentFragment createDocumentFragment()
 693:   {
 694:     return new DomDocumentFragment(this);
 695:   }
 696: 
 697:   /**
 698:    * <b>DOM L1</b>
 699:    * Returns a newly created text node with the specified value.
 700:    */
 701:   public Text createTextNode(String value)
 702:   {
 703:     if (checkingCharacters)
 704:       {
 705:         checkChar(value, "1.1".equals(version));
 706:       }
 707:     return new DomText(this, value);
 708:   }
 709: 
 710:   /**
 711:    * Returns a newly created text node with the specified value.
 712:    */
 713:   public Text createTextNode(char[] buf, int off, int len)
 714:   {
 715:     if (checkingCharacters)
 716:       {
 717:         checkChar(buf, off, len, "1.1".equals(version));
 718:       }
 719:     return new DomText(this, buf, off, len);
 720:   }
 721: 
 722:   /**
 723:    * <b>DOM L1</b>
 724:    * Returns a newly created comment node with the specified value.
 725:    */
 726:   public Comment createComment(String value)
 727:   {
 728:     if (checkingCharacters)
 729:       {
 730:         checkChar(value, "1.1".equals(version));
 731:       }
 732:     return new DomComment(this, value);
 733:   }
 734: 
 735:   /**
 736:    * <b>DOM L1</b>
 737:    * Returns a newly created CDATA section node with the specified value.
 738:    */
 739:   public CDATASection createCDATASection(String value)
 740:   {
 741:     if (checkingCharacters)
 742:       {
 743:         checkChar(value, "1.1".equals(version));
 744:       }
 745:     return new DomCDATASection(this, value);
 746:   }
 747: 
 748:   /**
 749:    * Returns a newly created CDATA section node with the specified value.
 750:    */
 751:   public CDATASection createCDATASection(char[] buf, int off, int len)
 752:   {
 753:     if (checkingCharacters)
 754:       {
 755:         checkChar(buf, off, len, "1.1".equals(version));
 756:       }
 757:     return new DomCDATASection(this, buf, off, len);
 758:   }
 759: 
 760:   /**
 761:    * <b>DOM L1</b>
 762:    * Returns a newly created processing instruction.
 763:    */
 764:   public ProcessingInstruction createProcessingInstruction(String target,
 765:                                                            String data)
 766:   {
 767:     if (checkingCharacters)
 768:       {
 769:         boolean xml11 = "1.1".equals(version);
 770:         checkName(target, xml11);
 771:         if ("xml".equalsIgnoreCase(target))
 772:           {
 773:             throw new DomDOMException(DOMException.SYNTAX_ERR,
 774:                                       "illegal PI target name",
 775:                                       this, 0);
 776:           }
 777:         checkChar(data, xml11);
 778:       }
 779:     return new DomProcessingInstruction(this, target, data);
 780:   }
 781: 
 782:   /**
 783:    * <b>DOM L1</b>
 784:    * Returns a newly created attribute with the specified name.
 785:    */
 786:   public Attr createAttribute(String name)
 787:   {
 788:     if (checkingCharacters)
 789:       {
 790:         checkName(name, "1.1".equals(version));
 791:       }
 792:     if (name.startsWith("xml:"))
 793:       {
 794:         return createAttributeNS(XMLConstants.XML_NS_URI, name);
 795:       }
 796:     else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
 797:              name.startsWith("xmlns:"))
 798:       {
 799:         return createAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, name);
 800:       }
 801:     else
 802:       {
 803:         DomAttr ret = new DomAttr(this, null, name);
 804:         ret.localName = null;
 805:         return ret;
 806:       }
 807:   }
 808: 
 809:   /**
 810:    * <b>DOM L2</b>
 811:    * Returns a newly created attribute with the specified name
 812:    * and namespace information.
 813:    */
 814:   public Attr createAttributeNS(String namespaceURI, String name)
 815:   {
 816:     if (checkingCharacters)
 817:       {
 818:         checkNCName(name, "1.1".equals(version));
 819:       }
 820:     
 821:     if ("".equals(namespaceURI))
 822:       {
 823:         namespaceURI = null;
 824:       }
 825:     if (name.startsWith ("xml:"))
 826:       {
 827:         if (namespaceURI == null)
 828:           {
 829:             namespaceURI = XMLConstants.XML_NS_URI;
 830:           }
 831:         else if (!XMLConstants.XML_NS_URI.equals(namespaceURI))
 832:           {
 833:             throw new DomDOMException(DOMException.NAMESPACE_ERR,
 834:                                       "xml namespace is always " +
 835:                                       XMLConstants.XML_NS_URI,
 836:                                       this, 0);
 837:           }
 838:       }
 839:     else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
 840:              name.startsWith("xmlns:"))
 841:       {
 842:         if (namespaceURI == null)
 843:           {
 844:             namespaceURI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
 845:           }
 846:         else if (!XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
 847:           {
 848:             throw new DomDOMException(DOMException.NAMESPACE_ERR,
 849:                                       "xmlns namespace must be " +
 850:                                       XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
 851:                                       this, 0);
 852:           }
 853:       }
 854:     else if (namespaceURI == null && name.indexOf(':') != -1)
 855:       {
 856:         throw new DomDOMException(DOMException.NAMESPACE_ERR,
 857:                         "prefixed name needs a URI: " + name, this, 0);
 858:       }
 859:     return new DomAttr(this, namespaceURI, name);
 860:   }
 861:   
 862:   /**
 863:    * <b>DOM L1</b>
 864:    * Returns a newly created reference to the specified entity.
 865:    * The caller should populate this with the appropriate children
 866:    * and then mark it as readonly.
 867:    *
 868:    * @see DomNode#makeReadonly
 869:    */
 870:   public EntityReference createEntityReference(String name)
 871:   {
 872:     DomEntityReference ret = new DomEntityReference(this, name);
 873:     DocumentType doctype = getDoctype();
 874:     if (doctype != null)
 875:       {
 876:         DomEntity ent = (DomEntity) doctype.getEntities().getNamedItem(name);
 877:         if (ent != null)
 878:           {
 879:             for (DomNode ctx = ent.first; ctx != null; ctx = ctx.next)
 880:               {
 881:                 ret.appendChild(ctx.cloneNode(true));
 882:               }
 883:           }
 884:       }
 885:     ret.makeReadonly();
 886:     return ret;
 887:   }
 888: 
 889:   /**
 890:    * <b>DOM L2</b>
 891:    * Makes a copy of the specified node, with all nodes "owned" by
 892:    * this document and with children optionally copied.  This type
 893:    * of standard utility has become, well, a standard utility.
 894:    *
 895:    * <p> Note that EntityReference nodes created through this method (either
 896:    * directly, or recursively) never have children, and that there is no
 897:    * portable way to associate them with such children.
 898:    *
 899:    * <p> Note also that there is no requirement that the specified node
 900:    * be associated with a different document.  This differs from the
 901:    * <em>cloneNode</em> operation in that the node itself is not given
 902:    * an opportunity to participate, so that any information managed
 903:    * by node subclasses will be lost.
 904:    */
 905:   public Node importNode(Node src, boolean deep)
 906:   {
 907:     Node dst = null;
 908:     switch (src.getNodeType())
 909:       {
 910:       case TEXT_NODE:
 911:         dst = createTextNode(src.getNodeValue());
 912:         break;
 913:       case CDATA_SECTION_NODE:
 914:         dst = createCDATASection(src.getNodeValue());
 915:         break;
 916:       case COMMENT_NODE:
 917:         dst = createComment(src.getNodeValue());
 918:         break;
 919:       case PROCESSING_INSTRUCTION_NODE:
 920:         dst = createProcessingInstruction(src.getNodeName(),
 921:                                           src.getNodeValue());
 922:         break;
 923:       case NOTATION_NODE:
 924:         // NOTE:  There's no standard way to create
 925:         // these, or add them to a doctype.  Useless.
 926:         Notation notation = (Notation) src;
 927:         dst = new DomNotation(this, notation.getNodeName(),
 928:                               notation.getPublicId(),
 929:                               notation.getSystemId());
 930:         break;
 931:       case ENTITY_NODE:
 932:         // NOTE:  There's no standard way to create
 933:         // these, or add them to a doctype.  Useless.
 934:         Entity entity = (Entity) src;
 935:         dst = new DomEntity(this, entity.getNodeName(),
 936:                             entity.getPublicId(),
 937:                             entity.getSystemId(),
 938:                             entity.getNotationName());
 939:         if (deep)
 940:           {
 941:             for (Node ctx = src.getFirstChild(); ctx != null;
 942:                  ctx = ctx.getNextSibling())
 943:               {
 944:                 dst.appendChild(importNode(ctx, deep));
 945:               }
 946:           }
 947:         break;
 948:       case ENTITY_REFERENCE_NODE:
 949:         dst = createEntityReference(src.getNodeName());
 950:         break;
 951:       case DOCUMENT_FRAGMENT_NODE:
 952:         dst = new DomDocumentFragment(this);
 953:         if (deep)
 954:           {
 955:             for (Node ctx = src.getFirstChild(); ctx != null;
 956:                  ctx = ctx.getNextSibling())
 957:               {
 958:                 dst.appendChild(importNode(ctx, deep));
 959:               }
 960:           }
 961:         break;
 962:       case ATTRIBUTE_NODE:
 963:         String attr_nsuri = src.getNamespaceURI();
 964:         if (attr_nsuri != null)
 965:           {
 966:             dst = createAttributeNS(attr_nsuri, src.getNodeName());
 967:           }
 968:         else
 969:           {
 970:             dst = createAttribute(src.getNodeName());
 971:           }
 972:         // this is _always_ done regardless of "deep" setting
 973:         for (Node ctx = src.getFirstChild(); ctx != null;
 974:              ctx = ctx.getNextSibling())
 975:           {
 976:             dst.appendChild(importNode(ctx, false));
 977:           }
 978:         break;
 979:       case ELEMENT_NODE:
 980:         String elem_nsuri = src.getNamespaceURI();
 981:         if (elem_nsuri != null)
 982:           {
 983:             dst = createElementNS(elem_nsuri, src.getNodeName());
 984:           }
 985:         else
 986:           {
 987:             dst = createElement(src.getNodeName());
 988:           }
 989:         NamedNodeMap srcAttrs = src.getAttributes();
 990:         NamedNodeMap dstAttrs = dst.getAttributes();
 991:         int len = srcAttrs.getLength();
 992:         for (int i = 0; i < len; i++)
 993:           {
 994:             Attr a = (Attr) srcAttrs.item(i);
 995:             Attr dflt;
 996:             
 997:             // maybe update defaulted attributes
 998:             dflt = (Attr) dstAttrs.getNamedItem(a.getNodeName());
 999:             if (dflt != null)
1000:               {
1001:                 String newval = a.getNodeValue();
1002:                 if (!dflt.getNodeValue().equals(newval)
1003:                     || a.getSpecified () == true)
1004:                   {
1005:                     dflt.setNodeValue (newval);
1006:                   }
1007:                 continue;
1008:               }
1009:             
1010:             dstAttrs.setNamedItem((Attr) importNode(a, false));
1011:           }
1012:         if (deep)
1013:           {
1014:             for (Node ctx = src.getFirstChild(); ctx != null;
1015:                  ctx = ctx.getNextSibling())
1016:               {
1017:                 dst.appendChild(importNode(ctx, true));
1018:               }
1019:           }
1020:         break;
1021:         // can't import document or doctype nodes
1022:       case DOCUMENT_NODE:
1023:       case DOCUMENT_TYPE_NODE:
1024:         // FALLTHROUGH
1025:         // can't import unrecognized or nonstandard nodes
1026:       default:
1027:         throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR, null, src, 0);
1028:       }
1029:     
1030:     // FIXME cleanup a bit -- for deep copies, copy those
1031:     // children in one place, here (code sharing is healthy)
1032: 
1033:     if (src instanceof DomNode)
1034:       {
1035:         ((DomNode) src).notifyUserDataHandlers(UserDataHandler.NODE_IMPORTED,
1036:                                                src, dst);
1037:       }
1038:     return dst;
1039:   }
1040: 
1041:   /**
1042:    * <b>DOM L2 (Traversal)</b>
1043:    * Returns a newly created node iterator.  Don't forget to detach
1044:    * this iterator when you're done using it!
1045:    *
1046:    * @see DomIterator
1047:    */
1048:   public NodeIterator createNodeIterator(Node root,
1049:                                          int whatToShow,
1050:                                          NodeFilter filter,
1051:                                          boolean expandEntities)
1052:   {
1053:     return new DomNodeIterator(root, whatToShow, filter, expandEntities,
1054:                                false);
1055:   }
1056: 
1057:   public TreeWalker createTreeWalker(Node root,
1058:                                      int whatToShow,
1059:                                      NodeFilter filter,
1060:                                      boolean expandEntities)
1061:   {
1062:     return new DomNodeIterator(root, whatToShow, filter, expandEntities,
1063:                                true);
1064:   }
1065: 
1066:   // DOM Level 3 methods
1067:   
1068:   /**
1069:    * DOM L3
1070:    */
1071:   public String getInputEncoding()
1072:   {
1073:     return inputEncoding;
1074:   }
1075: 
1076:   public void setInputEncoding(String inputEncoding)
1077:   {
1078:     this.inputEncoding = inputEncoding;
1079:   }
1080:   
1081:   /**
1082:    * DOM L3
1083:    */
1084:   public String getXmlEncoding()
1085:   {
1086:     return encoding;
1087:   }
1088:   
1089:   public void setXmlEncoding(String encoding)
1090:   {
1091:     this.encoding = encoding;
1092:   }
1093:   
1094:   public boolean getXmlStandalone()
1095:   {
1096:     return standalone;
1097:   }
1098: 
1099:   public void setXmlStandalone(boolean xmlStandalone)
1100:   {
1101:     standalone = xmlStandalone;
1102:   }
1103: 
1104:   public String getXmlVersion()
1105:   {
1106:     return version;
1107:   }
1108: 
1109:   public void setXmlVersion(String xmlVersion)
1110:   {
1111:     if (xmlVersion == null)
1112:       {
1113:         xmlVersion = "1.0";
1114:       }
1115:     if ("1.0".equals(xmlVersion) ||
1116:         "1.1".equals(xmlVersion))
1117:       {
1118:         version = xmlVersion;
1119:       }
1120:     else
1121:       {
1122:         throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
1123:       }
1124:   }
1125: 
1126:   public boolean getStrictErrorChecking()
1127:   {
1128:     return checkingCharacters;
1129:   }
1130: 
1131:   public void setStrictErrorChecking(boolean strictErrorChecking)
1132:   {
1133:     checkingCharacters = strictErrorChecking;
1134:   }
1135: 
1136:   public String lookupPrefix(String namespaceURI)
1137:   {
1138:     Node root = getDocumentElement();
1139:     return (root == null) ? null : root.lookupPrefix(namespaceURI);
1140:   }
1141: 
1142:   public boolean isDefaultNamespace(String namespaceURI)
1143:   {
1144:     Node root = getDocumentElement();
1145:     return (root == null) ? false : root.isDefaultNamespace(namespaceURI);
1146:   }
1147: 
1148:   public String lookupNamespaceURI(String prefix)
1149:   {
1150:     Node root = getDocumentElement();
1151:     return (root == null) ? null : root.lookupNamespaceURI(prefix);
1152:   }
1153: 
1154:   public String getBaseURI()
1155:   {
1156:     return getDocumentURI();
1157:     /*
1158:     Node root = getDocumentElement();
1159:     if (root != null)
1160:       {
1161:         NamedNodeMap attrs = root.getAttributes();
1162:         Node xmlBase = attrs.getNamedItemNS(XMLConstants.XML_NS_URI, "base");
1163:         if (xmlBase != null)
1164:           {
1165:             return xmlBase.getNodeValue();
1166:           }
1167:       }
1168:     return systemId;
1169:     */
1170:   }
1171:   
1172:   public String getDocumentURI()
1173:   {
1174:     return systemId;
1175:   }
1176: 
1177:   public void setDocumentURI(String documentURI)
1178:   {
1179:     systemId = documentURI;
1180:   }
1181: 
1182:   public Node adoptNode(Node source)
1183:   {
1184:     int sourceNodeType = source.getNodeType();
1185:     switch (sourceNodeType)
1186:       {
1187:       case DOCUMENT_NODE:
1188:       case DOCUMENT_TYPE_NODE:
1189:         throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
1190:       case ENTITY_NODE:
1191:       case NOTATION_NODE:
1192:         throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
1193:       }
1194:     if (source instanceof DomNode)
1195:       {
1196:         // GNU native
1197:         DomNode src = (DomNode) source;
1198:         DomNode dst = src;
1199:         if (dst.parent != null)
1200:           {
1201:             dst = (DomNode) dst.cloneNode(true);
1202:           }
1203:         dst.setOwner(this);
1204:         src.notifyUserDataHandlers(UserDataHandler.NODE_ADOPTED, src, dst);
1205:         return dst;
1206:       }
1207:     else
1208:       {
1209:         // Some other implementation
1210:         Node dst = null;
1211:         switch (sourceNodeType)
1212:           {
1213:           case Node.ATTRIBUTE_NODE:
1214:               {
1215:                 Attr src = (Attr) source;
1216:                 String nodeName = src.getNodeName();
1217:                 String localName = src.getLocalName();
1218:                 String namespaceUri = src.getNamespaceURI();
1219:                 dst = (localName == null) ?
1220:                   createAttribute(nodeName) :
1221:                   createAttributeNS(namespaceUri, nodeName);
1222:                 adoptChildren(src, dst);
1223:                 break;
1224:               }
1225:           case Node.CDATA_SECTION_NODE:
1226:               {
1227:                 CDATASection src = (CDATASection) source;
1228:                 dst = createCDATASection(src.getData());
1229:                 break;
1230:               }
1231:           case Node.COMMENT_NODE:
1232:               {
1233:                 Comment src = (Comment) source;
1234:                 dst = createComment(src.getData());
1235:                 break;
1236:               }
1237:           case Node.DOCUMENT_FRAGMENT_NODE:
1238:               {
1239:                 DocumentFragment src = (DocumentFragment) source;
1240:                 dst = createDocumentFragment();
1241:                 adoptChildren(src, dst);
1242:                 break;
1243:               }
1244:           case Node.ELEMENT_NODE:
1245:               {
1246:                 Element src = (Element) source;
1247:                 String nodeName = src.getNodeName();
1248:                 String localName = src.getLocalName();
1249:                 String namespaceUri = src.getNamespaceURI();
1250:                 dst = (localName == null) ?
1251:                   createElement(nodeName) :
1252:                   createElementNS(namespaceUri, nodeName);
1253:                 adoptAttributes(src, dst);
1254:                 adoptChildren(src, dst);
1255:                 break;
1256:               }
1257:           case Node.ENTITY_REFERENCE_NODE:
1258:               {
1259:                 EntityReference src = (EntityReference) source;
1260:                 dst = createEntityReference(src.getNodeName());
1261:                 adoptChildren(src, dst);
1262:                 break;
1263:               }
1264:           case Node.PROCESSING_INSTRUCTION_NODE:
1265:               {
1266:                 ProcessingInstruction src = (ProcessingInstruction) source;
1267:                 dst = createProcessingInstruction(src.getTarget(),
1268:                                                   src.getData());
1269:                 break;
1270:               }
1271:           case Node.TEXT_NODE:
1272:               {
1273:                 Text src = (Text) source;
1274:                 dst = createTextNode(src.getData());
1275:                 break;
1276:               }
1277:           }
1278:         return dst;
1279:       }
1280:   }
1281: 
1282:   void adoptChildren(Node src, Node dst)
1283:   {
1284:     Node node = src.getFirstChild();
1285:     while (node != null)
1286:       {
1287:         Node next = node.getNextSibling();
1288:         dst.appendChild(adoptNode(node));
1289:         node = next;
1290:       }
1291:   }
1292: 
1293:   void adoptAttributes(Node src, Node dst)
1294:   {
1295:     NamedNodeMap srcAttrs = src.getAttributes();
1296:     NamedNodeMap dstAttrs = dst.getAttributes();
1297:     int len = srcAttrs.getLength();
1298:     for (int i = 0; i < len; i++)
1299:       {
1300:         Node node = srcAttrs.item(i);
1301:         String localName = node.getLocalName();
1302:         if (localName == null)
1303:           {
1304:             dstAttrs.setNamedItem(adoptNode(node));
1305:           }
1306:         else
1307:           {
1308:             dstAttrs.setNamedItemNS(adoptNode(node));
1309:           }
1310:       }
1311:   }
1312: 
1313:   public DOMConfiguration getDomConfig()
1314:   {
1315:     if (config == null)
1316:       {
1317:         config = new DomDocumentConfiguration();
1318:       }
1319:     return config;
1320:   }
1321: 
1322:   public boolean isEqualNode(Node arg)
1323:   {
1324:     if (!super.isEqualNode(arg))
1325:       return false;
1326:     Document d = (Document) arg;
1327:     String dversion = d.getXmlVersion();
1328:     if (dversion == null || !dversion.equals(version))
1329:       return false;
1330:     boolean dstandalone = d.getXmlStandalone();
1331:     if (dstandalone != standalone)
1332:       return false;
1333:     String dencoding = d.getXmlEncoding();
1334:     if (dencoding == null || dencoding.equalsIgnoreCase("UTF-8"))
1335:       {
1336:         if (encoding != null && !encoding.equalsIgnoreCase("UTF-8"))
1337:           return false;
1338:       }
1339:     else
1340:       {
1341:         if (!dencoding.equals(encoding))
1342:           return false;
1343:       }
1344:     return true;
1345:   }
1346: 
1347:   public void normalizeDocument()
1348:   {
1349:     boolean save = building;
1350:     building = true;
1351:     normalizeNode(this);
1352:     building = save;
1353:   }
1354: 
1355:   void normalizeNode(DomNode node)
1356:   {
1357:     node.normalize();
1358:     if (config != null)
1359:       {
1360:         switch (node.nodeType)
1361:           {
1362:           case CDATA_SECTION_NODE:
1363:             if (!config.cdataSections)
1364:               {
1365:                 // replace CDATA section with text node
1366:                 Text text = createTextNode(node.getNodeValue());
1367:                 node.parent.insertBefore(text, node);
1368:                 node.parent.removeChild(node);
1369:                 // merge adjacent text nodes
1370:                 String data = text.getWholeText();
1371:                 node = (DomNode) text.replaceWholeText(data);
1372:               }
1373:             else if (config.splitCdataSections)
1374:               {
1375:                 String value = node.getNodeValue();
1376:                 int i = value.indexOf("]]>");
1377:                 while (i != -1)
1378:                   {
1379:                     Node node2 = createCDATASection(value.substring(0, i));
1380:                     node.parent.insertBefore(node2, node);
1381:                     value = value.substring(i + 3);
1382:                     node.setNodeValue(value);
1383:                     i = value.indexOf("]]>");
1384:                   }
1385:               }
1386:             break;
1387:           case COMMENT_NODE:
1388:             if (!config.comments)
1389:               {
1390:                 node.parent.removeChild(node);
1391:               }
1392:             break;
1393:           case TEXT_NODE:
1394:             if (!config.elementContentWhitespace &&
1395:                 ((Text) node).isElementContentWhitespace())
1396:               {
1397:                 node.parent.removeChild(node);
1398:               }
1399:             break;
1400:           case ENTITY_REFERENCE_NODE:
1401:             if (!config.entities)
1402:               {
1403:                 for (DomNode ctx = node.first; ctx != null; )
1404:                   {
1405:                     DomNode ctxNext = ctx.next;
1406:                     node.parent.insertBefore(ctx, node);
1407:                     ctx = ctxNext;
1408:                   }
1409:                 node.parent.removeChild(node);
1410:               }
1411:             break;
1412:           case ELEMENT_NODE:
1413:             if (!config.namespaceDeclarations)
1414:               {
1415:                 DomNamedNodeMap attrs =
1416:                   (DomNamedNodeMap) node.getAttributes();
1417:                 boolean aro = attrs.readonly;
1418:                 attrs.readonly = false; // Ensure we can delete if necessary
1419:                 int len = attrs.getLength();
1420:                 for (int i = 0; i < len; i++)
1421:                   {
1422:                     Node attr = attrs.item(i);
1423:                     String namespace = attr.getNamespaceURI();
1424:                     if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespace))
1425:                       {
1426:                         attrs.removeNamedItemNS(namespace,
1427:                                                 attr.getLocalName());
1428:                         i--;
1429:                         len--;
1430:                       }
1431:                   }
1432:                 attrs.readonly = aro;
1433:               }
1434:             break;
1435:           }
1436:       }
1437:     for (DomNode ctx = node.first; ctx != null; )
1438:       {
1439:         DomNode ctxNext = ctx.next;
1440:         normalizeNode(ctx);
1441:         ctx = ctxNext;
1442:       }
1443:   }
1444:   
1445:   public Node renameNode(Node n, String namespaceURI, String qualifiedName)
1446:     throws DOMException
1447:   {
1448:     if (n instanceof DomNsNode)
1449:       {
1450:         DomNsNode src = (DomNsNode) n;
1451:         if (src == null)
1452:           {
1453:             throw new DomDOMException(DOMException.NOT_FOUND_ERR);
1454:           }
1455:         if (src.owner != this)
1456:           {
1457:             throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
1458:                                       null, src, 0);
1459:           }
1460:         boolean xml11 = "1.1".equals(version);
1461:         checkName(qualifiedName, xml11);
1462:         int ci = qualifiedName.indexOf(':');
1463:         if ("".equals(namespaceURI))
1464:           {
1465:             namespaceURI = null;
1466:           }
1467:         if (namespaceURI != null)
1468:           {
1469:             checkNCName(qualifiedName, xml11);
1470:             String prefix = (ci == -1) ? "" :
1471:               qualifiedName.substring(0, ci);
1472:             if (XMLConstants.XML_NS_PREFIX.equals(prefix) &&
1473:                 !XMLConstants.XML_NS_URI.equals(namespaceURI))
1474:               {
1475:                 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1476:                                 "xml namespace must be " +
1477:                                 XMLConstants.XML_NS_URI, src, 0);
1478:               }
1479:             else if (src.nodeType == ATTRIBUTE_NODE &&
1480:                      (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
1481:                       XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)) &&
1482:                      !XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
1483:               {
1484:                 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1485:                                 "xmlns namespace must be " +
1486:                                 XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
1487:               }
1488:             if (XMLConstants.XML_NS_URI.equals(namespaceURI) &&
1489:                 !XMLConstants.XML_NS_PREFIX.equals(prefix))
1490:               {
1491:                 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1492:                                 "xml namespace must be " +
1493:                                 XMLConstants.XML_NS_URI, src, 0);
1494:               }
1495:             else if (src.nodeType == ATTRIBUTE_NODE &&
1496:                      XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI) &&
1497:                      !(XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
1498:                        XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)))
1499:               {
1500:                 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1501:                                 "xmlns namespace must be " +
1502:                                 XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
1503:               }
1504:                 
1505:           }
1506:         src.setNodeName(qualifiedName);
1507:         src.setNamespaceURI(namespaceURI);
1508:         src.notifyUserDataHandlers(UserDataHandler.NODE_RENAMED, src, src);
1509:         // TODO MutationNameEvents
1510:         // DOMElementNameChanged or DOMAttributeNameChanged
1511:         return src;
1512:       }
1513:     throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR, null, n, 0);
1514:   }
1515: 
1516:   // -- XPathEvaluator --
1517:   
1518:   public XPathExpression createExpression(String expression,
1519:                                           XPathNSResolver resolver)
1520:     throws XPathException, DOMException
1521:   {
1522:     return new DomXPathExpression(this, expression, resolver);
1523:   }
1524:   
1525:   public XPathNSResolver createNSResolver(Node nodeResolver)
1526:   {
1527:     return new DomXPathNSResolver(nodeResolver);
1528:   }
1529:     
1530:   public Object evaluate(String expression,
1531:                          Node contextNode,
1532:                          XPathNSResolver resolver,
1533:                          short type,
1534:                          Object result)
1535:     throws XPathException, DOMException
1536:   {
1537:     XPathExpression xpe =
1538:       new DomXPathExpression(this, expression, resolver);
1539:     return xpe.evaluate(contextNode, type, result);
1540:   }
1541: 
1542: }