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