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