1 /*
2 * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements. See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 package com.sun.org.apache.xerces.internal.dom;
21
22 import com.sun.org.apache.xerces.internal.impl.Constants;
23 import com.sun.org.apache.xerces.internal.util.URI;
24 import com.sun.org.apache.xerces.internal.util.XML11Char;
25 import com.sun.org.apache.xerces.internal.util.XMLChar;
26 import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
27 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.ObjectStreamField;
32 import java.lang.reflect.Constructor;
33 import java.util.HashMap;
34 import java.util.Hashtable;
35 import java.util.Map;
36 import jdk.xml.internal.SecuritySupport;
37 import org.w3c.dom.Attr;
38 import org.w3c.dom.CDATASection;
39 import org.w3c.dom.Comment;
40 import org.w3c.dom.DOMConfiguration;
41 import org.w3c.dom.DOMException;
42 import org.w3c.dom.DOMImplementation;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.DocumentFragment;
45 import org.w3c.dom.DocumentType;
46 import org.w3c.dom.Element;
47 import org.w3c.dom.Entity;
48 import org.w3c.dom.EntityReference;
49 import org.w3c.dom.NamedNodeMap;
50 import org.w3c.dom.Node;
51 import org.w3c.dom.NodeList;
52 import org.w3c.dom.Notation;
53 import org.w3c.dom.ProcessingInstruction;
54 import org.w3c.dom.Text;
55 import org.w3c.dom.UserDataHandler;
56 import org.w3c.dom.events.Event;
57 import org.w3c.dom.events.EventListener;
58 import org.w3c.dom.ls.DOMImplementationLS;
59 import org.w3c.dom.ls.LSSerializer;
60
61 /**
62 * The Document interface represents the entire HTML or XML document.
63 * Conceptually, it is the root of the document tree, and provides the
64 * primary access to the document's data.
65 * <P>
66 * Since elements, text nodes, comments, processing instructions,
67 * etc. cannot exist outside the context of a Document, the Document
68 * interface also contains the factory methods needed to create these
69 * objects. The Node objects created have a ownerDocument attribute
70 * which associates them with the Document within whose context they
71 * were created.
72 * <p>
73 * The CoreDocumentImpl class only implements the DOM Core. Additional modules
74 * are supported by the more complete DocumentImpl subclass.
75 * <p>
76 * <b>Note:</b> When any node in the document is serialized, the
77 * entire document is serialized along with it.
78 *
79 * @xerces.internal
80 *
81 * @author Arnaud Le Hors, IBM
82 * @author Joe Kesselman, IBM
83 * @author Andy Clark, IBM
84 * @author Ralf Pfeiffer, IBM
85 * @since PR-DOM-Level-1-19980818.
86 * @LastModified: Nov 2017
87 */
88 public class CoreDocumentImpl
89 extends ParentNode implements Document {
90
91 /**
92 * TODO:: 1. Change XML11Char method names similar to XMLChar. That will
93 * prevent lot of dirty version checking code.
94 *
95 * 2. IMO during cloneNode qname/isXMLName check should not be made.
96 */
97 //
98 // Constants
99 //
100
101 /** Serialization version. */
102 static final long serialVersionUID = 0;
103
104 //
105 // Data
106 //
107
108 // document information
109
110 /** Document type. */
111 protected DocumentTypeImpl docType;
112
113 /** Document element. */
114 protected ElementImpl docElement;
115
116 /** NodeListCache free list */
117 transient NodeListCache fFreeNLCache;
118
119 /**Experimental DOM Level 3 feature: Document encoding */
120 protected String encoding;
121
122 /**Experimental DOM Level 3 feature: Document actualEncoding */
123 protected String actualEncoding;
124
125 /**Experimental DOM Level 3 feature: Document version */
126 protected String version;
127
128 /**Experimental DOM Level 3 feature: Document standalone */
129 protected boolean standalone;
130
131 /**Experimental DOM Level 3 feature: documentURI */
132 protected String fDocumentURI;
133
134 //Revisit :: change to a better data structure.
135 /** Table for user data attached to this document nodes. */
136 private Map<Node, Map<String, UserDataRecord>> nodeUserData;
137
138 /** Identifiers. */
139 protected Map<String, Node> identifiers;
140
141 // DOM Level 3: normalizeDocument
142 transient DOMNormalizer domNormalizer = null;
143 transient DOMConfigurationImpl fConfiguration = null;
144
145 // support of XPath API
146 transient Object fXPathEvaluator = null;
147
148 /** Table for quick check of child insertion. */
149 private final static int[] kidOK;
150
151 /**
152 * Number of alterations made to this document since its creation.
153 * Serves as a "dirty bit" so that live objects such as NodeList can
154 * recognize when an alteration has been made and discard its cached
155 * state information.
156 * <p>
157 * Any method that alters the tree structure MUST cause or be
158 * accompanied by a call to changed(), to inform it that any outstanding
159 * NodeLists may have to be updated.
160 * <p>
161 * (Required because NodeList is simultaneously "live" and integer-
162 * indexed -- a bad decision in the DOM's design.)
163 * <p>
164 * Note that changes which do not affect the tree's structure -- changing
165 * the node's name, for example -- do _not_ have to call changed().
166 * <p>
167 * Alternative implementation would be to use a cryptographic
168 * Digest value rather than a count. This would have the advantage that
169 * "harmless" changes (those producing equal() trees) would not force
170 * NodeList to resynchronize. Disadvantage is that it's slightly more prone
171 * to "false negatives", though that's the difference between "wildly
172 * unlikely" and "absurdly unlikely". IF we start maintaining digests,
173 * we should consider taking advantage of them.
174 *
175 * Note: This used to be done a node basis, so that we knew what
176 * subtree changed. But since only DeepNodeList really use this today,
177 * the gain appears to be really small compared to the cost of having
178 * an int on every (parent) node plus having to walk up the tree all the
179 * way to the root to mark the branch as changed everytime a node is
180 * changed.
181 * So we now have a single counter global to the document. It means that
182 * some objects may flush their cache more often than necessary, but this
183 * makes nodes smaller and only the document needs to be marked as changed.
184 */
185 protected int changes = 0;
186
187 // experimental
188
189 /** Allow grammar access. */
190 protected boolean allowGrammarAccess;
191
192 /** Bypass error checking. */
193 protected boolean errorChecking = true;
194 /** Ancestor checking */
195 protected boolean ancestorChecking = true;
196
197 //Did version change at any point when the document was created ?
198 //this field helps us to optimize when normalizingDocument.
199 protected boolean xmlVersionChanged = false ;
200
201 /** The following are required for compareDocumentPosition
202 */
203 // Document number. Documents are ordered across the implementation using
204 // positive integer values. Documents are assigned numbers on demand.
205 private int documentNumber=0;
206 // Node counter and table. Used to assign numbers to nodes for this
207 // document. Node number values are negative integers. Nodes are
208 // assigned numbers on demand.
209 private int nodeCounter = 0;
210 private Map<Node, Integer> nodeTable;
211 private boolean xml11Version = false; //by default 1.0
212 //
213 // Static initialization
214 //
215
216 static {
217
218 kidOK = new int[13];
219
220 kidOK[DOCUMENT_NODE] =
221 1 << ELEMENT_NODE | 1 << PROCESSING_INSTRUCTION_NODE |
222 1 << COMMENT_NODE | 1 << DOCUMENT_TYPE_NODE;
223
224 kidOK[DOCUMENT_FRAGMENT_NODE] =
225 kidOK[ENTITY_NODE] =
226 kidOK[ENTITY_REFERENCE_NODE] =
227 kidOK[ELEMENT_NODE] =
228 1 << ELEMENT_NODE | 1 << PROCESSING_INSTRUCTION_NODE |
229 1 << COMMENT_NODE | 1 << TEXT_NODE |
230 1 << CDATA_SECTION_NODE | 1 << ENTITY_REFERENCE_NODE ;
231
232
233 kidOK[ATTRIBUTE_NODE] =
234 1 << TEXT_NODE | 1 << ENTITY_REFERENCE_NODE;
235
236 kidOK[DOCUMENT_TYPE_NODE] =
237 kidOK[PROCESSING_INSTRUCTION_NODE] =
238 kidOK[COMMENT_NODE] =
239 kidOK[TEXT_NODE] =
240 kidOK[CDATA_SECTION_NODE] =
241 kidOK[NOTATION_NODE] =
242 0;
243
244 } // static
245
246 /**
247 * @serialField docType DocumentTypeImpl document type
248 * @serialField docElement ElementImpl document element
249 * @serialField fFreeNLCache NodeListCache NodeListCache free list
250 * @serialField encoding String Document encoding
251 * @serialField actualEncoding String Document actualEncoding
252 * @serialField version String Document version
253 * @serialField standalone boolean Document standalone
254 * @serialField fDocumentURI String Document URI
255 * @serialField userData Hashtable user data attached to the nodes. Note that
256 * it was original called "userData". It has been changed to nodeUserData to
257 * avoid confusion with those that are actually values of the map.
258 * @serialField identifiers Hashtable identifiers
259 * @serialField changes int flag indicates whether the node has changed
260 * @serialField allowGrammarAccess boolean Allow grammar access
261 * @serialField errorChecking boolean Bypass error checking
262 * @serialField ancestorChecking boolean Ancestor checking
263 * @serialField xmlVersionChanged boolean Indicate whether the version has changed
264 * @serialField documentNumber int Document number
265 * @serialField nodeCounter int Node counter
266 * @serialField nodeTable Hashtable Node table
267 * @serialField xml11Version boolean XML version
268 */
269 private static final ObjectStreamField[] serialPersistentFields =
270 new ObjectStreamField[] {
271 new ObjectStreamField("docType", DocumentTypeImpl.class),
272 new ObjectStreamField("docElement", ElementImpl.class),
273 new ObjectStreamField("fFreeNLCache", NodeListCache.class),
274 new ObjectStreamField("encoding", String.class),
275 new ObjectStreamField("actualEncoding", String.class),
276 new ObjectStreamField("version", String.class),
277 new ObjectStreamField("standalone", boolean.class),
278 new ObjectStreamField("fDocumentURI", String.class),
279 new ObjectStreamField("userData", Hashtable.class),
280 new ObjectStreamField("identifiers", Hashtable.class),
281 new ObjectStreamField("changes", int.class),
282 new ObjectStreamField("allowGrammarAccess", boolean.class),
283 new ObjectStreamField("errorChecking", boolean.class),
284 new ObjectStreamField("ancestorChecking", boolean.class),
285 new ObjectStreamField("xmlVersionChanged", boolean.class),
286 new ObjectStreamField("documentNumber", int.class),
287 new ObjectStreamField("nodeCounter", int.class),
288 new ObjectStreamField("nodeTable", Hashtable.class),
289 new ObjectStreamField("xml11Version", boolean.class),
290 };
291
292 //
293 // Constructors
294 //
295
296 /**
297 * NON-DOM: Actually creating a Document is outside the DOM's spec,
298 * since it has to operate in terms of a particular implementation.
299 */
300 public CoreDocumentImpl() {
301 this(false);
302 }
303
304 /** Constructor. */
305 public CoreDocumentImpl(boolean grammarAccess) {
306 super(null);
307 ownerDocument = this;
308 allowGrammarAccess = grammarAccess;
309 String systemProp = SecuritySupport.getSystemProperty(Constants.SUN_DOM_PROPERTY_PREFIX+Constants.SUN_DOM_ANCESTOR_CHECCK);
310 if (systemProp != null) {
311 if (systemProp.equalsIgnoreCase("false")) {
312 ancestorChecking = false;
313 }
314 }
315 }
316
317 /**
318 * For DOM2 support.
319 * The createDocument factory method is in DOMImplementation.
320 */
321 public CoreDocumentImpl(DocumentType doctype) {
322 this(doctype, false);
323 }
324
325 /** For DOM2 support. */
326 public CoreDocumentImpl(DocumentType doctype, boolean grammarAccess) {
327 this(grammarAccess);
328 if (doctype != null) {
329 DocumentTypeImpl doctypeImpl;
330 try {
331 doctypeImpl = (DocumentTypeImpl) doctype;
332 } catch (ClassCastException e) {
333 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
334 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
335 }
336 doctypeImpl.ownerDocument = this;
337 appendChild(doctype);
338 }
339 }
340
341 //
342 // Node methods
343 //
344
345 // even though ownerDocument refers to this in this implementation
346 // the DOM Level 2 spec says it must be null, so make it appear so
347 final public Document getOwnerDocument() {
348 return null;
349 }
350
351 /** Returns the node type. */
352 public short getNodeType() {
353 return Node.DOCUMENT_NODE;
354 }
355
356 /** Returns the node name. */
357 public String getNodeName() {
358 return "#document";
359 }
360
361 /**
362 * Deep-clone a document, including fixing ownerDoc for the cloned
363 * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR
364 * protection. I've chosen to implement it by calling importNode
365 * which is DOM Level 2.
366 *
367 * @return org.w3c.dom.Node
368 * @param deep boolean, iff true replicate children
369 */
370 public Node cloneNode(boolean deep) {
371
372 CoreDocumentImpl newdoc = new CoreDocumentImpl();
373 callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED);
374 cloneNode(newdoc, deep);
375
376 return newdoc;
377
378 } // cloneNode(boolean):Node
379
380
381 /**
382 * internal method to share code with subclass
383 **/
384 protected void cloneNode(CoreDocumentImpl newdoc, boolean deep) {
385
386 // clone the children by importing them
387 if (needsSyncChildren()) {
388 synchronizeChildren();
389 }
390
391 if (deep) {
392 Map<Node, String> reversedIdentifiers = null;
393
394 if (identifiers != null) {
395 // Build a reverse mapping from element to identifier.
396 reversedIdentifiers = new HashMap<>(identifiers.size());
397 for (String elementId : identifiers.keySet()) {
398 reversedIdentifiers.put(identifiers.get(elementId), elementId);
399 }
400 }
401
402 // Copy children into new document.
403 for (ChildNode kid = firstChild; kid != null;
404 kid = kid.nextSibling) {
405 newdoc.appendChild(newdoc.importNode(kid, true, true,
406 reversedIdentifiers));
407 }
408 }
409
410 // experimental
411 newdoc.allowGrammarAccess = allowGrammarAccess;
412 newdoc.errorChecking = errorChecking;
413
414 } // cloneNode(CoreDocumentImpl,boolean):void
415
416 /**
417 * Since a Document may contain at most one top-level Element child,
418 * and at most one DocumentType declaraction, we need to subclass our
419 * add-children methods to implement this constraint.
420 * Since appendChild() is implemented as insertBefore(,null),
421 * altering the latter fixes both.
422 * <p>
423 * While I'm doing so, I've taken advantage of the opportunity to
424 * cache documentElement and docType so we don't have to
425 * search for them.
426 *
427 * REVISIT: According to the spec it is not allowed to alter neither the
428 * document element nor the document type in any way
429 */
430 public Node insertBefore(Node newChild, Node refChild)
431 throws DOMException {
432
433 // Only one such child permitted
434 int type = newChild.getNodeType();
435 if (errorChecking) {
436 if((type == Node.ELEMENT_NODE && docElement != null) ||
437 (type == Node.DOCUMENT_TYPE_NODE && docType != null)) {
438 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
439 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
440 }
441 }
442 // Adopt orphan doctypes
443 if (newChild.getOwnerDocument() == null &&
444 newChild instanceof DocumentTypeImpl) {
445 ((DocumentTypeImpl) newChild).ownerDocument = this;
446 }
447 super.insertBefore(newChild,refChild);
448
449 // If insert succeeded, cache the kid appropriately
450 if (type == Node.ELEMENT_NODE) {
451 docElement = (ElementImpl)newChild;
452 }
453 else if (type == Node.DOCUMENT_TYPE_NODE) {
454 docType = (DocumentTypeImpl)newChild;
455 }
456
457 return newChild;
458
459 } // insertBefore(Node,Node):Node
460
461 /**
462 * Since insertBefore caches the docElement (and, currently, docType),
463 * removeChild has to know how to undo the cache
464 *
465 * REVISIT: According to the spec it is not allowed to alter neither the
466 * document element nor the document type in any way
467 */
468 public Node removeChild(Node oldChild) throws DOMException {
469
470 super.removeChild(oldChild);
471
472 // If remove succeeded, un-cache the kid appropriately
473 int type = oldChild.getNodeType();
474 if(type == Node.ELEMENT_NODE) {
475 docElement = null;
476 }
477 else if (type == Node.DOCUMENT_TYPE_NODE) {
478 docType = null;
479 }
480
481 return oldChild;
482
483 } // removeChild(Node):Node
484
485 /**
486 * Since we cache the docElement (and, currently, docType),
487 * replaceChild has to update the cache
488 *
489 * REVISIT: According to the spec it is not allowed to alter neither the
490 * document element nor the document type in any way
491 */
492 public Node replaceChild(Node newChild, Node oldChild)
493 throws DOMException {
494
495 // Adopt orphan doctypes
496 if (newChild.getOwnerDocument() == null &&
497 newChild instanceof DocumentTypeImpl) {
498 ((DocumentTypeImpl) newChild).ownerDocument = this;
499 }
500
501 if (errorChecking &&((docType != null &&
502 oldChild.getNodeType() != Node.DOCUMENT_TYPE_NODE &&
503 newChild.getNodeType() == Node.DOCUMENT_TYPE_NODE)
504 || (docElement != null &&
505 oldChild.getNodeType() != Node.ELEMENT_NODE &&
506 newChild.getNodeType() == Node.ELEMENT_NODE))) {
507
508 throw new DOMException(
509 DOMException.HIERARCHY_REQUEST_ERR,
510 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
511 }
512 super.replaceChild(newChild, oldChild);
513
514 int type = oldChild.getNodeType();
515 if(type == Node.ELEMENT_NODE) {
516 docElement = (ElementImpl)newChild;
517 }
518 else if (type == Node.DOCUMENT_TYPE_NODE) {
519 docType = (DocumentTypeImpl)newChild;
520 }
521 return oldChild;
522 } // replaceChild(Node,Node):Node
523
524 /*
525 * Get Node text content
526 * @since DOM Level 3
527 */
528 public String getTextContent() throws DOMException {
529 return null;
530 }
531
532 /*
533 * Set Node text content
534 * @since DOM Level 3
535 */
536 public void setTextContent(String textContent)
537 throws DOMException {
538 // no-op
539 }
540
541 /**
542 * @since DOM Level 3
543 */
544 public Object getFeature(String feature, String version) {
545 return super.getFeature(feature, version);
546 }
547
548 //
549 // Document methods
550 //
551
552 // factory methods
553
554 /**
555 * Factory method; creates an Attribute having this Document as its
556 * OwnerDoc.
557 *
558 * @param name The name of the attribute. Note that the attribute's value is
559 * _not_ established at the factory; remember to set it!
560 *
561 * @throws DOMException(INVALID_NAME_ERR)
562 * if the attribute name is not acceptable.
563 */
564 public Attr createAttribute(String name)
565 throws DOMException {
566
567 if (errorChecking && !isXMLName(name,xml11Version)) {
568 String msg =
569 DOMMessageFormatter.formatMessage(
570 DOMMessageFormatter.DOM_DOMAIN,
571 "INVALID_CHARACTER_ERR",
572 null);
573 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
574 }
575 return new AttrImpl(this, name);
576
577 } // createAttribute(String):Attr
578
579 /**
580 * Factory method; creates a CDATASection having this Document as
581 * its OwnerDoc.
582 *
583 * @param data The initial contents of the CDATA
584 *
585 * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents. (HTML
586 * not yet implemented.)
587 */
588 public CDATASection createCDATASection(String data)
589 throws DOMException {
590 return new CDATASectionImpl(this, data);
591 }
592
593 /**
594 * Factory method; creates a Comment having this Document as its
595 * OwnerDoc.
596 *
597 * @param data The initial contents of the Comment. */
598 public Comment createComment(String data) {
599 return new CommentImpl(this, data);
600 }
601
602 /**
603 * Factory method; creates a DocumentFragment having this Document
604 * as its OwnerDoc.
605 */
606 public DocumentFragment createDocumentFragment() {
607 return new DocumentFragmentImpl(this);
608 }
609
610 /**
611 * Factory method; creates an Element having this Document
612 * as its OwnerDoc.
613 *
614 * @param tagName The name of the element type to instantiate. For
615 * XML, this is case-sensitive. For HTML, the tagName parameter may
616 * be provided in any case, but it must be mapped to the canonical
617 * uppercase form by the DOM implementation.
618 *
619 * @throws DOMException(INVALID_NAME_ERR) if the tag name is not
620 * acceptable.
621 */
622 public Element createElement(String tagName)
623 throws DOMException {
624
625 if (errorChecking && !isXMLName(tagName,xml11Version)) {
626 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
627 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
628 }
629 return new ElementImpl(this, tagName);
630
631 } // createElement(String):Element
632
633 /**
634 * Factory method; creates an EntityReference having this Document
635 * as its OwnerDoc.
636 *
637 * @param name The name of the Entity we wish to refer to
638 *
639 * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
640 * nonstandard entities are not permitted. (HTML not yet
641 * implemented.)
642 */
643 public EntityReference createEntityReference(String name)
644 throws DOMException {
645
646 if (errorChecking && !isXMLName(name,xml11Version)) {
647 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
648 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
649 }
650 return new EntityReferenceImpl(this, name);
651
652 } // createEntityReference(String):EntityReference
653
654 /**
655 * Factory method; creates a ProcessingInstruction having this Document
656 * as its OwnerDoc.
657 *
658 * @param target The target "processor channel"
659 * @param data Parameter string to be passed to the target.
660 *
661 * @throws DOMException(INVALID_NAME_ERR) if the target name is not
662 * acceptable.
663 *
664 * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents. (HTML
665 * not yet implemented.)
666 */
667 public ProcessingInstruction createProcessingInstruction(String target,
668 String data)
669 throws DOMException {
670
671 if (errorChecking && !isXMLName(target,xml11Version)) {
672 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
673 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
674 }
675 return new ProcessingInstructionImpl(this, target, data);
676
677 } // createProcessingInstruction(String,String):ProcessingInstruction
678
679 /**
680 * Factory method; creates a Text node having this Document as its
681 * OwnerDoc.
682 *
683 * @param data The initial contents of the Text.
684 */
685 public Text createTextNode(String data) {
686 return new TextImpl(this, data);
687 }
688
689 // other document methods
690
691 /**
692 * For XML, this provides access to the Document Type Definition.
693 * For HTML documents, and XML documents which don't specify a DTD,
694 * it will be null.
695 */
696 public DocumentType getDoctype() {
697 if (needsSyncChildren()) {
698 synchronizeChildren();
699 }
700 return docType;
701 }
702
703 /**
704 * Convenience method, allowing direct access to the child node
705 * which is considered the root of the actual document content. For
706 * HTML, where it is legal to have more than one Element at the top
707 * level of the document, we pick the one with the tagName
708 * "HTML". For XML there should be only one top-level
709 *
710 * (HTML not yet supported.)
711 */
712 public Element getDocumentElement() {
713 if (needsSyncChildren()) {
714 synchronizeChildren();
715 }
716 return docElement;
717 }
718
719 /**
720 * Return a <em>live</em> collection of all descendent Elements (not just
721 * immediate children) having the specified tag name.
722 *
723 * @param tagname The type of Element we want to gather. "*" will be
724 * taken as a wildcard, meaning "all elements in the document."
725 *
726 * @see DeepNodeListImpl
727 */
728 public NodeList getElementsByTagName(String tagname) {
729 return new DeepNodeListImpl(this,tagname);
730 }
731
732 /**
733 * Retrieve information describing the abilities of this particular
734 * DOM implementation. Intended to support applications that may be
735 * using DOMs retrieved from several different sources, potentially
736 * with different underlying representations.
737 */
738 public DOMImplementation getImplementation() {
739 // Currently implemented as a singleton, since it's hardcoded
740 // information anyway.
741 return CoreDOMImplementationImpl.getDOMImplementation();
742 }
743
744 //
745 // Public methods
746 //
747
748 // properties
749
750 /**
751 * Sets whether the DOM implementation performs error checking
752 * upon operations. Turning off error checking only affects
753 * the following DOM checks:
754 * <ul>
755 * <li>Checking strings to make sure that all characters are
756 * legal XML characters
757 * <li>Hierarchy checking such as allowed children, checks for
758 * cycles, etc.
759 * </ul>
760 * <p>
761 * Turning off error checking does <em>not</em> turn off the
762 * following checks:
763 * <ul>
764 * <li>Read only checks
765 * <li>Checks related to DOM events
766 * </ul>
767 */
768
769 public void setErrorChecking(boolean check) {
770 errorChecking = check;
771 }
772
773 /*
774 * DOM Level 3 WD - Experimental.
775 */
776 public void setStrictErrorChecking(boolean check) {
777 errorChecking = check;
778 }
779
780 /**
781 * Returns true if the DOM implementation performs error checking.
782 */
783 public boolean getErrorChecking() {
784 return errorChecking;
785 }
786
787 /*
788 * DOM Level 3 WD - Experimental.
789 */
790 public boolean getStrictErrorChecking() {
791 return errorChecking;
792 }
793
794 /**
795 * DOM Level 3 CR - Experimental. (Was getActualEncoding)
796 *
797 * An attribute specifying the encoding used for this document
798 * at the time of the parsing. This is <code>null</code> when
799 * it is not known, such as when the <code>Document</code> was
800 * created in memory.
801 * @since DOM Level 3
802 */
803 public String getInputEncoding() {
804 return actualEncoding;
805 }
806
807 /**
808 * DOM Internal
809 * (Was a DOM L3 Core WD public interface method setActualEncoding )
810 *
811 * An attribute specifying the actual encoding of this document. This is
812 * <code>null</code> otherwise.
813 * <br> This attribute represents the property [character encoding scheme]
814 * defined in .
815 */
816 public void setInputEncoding(String value) {
817 actualEncoding = value;
818 }
819
820 /**
821 * DOM Internal
822 * (Was a DOM L3 Core WD public interface method setXMLEncoding )
823 *
824 * An attribute specifying, as part of the XML declaration,
825 * the encoding of this document. This is null when unspecified.
826 */
827 public void setXmlEncoding(String value) {
828 encoding = value;
829 }
830
831 /**
832 * @deprecated This method is internal and only exists for
833 * compatibility with older applications. New applications
834 * should never call this method.
835 */
836 @Deprecated
837 public void setEncoding(String value) {
838 setXmlEncoding(value);
839 }
840
841 /**
842 * DOM Level 3 WD - Experimental.
843 * The encoding of this document (part of XML Declaration)
844 */
845 public String getXmlEncoding() {
846 return encoding;
847 }
848
849 /**
850 * @deprecated This method is internal and only exists for
851 * compatibility with older applications. New applications
852 * should never call this method.
853 */
854 @Deprecated
855 public String getEncoding() {
856 return getXmlEncoding();
857 }
858
859 /**
860 * DOM Level 3 CR - Experimental.
861 * version - An attribute specifying, as part of the XML declaration,
862 * the version number of this document.
863 */
864 public void setXmlVersion(String value) {
865 if(value.equals("1.0") || value.equals("1.1")){
866 //we need to change the flag value only --
867 // when the version set is different than already set.
868 if(!getXmlVersion().equals(value)){
869 xmlVersionChanged = true ;
870 //change the normalization value back to false
871 isNormalized(false);
872 version = value;
873 }
874 }
875 else{
876 //NOT_SUPPORTED_ERR: Raised if the vesion is set to a value that is not supported by
877 //this document
878 //we dont support any other XML version
879 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
880 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
881
882 }
883 if((getXmlVersion()).equals("1.1")){
884 xml11Version = true;
885 }
886 else{
887 xml11Version = false;
888 }
889 }
890
891 /**
892 * @deprecated This method is internal and only exists for
893 * compatibility with older applications. New applications
894 * should never call this method.
895 */
896 @Deprecated
897 public void setVersion(String value) {
898 setXmlVersion(value);
899 }
900
901 /**
902 * DOM Level 3 WD - Experimental.
903 * The version of this document (part of XML Declaration)
904 */
905
906 public String getXmlVersion() {
907 return (version == null)?"1.0":version;
908 }
909
910 /**
911 * @deprecated This method is internal and only exists for
912 * compatibility with older applications. New applications
913 * should never call this method.
914 */
915 @Deprecated
916 public String getVersion() {
917 return getXmlVersion();
918 }
919
920 /**
921 * DOM Level 3 CR - Experimental.
922 *
923 * Xmlstandalone - An attribute specifying, as part of the XML declaration,
924 * whether this document is standalone
925 * @exception DOMException
926 * NOT_SUPPORTED_ERR: Raised if this document does not support the
927 * "XML" feature.
928 * @since DOM Level 3
929 */
930 public void setXmlStandalone(boolean value)
931 throws DOMException {
932 standalone = value;
933 }
934
935 /**
936 * @deprecated This method is internal and only exists for
937 * compatibility with older applications. New applications
938 * should never call this method.
939 */
940 @Deprecated
941 public void setStandalone(boolean value) {
942 setXmlStandalone(value);
943 }
944
945 /**
946 * DOM Level 3 WD - Experimental.
947 * standalone that specifies whether this document is standalone
948 * (part of XML Declaration)
949 */
950 public boolean getXmlStandalone() {
951 return standalone;
952 }
953
954 /**
955 * @deprecated This method is internal and only exists for
956 * compatibility with older applications. New applications
957 * should never call this method.
958 */
959 @Deprecated
960 public boolean getStandalone() {
961 return getXmlStandalone();
962 }
963
964 /**
965 * DOM Level 3 WD - Experimental.
966 * The location of the document or <code>null</code> if undefined.
967 * <br>Beware that when the <code>Document</code> supports the feature
968 * "HTML" , the href attribute of the HTML BASE element takes precedence
969 * over this attribute.
970 * @since DOM Level 3
971 */
972 public String getDocumentURI(){
973 return fDocumentURI;
974 }
975
976
977 /**
978 * DOM Level 3 WD - Experimental.
979 * Renaming node
980 */
981 public Node renameNode(Node n,String namespaceURI,String name)
982 throws DOMException{
983
984 if (errorChecking && n.getOwnerDocument() != this && n != this) {
985 String msg = DOMMessageFormatter.formatMessage(
986 DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
987 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
988 }
989 switch (n.getNodeType()) {
990 case ELEMENT_NODE: {
991 ElementImpl el = (ElementImpl) n;
992 if (el instanceof ElementNSImpl) {
993 ((ElementNSImpl) el).rename(namespaceURI, name);
994
995 // fire user data NODE_RENAMED event
996 callUserDataHandlers(el, null, UserDataHandler.NODE_RENAMED);
997 }
998 else {
999 if (namespaceURI == null) {
1000 if (errorChecking) {
1001 int colon1 = name.indexOf(':');
1002 if(colon1 != -1){
1003 String msg =
1004 DOMMessageFormatter.formatMessage(
1005 DOMMessageFormatter.DOM_DOMAIN,
1006 "NAMESPACE_ERR",
1007 null);
1008 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
1009 }
1010 if (!isXMLName(name,xml11Version)) {
1011 String msg = DOMMessageFormatter.formatMessage(
1012 DOMMessageFormatter.DOM_DOMAIN,
1013 "INVALID_CHARACTER_ERR", null);
1014 throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
1015 msg);
1016 }
1017 }
1018 el.rename(name);
1019
1020 // fire user data NODE_RENAMED event
1021 callUserDataHandlers(el, null,
1022 UserDataHandler.NODE_RENAMED);
1023 }
1024 else {
1025 // we need to create a new object
1026 ElementNSImpl nel =
1027 new ElementNSImpl(this, namespaceURI, name);
1028
1029 // register event listeners on new node
1030 copyEventListeners(el, nel);
1031
1032 // remove user data from old node
1033 Map<String, UserDataRecord> data = removeUserDataTable(el);
1034
1035 // remove old node from parent if any
1036 Node parent = el.getParentNode();
1037 Node nextSib = el.getNextSibling();
1038 if (parent != null) {
1039 parent.removeChild(el);
1040 }
1041 // move children to new node
1042 Node child = el.getFirstChild();
1043 while (child != null) {
1044 el.removeChild(child);
1045 nel.appendChild(child);
1046 child = el.getFirstChild();
1047 }
1048 // move specified attributes to new node
1049 nel.moveSpecifiedAttributes(el);
1050
1051 // attach user data to new node
1052 setUserDataTable(nel, data);
1053
1054 // and fire user data NODE_RENAMED event
1055 callUserDataHandlers(el, nel,
1056 UserDataHandler.NODE_RENAMED);
1057
1058 // insert new node where old one was
1059 if (parent != null) {
1060 parent.insertBefore(nel, nextSib);
1061 }
1062 el = nel;
1063 }
1064 }
1065 // fire ElementNameChanged event
1066 renamedElement((Element) n, el);
1067 return el;
1068 }
1069 case ATTRIBUTE_NODE: {
1070 AttrImpl at = (AttrImpl) n;
1071
1072 // dettach attr from element
1073 Element el = at.getOwnerElement();
1074 if (el != null) {
1075 el.removeAttributeNode(at);
1076 }
1077 if (n instanceof AttrNSImpl) {
1078 ((AttrNSImpl) at).rename(namespaceURI, name);
1079 // reattach attr to element
1080 if (el != null) {
1081 el.setAttributeNodeNS(at);
1082 }
1083
1084 // fire user data NODE_RENAMED event
1085 callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
1086 }
1087 else {
1088 if (namespaceURI == null) {
1089 at.rename(name);
1090 // reattach attr to element
1091 if (el != null) {
1092 el.setAttributeNode(at);
1093 }
1094
1095 // fire user data NODE_RENAMED event
1096 callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
1097 }
1098 else {
1099 // we need to create a new object
1100 AttrNSImpl nat = new AttrNSImpl(this, namespaceURI, name);
1101
1102 // register event listeners on new node
1103 copyEventListeners(at, nat);
1104
1105 // remove user data from old node
1106 Map<String, UserDataRecord> data = removeUserDataTable(at);
1107
1108 // move children to new node
1109 Node child = at.getFirstChild();
1110 while (child != null) {
1111 at.removeChild(child);
1112 nat.appendChild(child);
1113 child = at.getFirstChild();
1114 }
1115
1116 // attach user data to new node
1117 setUserDataTable(nat, data);
1118
1119 // and fire user data NODE_RENAMED event
1120 callUserDataHandlers(at, nat, UserDataHandler.NODE_RENAMED);
1121
1122 // reattach attr to element
1123 if (el != null) {
1124 el.setAttributeNode(nat);
1125 }
1126 at = nat;
1127 }
1128 }
1129 // fire AttributeNameChanged event
1130 renamedAttrNode((Attr) n, at);
1131
1132 return at;
1133 }
1134 default: {
1135 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1136 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1137 }
1138 }
1139
1140 }
1141
1142
1143 /**
1144 * DOM Level 3 WD - Experimental
1145 * Normalize document.
1146 */
1147 public void normalizeDocument(){
1148 // No need to normalize if already normalized.
1149 if (isNormalized() && !isNormalizeDocRequired()) {
1150 return;
1151 }
1152 if (needsSyncChildren()) {
1153 synchronizeChildren();
1154 }
1155
1156 if (domNormalizer == null) {
1157 domNormalizer = new DOMNormalizer();
1158 }
1159
1160 if (fConfiguration == null) {
1161 fConfiguration = new DOMConfigurationImpl();
1162 }
1163 else {
1164 fConfiguration.reset();
1165 }
1166
1167 domNormalizer.normalizeDocument(this, fConfiguration);
1168 isNormalized(true);
1169 //set the XMLversion changed value to false -- once we have finished
1170 //doing normalization
1171 xmlVersionChanged = false ;
1172 }
1173
1174
1175 /**
1176 * DOM Level 3 CR - Experimental
1177 *
1178 * The configuration used when <code>Document.normalizeDocument</code> is
1179 * invoked.
1180 * @since DOM Level 3
1181 */
1182 public DOMConfiguration getDomConfig(){
1183 if (fConfiguration == null) {
1184 fConfiguration = new DOMConfigurationImpl();
1185 }
1186 return fConfiguration;
1187 }
1188
1189
1190 /**
1191 * Returns the absolute base URI of this node or null if the implementation
1192 * wasn't able to obtain an absolute URI. Note: If the URI is malformed, a
1193 * null is returned.
1194 *
1195 * @return The absolute base URI of this node or null.
1196 * @since DOM Level 3
1197 */
1198 public String getBaseURI() {
1199 if (fDocumentURI != null && fDocumentURI.length() != 0 ) {// attribute value is always empty string
1200 try {
1201 return new URI(fDocumentURI).toString();
1202 }
1203 catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
1204 // REVISIT: what should happen in this case?
1205 return null;
1206 }
1207 }
1208 return fDocumentURI;
1209 }
1210
1211 /**
1212 * DOM Level 3 WD - Experimental.
1213 */
1214 public void setDocumentURI(String documentURI){
1215 fDocumentURI = documentURI;
1216 }
1217
1218
1219 //
1220 // DOM L3 LS
1221 //
1222 /**
1223 * DOM Level 3 WD - Experimental.
1224 * Indicates whether the method load should be synchronous or
1225 * asynchronous. When the async attribute is set to <code>true</code>
1226 * the load method returns control to the caller before the document has
1227 * completed loading. The default value of this property is
1228 * <code>false</code>.
1229 * <br>Setting the value of this attribute might throw NOT_SUPPORTED_ERR
1230 * if the implementation doesn't support the mode the attribute is being
1231 * set to. Should the DOM spec define the default value of this
1232 * property? What if implementing both async and sync IO is impractical
1233 * in some systems? 2001-09-14. default is <code>false</code> but we
1234 * need to check with Mozilla and IE.
1235 */
1236 public boolean getAsync() {
1237 return false;
1238 }
1239
1240 /**
1241 * DOM Level 3 WD - Experimental.
1242 * Indicates whether the method load should be synchronous or
1243 * asynchronous. When the async attribute is set to <code>true</code>
1244 * the load method returns control to the caller before the document has
1245 * completed loading. The default value of this property is
1246 * <code>false</code>.
1247 * <br>Setting the value of this attribute might throw NOT_SUPPORTED_ERR
1248 * if the implementation doesn't support the mode the attribute is being
1249 * set to. Should the DOM spec define the default value of this
1250 * property? What if implementing both async and sync IO is impractical
1251 * in some systems? 2001-09-14. default is <code>false</code> but we
1252 * need to check with Mozilla and IE.
1253 */
1254 public void setAsync(boolean async) {
1255 if (async) {
1256 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1257 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1258 }
1259 }
1260 /**
1261 * DOM Level 3 WD - Experimental.
1262 * If the document is currently being loaded as a result of the method
1263 * <code>load</code> being invoked the loading and parsing is
1264 * immediately aborted. The possibly partial result of parsing the
1265 * document is discarded and the document is cleared.
1266 */
1267 public void abort() {
1268 }
1269
1270 /**
1271 * DOM Level 3 WD - Experimental.
1272 *
1273 * Replaces the content of the document with the result of parsing the
1274 * given URI. Invoking this method will either block the caller or
1275 * return to the caller immediately depending on the value of the async
1276 * attribute. Once the document is fully loaded a "load" event (as
1277 * defined in [<a href='http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331'>DOM Level 3 Events</a>]
1278 * , except that the <code>Event.targetNode</code> will be the document,
1279 * not an element) will be dispatched on the document. If an error
1280 * occurs, an implementation dependent "error" event will be dispatched
1281 * on the document. If this method is called on a document that is
1282 * currently loading, the current load is interrupted and the new URI
1283 * load is initiated.
1284 * <br> When invoking this method the parameters used in the
1285 * <code>DOMParser</code> interface are assumed to have their default
1286 * values with the exception that the parameters <code>"entities"</code>
1287 * , <code>"normalize-characters"</code>,
1288 * <code>"check-character-normalization"</code> are set to
1289 * <code>"false"</code>.
1290 * <br> The result of a call to this method is the same the result of a
1291 * call to <code>DOMParser.parseWithContext</code> with an input stream
1292 * referencing the URI that was passed to this call, the document as the
1293 * context node, and the action <code>ACTION_REPLACE_CHILDREN</code>.
1294 * @param uri The URI reference for the XML file to be loaded. If this is
1295 * a relative URI, the base URI used by the implementation is
1296 * implementation dependent.
1297 * @return If async is set to <code>true</code> <code>load</code> returns
1298 * <code>true</code> if the document load was successfully initiated.
1299 * If an error occurred when initiating the document load,
1300 * <code>load</code> returns <code>false</code>.If async is set to
1301 * <code>false</code> <code>load</code> returns <code>true</code> if
1302 * the document was successfully loaded and parsed. If an error
1303 * occurred when either loading or parsing the URI, <code>load</code>
1304 * returns <code>false</code>.
1305 */
1306 public boolean load(String uri) {
1307 return false;
1308 }
1309
1310 /**
1311 * DOM Level 3 WD - Experimental.
1312 * Replace the content of the document with the result of parsing the
1313 * input string, this method is always synchronous.
1314 * @param source A string containing an XML document.
1315 * @return <code>true</code> if parsing the input string succeeded
1316 * without errors, otherwise <code>false</code>.
1317 */
1318 public boolean loadXML(String source) {
1319 return false;
1320 }
1321
1322 /**
1323 * DOM Level 3 WD - Experimental.
1324 * Save the document or the given node and all its descendants to a string
1325 * (i.e. serialize the document or node).
1326 * <br>The parameters used in the <code>LSSerializer</code> interface are
1327 * assumed to have their default values when invoking this method.
1328 * <br> The result of a call to this method is the same the result of a
1329 * call to <code>LSSerializer.writeToString</code> with the document as
1330 * the node to write.
1331 * @param node Specifies what to serialize, if this parameter is
1332 * <code>null</code> the whole document is serialized, if it's
1333 * non-null the given node is serialized.
1334 * @return The serialized document or <code>null</code> in case an error
1335 * occurred.
1336 * @exception DOMException
1337 * WRONG_DOCUMENT_ERR: Raised if the node passed in as the node
1338 * parameter is from an other document.
1339 */
1340 public String saveXML(Node node)
1341 throws DOMException {
1342 if (errorChecking && node != null
1343 && this != node.getOwnerDocument()) {
1344 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
1345 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
1346 }
1347 DOMImplementationLS domImplLS = (DOMImplementationLS) DOMImplementationImpl.getDOMImplementation();
1348 LSSerializer xmlWriter = domImplLS.createLSSerializer();
1349 if (node == null) {
1350 node = this;
1351 }
1352 return xmlWriter.writeToString(node);
1353 }
1354
1355 /**
1356 * Sets whether the DOM implementation generates mutation events upon
1357 * operations.
1358 */
1359 void setMutationEvents(boolean set) {
1360 // does nothing by default - overidden in subclass
1361 }
1362
1363 /**
1364 * Returns true if the DOM implementation generates mutation events.
1365 */
1366 boolean getMutationEvents() {
1367 // does nothing by default - overriden in subclass
1368 return false;
1369 }
1370
1371 // non-DOM factory methods
1372 /**
1373 * NON-DOM Factory method; creates a DocumentType having this Document as
1374 * its OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1375 * information unspecified.)
1376 *
1377 * @param name The name of the Entity we wish to provide a value for.
1378 *
1379 * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where DTDs
1380 * are not permitted. (HTML not yet implemented.)
1381 */
1382 public DocumentType createDocumentType(String qualifiedName,
1383 String publicID,
1384 String systemID)
1385 throws DOMException {
1386
1387 return new DocumentTypeImpl(this, qualifiedName, publicID, systemID);
1388
1389 } // createDocumentType(String):DocumentType
1390
1391 /**
1392 * NON-DOM Factory method; creates an Entity having this Document as its
1393 * OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1394 * information unspecified.)
1395 *
1396 * @param name The name of the Entity we wish to provide a value for.
1397 *
1398 * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
1399 * nonstandard entities are not permitted. (HTML not yet implemented.)
1400 */
1401 public Entity createEntity(String name)
1402 throws DOMException {
1403
1404 if (errorChecking && !isXMLName(name, xml11Version)) {
1405 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1406 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1407 }
1408 return new EntityImpl(this, name);
1409
1410 } // createEntity(String):Entity
1411
1412 /**
1413 * NON-DOM Factory method; creates a Notation having this Document as its
1414 * OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1415 * information unspecified.)
1416 *
1417 * @param name The name of the Notation we wish to describe
1418 *
1419 * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
1420 * notations are not permitted. (HTML not yet implemented.)
1421 */
1422 public Notation createNotation(String name)
1423 throws DOMException {
1424
1425 if (errorChecking && !isXMLName(name, xml11Version)) {
1426 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1427 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1428 }
1429 return new NotationImpl(this, name);
1430
1431 } // createNotation(String):Notation
1432
1433 /**
1434 * NON-DOM Factory method: creates an element definition. Element
1435 * definitions hold default attribute values.
1436 */
1437 public ElementDefinitionImpl createElementDefinition(String name)
1438 throws DOMException {
1439
1440 if (errorChecking && !isXMLName(name, xml11Version)) {
1441 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1442 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1443 }
1444 return new ElementDefinitionImpl(this, name);
1445
1446 } // createElementDefinition(String):ElementDefinitionImpl
1447
1448 // other non-DOM methods
1449 /**
1450 * NON-DOM: Get the number associated with this document. Used to order
1451 * documents in the implementation.
1452 */
1453 protected int getNodeNumber() {
1454 if (documentNumber == 0) {
1455
1456 CoreDOMImplementationImpl cd = (CoreDOMImplementationImpl) CoreDOMImplementationImpl.getDOMImplementation();
1457 documentNumber = cd.assignDocumentNumber();
1458 }
1459 return documentNumber;
1460 }
1461
1462 /**
1463 * NON-DOM: Get a number associated with a node created with respect to this
1464 * document. Needed for compareDocumentPosition when nodes are disconnected.
1465 * This is only used on demand.
1466 */
1467 protected int getNodeNumber(Node node) {
1468
1469 // Check if the node is already in the hash
1470 // If so, retrieve the node number
1471 // If not, assign a number to the node
1472 // Node numbers are negative, from -1 to -n
1473 int num;
1474 if (nodeTable == null) {
1475 nodeTable = new HashMap<>();
1476 num = --nodeCounter;
1477 nodeTable.put(node, num);
1478 } else {
1479 Integer n = nodeTable.get(node);
1480 if (n == null) {
1481 num = --nodeCounter;
1482 nodeTable.put(node, num);
1483 } else {
1484 num = n.intValue();
1485 }
1486 }
1487 return num;
1488 }
1489
1490 /**
1491 * Copies a node from another document to this document. The new nodes are
1492 * created using this document's factory methods and are populated with the
1493 * data from the source's accessor methods defined by the DOM interfaces.
1494 * Its behavior is otherwise similar to that of cloneNode.
1495 * <p>
1496 * According to the DOM specifications, document nodes cannot be imported
1497 * and a NOT_SUPPORTED_ERR exception is thrown if attempted.
1498 */
1499 public Node importNode(Node source, boolean deep)
1500 throws DOMException {
1501 return importNode(source, deep, false, null);
1502 } // importNode(Node,boolean):Node
1503
1504 /**
1505 * Overloaded implementation of DOM's importNode method. This method
1506 * provides the core functionality for the public importNode and cloneNode
1507 * methods.
1508 *
1509 * The reversedIdentifiers parameter is provided for cloneNode to preserve
1510 * the document's identifiers. The Map has Elements as the keys and
1511 * their identifiers as the values. When an element is being imported, a
1512 * check is done for an associated identifier. If one exists, the identifier
1513 * is registered with the new, imported element. If reversedIdentifiers is
1514 * null, the parameter is not applied.
1515 */
1516 private Node importNode(Node source, boolean deep, boolean cloningDoc,
1517 Map<Node, String> reversedIdentifiers)
1518 throws DOMException {
1519 Node newnode = null;
1520 Map<String, UserDataRecord> userData = null;
1521
1522 // Sigh. This doesn't work; too many nodes have private data that
1523 // would have to be manually tweaked. May be able to add local
1524 // shortcuts to each nodetype. Consider ?????
1525 // if(source instanceof NodeImpl &&
1526 // !(source instanceof DocumentImpl))
1527 // {
1528 // // Can't clone DocumentImpl since it invokes us...
1529 // newnode=(NodeImpl)source.cloneNode(false);
1530 // newnode.ownerDocument=this;
1531 // }
1532 // else
1533 if (source instanceof NodeImpl) {
1534 userData = ((NodeImpl) source).getUserDataRecord();
1535 }
1536 int type = source.getNodeType();
1537 switch (type) {
1538 case ELEMENT_NODE: {
1539 Element newElement;
1540 boolean domLevel20 = source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0");
1541 // Create element according to namespace support/qualification.
1542 if(domLevel20 == false || source.getLocalName() == null)
1543 newElement = createElement(source.getNodeName());
1544 else
1545 newElement = createElementNS(source.getNamespaceURI(),
1546 source.getNodeName());
1547
1548 // Copy element's attributes, if any.
1549 NamedNodeMap sourceAttrs = source.getAttributes();
1550 if (sourceAttrs != null) {
1551 int length = sourceAttrs.getLength();
1552 for (int index = 0; index < length; index++) {
1553 Attr attr = (Attr)sourceAttrs.item(index);
1554
1555 // NOTE: this methods is used for both importingNode
1556 // and cloning the document node. In case of the
1557 // clonning default attributes should be copied.
1558 // But for importNode defaults should be ignored.
1559 if (attr.getSpecified() || cloningDoc) {
1560 Attr newAttr = (Attr)importNode(attr, true, cloningDoc,
1561 reversedIdentifiers);
1562
1563 // Attach attribute according to namespace
1564 // support/qualification.
1565 if (domLevel20 == false ||
1566 attr.getLocalName() == null)
1567 newElement.setAttributeNode(newAttr);
1568 else
1569 newElement.setAttributeNodeNS(newAttr);
1570 }
1571 }
1572 }
1573
1574 // Register element identifier.
1575 if (reversedIdentifiers != null) {
1576 // Does element have an associated identifier?
1577 String elementId = reversedIdentifiers.get(source);
1578 if (elementId != null) {
1579 if (identifiers == null) {
1580 identifiers = new HashMap<>();
1581 }
1582
1583 identifiers.put(elementId, newElement);
1584 }
1585 }
1586
1587 newnode = newElement;
1588 break;
1589 }
1590
1591 case ATTRIBUTE_NODE: {
1592
1593 if( source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0") ){
1594 if (source.getLocalName() == null) {
1595 newnode = createAttribute(source.getNodeName());
1596 } else {
1597 newnode = createAttributeNS(source.getNamespaceURI(),
1598 source.getNodeName());
1599 }
1600 }
1601 else {
1602 newnode = createAttribute(source.getNodeName());
1603 }
1604 // if source is an AttrImpl from this very same implementation
1605 // avoid creating the child nodes if possible
1606 if (source instanceof AttrImpl) {
1607 AttrImpl attr = (AttrImpl) source;
1608 if (attr.hasStringValue()) {
1609 AttrImpl newattr = (AttrImpl) newnode;
1610 newattr.setValue(attr.getValue());
1611 deep = false;
1612 }
1613 else {
1614 deep = true;
1615 }
1616 }
1617 else {
1618 // According to the DOM spec the kids carry the value.
1619 // However, there are non compliant implementations out
1620 // there that fail to do so. To avoid ending up with no
1621 // value at all, in this case we simply copy the text value
1622 // directly.
1623 if (source.getFirstChild() == null) {
1624 newnode.setNodeValue(source.getNodeValue());
1625 deep = false;
1626 } else {
1627 deep = true;
1628 }
1629 }
1630 break;
1631 }
1632
1633 case TEXT_NODE: {
1634 newnode = createTextNode(source.getNodeValue());
1635 break;
1636 }
1637
1638 case CDATA_SECTION_NODE: {
1639 newnode = createCDATASection(source.getNodeValue());
1640 break;
1641 }
1642
1643 case ENTITY_REFERENCE_NODE: {
1644 newnode = createEntityReference(source.getNodeName());
1645 // the subtree is created according to this doc by the method
1646 // above, so avoid carrying over original subtree
1647 deep = false;
1648 break;
1649 }
1650
1651 case ENTITY_NODE: {
1652 Entity srcentity = (Entity)source;
1653 EntityImpl newentity =
1654 (EntityImpl)createEntity(source.getNodeName());
1655 newentity.setPublicId(srcentity.getPublicId());
1656 newentity.setSystemId(srcentity.getSystemId());
1657 newentity.setNotationName(srcentity.getNotationName());
1658 // Kids carry additional value,
1659 // allow deep import temporarily
1660 newentity.isReadOnly(false);
1661 newnode = newentity;
1662 break;
1663 }
1664
1665 case PROCESSING_INSTRUCTION_NODE: {
1666 newnode = createProcessingInstruction(source.getNodeName(),
1667 source.getNodeValue());
1668 break;
1669 }
1670
1671 case COMMENT_NODE: {
1672 newnode = createComment(source.getNodeValue());
1673 break;
1674 }
1675
1676 case DOCUMENT_TYPE_NODE: {
1677 // unless this is used as part of cloning a Document
1678 // forbid it for the sake of being compliant to the DOM spec
1679 if (!cloningDoc) {
1680 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1681 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1682 }
1683 DocumentType srcdoctype = (DocumentType)source;
1684 DocumentTypeImpl newdoctype = (DocumentTypeImpl)
1685 createDocumentType(srcdoctype.getNodeName(),
1686 srcdoctype.getPublicId(),
1687 srcdoctype.getSystemId());
1688 // Values are on NamedNodeMaps
1689 NamedNodeMap smap = srcdoctype.getEntities();
1690 NamedNodeMap tmap = newdoctype.getEntities();
1691 if(smap != null) {
1692 for(int i = 0; i < smap.getLength(); i++) {
1693 tmap.setNamedItem(importNode(smap.item(i), true, true,
1694 reversedIdentifiers));
1695 }
1696 }
1697 smap = srcdoctype.getNotations();
1698 tmap = newdoctype.getNotations();
1699 if (smap != null) {
1700 for(int i = 0; i < smap.getLength(); i++) {
1701 tmap.setNamedItem(importNode(smap.item(i), true, true,
1702 reversedIdentifiers));
1703 }
1704 }
1705
1706 // NOTE: At this time, the DOM definition of DocumentType
1707 // doesn't cover Elements and their Attributes. domimpl's
1708 // extentions in that area will not be preserved, even if
1709 // copying from domimpl to domimpl. We could special-case
1710 // that here. Arguably we should. Consider. ?????
1711 newnode = newdoctype;
1712 break;
1713 }
1714
1715 case DOCUMENT_FRAGMENT_NODE: {
1716 newnode = createDocumentFragment();
1717 // No name, kids carry value
1718 break;
1719 }
1720
1721 case NOTATION_NODE: {
1722 Notation srcnotation = (Notation)source;
1723 NotationImpl newnotation =
1724 (NotationImpl)createNotation(source.getNodeName());
1725 newnotation.setPublicId(srcnotation.getPublicId());
1726 newnotation.setSystemId(srcnotation.getSystemId());
1727 // Kids carry additional value
1728 newnode = newnotation;
1729 // No name, no value
1730 break;
1731 }
1732 case DOCUMENT_NODE : // Can't import document nodes
1733 default: { // Unknown node type
1734 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1735 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1736 }
1737 }
1738
1739 if(userData != null)
1740 callUserDataHandlers(source, newnode, UserDataHandler.NODE_IMPORTED,userData);
1741
1742 // If deep, replicate and attach the kids.
1743 if (deep) {
1744 for (Node srckid = source.getFirstChild();
1745 srckid != null;
1746 srckid = srckid.getNextSibling()) {
1747 newnode.appendChild(importNode(srckid, true, cloningDoc,
1748 reversedIdentifiers));
1749 }
1750 }
1751 if (newnode.getNodeType() == Node.ENTITY_NODE) {
1752 ((NodeImpl)newnode).setReadOnly(true, true);
1753 }
1754 return newnode;
1755
1756 } // importNode(Node,boolean,boolean,Map):Node
1757
1758 /**
1759 * DOM Level 3 WD - Experimental
1760 * Change the node's ownerDocument, and its subtree, to this Document
1761 *
1762 * @param source The node to adopt.
1763 * @see #importNode
1764 **/
1765 public Node adoptNode(Node source) {
1766 NodeImpl node;
1767 Map<String, UserDataRecord> userData;
1768 try {
1769 node = (NodeImpl) source;
1770 } catch (ClassCastException e) {
1771 // source node comes from a different DOMImplementation
1772 return null;
1773 }
1774
1775 // Return null if the source is null
1776
1777 if (source == null ) {
1778 return null;
1779 } else if (source.getOwnerDocument() != null) {
1780
1781 DOMImplementation thisImpl = this.getImplementation();
1782 DOMImplementation otherImpl = source.getOwnerDocument().getImplementation();
1783
1784 // when the source node comes from a different implementation.
1785 if (thisImpl != otherImpl) {
1786
1787 // Adopting from a DefferedDOM to DOM
1788 if (thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl &&
1789 otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl) {
1790 // traverse the DOM and expand deffered nodes and then allow adoption
1791 undeferChildren (node);
1792 } else if ( thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl
1793 && otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl) {
1794 // Adopting from a DOM into a DefferedDOM, this should be okay
1795 } else {
1796 // Adopting between two dissimilar DOM's is not allowed
1797 return null;
1798 }
1799 }
1800 }
1801
1802 switch (node.getNodeType()) {
1803 case ATTRIBUTE_NODE: {
1804 AttrImpl attr = (AttrImpl) node;
1805 // remove node from wherever it is
1806 if( attr.getOwnerElement() != null){
1807 //1. owner element attribute is set to null
1808 attr.getOwnerElement().removeAttributeNode(attr);
1809 }
1810 //2. specified flag is set to true
1811 attr.isSpecified(true);
1812 userData = node.getUserDataRecord();
1813
1814 //3. change ownership
1815 attr.setOwnerDocument(this);
1816 if (userData != null) {
1817 setUserDataTable(node, userData);
1818 }
1819 break;
1820 }
1821 //entity, notation nodes are read only nodes.. so they can't be adopted.
1822 //runtime will fall through to NOTATION_NODE
1823 case ENTITY_NODE:
1824 case NOTATION_NODE:{
1825 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
1826 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
1827
1828 }
1829 //document, documentype nodes can't be adopted.
1830 //runtime will fall through to DocumentTypeNode
1831 case DOCUMENT_NODE:
1832 case DOCUMENT_TYPE_NODE: {
1833 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1834 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1835 }
1836 case ENTITY_REFERENCE_NODE: {
1837 userData = node.getUserDataRecord();
1838 // remove node from wherever it is
1839 Node parent = node.getParentNode();
1840 if (parent != null) {
1841 parent.removeChild(source);
1842 }
1843 // discard its replacement value
1844 Node child;
1845 while ((child = node.getFirstChild()) != null) {
1846 node.removeChild(child);
1847 }
1848 // change ownership
1849 node.setOwnerDocument(this);
1850 if (userData != null) {
1851 setUserDataTable(node, userData);
1852 }
1853 // set its new replacement value if any
1854 if (docType == null) {
1855 break;
1856 }
1857 NamedNodeMap entities = docType.getEntities();
1858 Node entityNode = entities.getNamedItem(node.getNodeName());
1859 if (entityNode == null) {
1860 break;
1861 }
1862 for (child = entityNode.getFirstChild();
1863 child != null; child = child.getNextSibling()) {
1864 Node childClone = child.cloneNode(true);
1865 node.appendChild(childClone);
1866 }
1867 break;
1868 }
1869 case ELEMENT_NODE: {
1870 userData = node.getUserDataRecord();
1871 // remove node from wherever it is
1872 Node parent = node.getParentNode();
1873 if (parent != null) {
1874 parent.removeChild(source);
1875 }
1876 // change ownership
1877 node.setOwnerDocument(this);
1878 if (userData != null) {
1879 setUserDataTable(node, userData);
1880 }
1881 // reconcile default attributes
1882 ((ElementImpl)node).reconcileDefaultAttributes();
1883 break;
1884 }
1885 default: {
1886 userData = node.getUserDataRecord();
1887 // remove node from wherever it is
1888 Node parent = node.getParentNode();
1889 if (parent != null) {
1890 parent.removeChild(source);
1891 }
1892 // change ownership
1893 node.setOwnerDocument(this);
1894 if (userData != null) {
1895 setUserDataTable(node, userData);
1896 }
1897 }
1898 }
1899
1900 //DOM L3 Core CR
1901 //http://www.w3.org/TR/2003/CR-DOM-Level-3-Core-20031107/core.html#UserDataHandler-ADOPTED
1902 if (userData != null) {
1903 callUserDataHandlers(source, null, UserDataHandler.NODE_ADOPTED, userData);
1904 }
1905
1906 return node;
1907 }
1908
1909 /**
1910 * Traverses the DOM Tree and expands deferred nodes and their
1911 * children.
1912 *
1913 */
1914 protected void undeferChildren(Node node) {
1915
1916 Node top = node;
1917
1918 while (null != node) {
1919
1920 if (((NodeImpl)node).needsSyncData()) {
1921 ((NodeImpl)node).synchronizeData();
1922 }
1923
1924 NamedNodeMap attributes = node.getAttributes();
1925 if (attributes != null) {
1926 int length = attributes.getLength();
1927 for (int i = 0; i < length; ++i) {
1928 undeferChildren(attributes.item(i));
1929 }
1930 }
1931
1932 Node nextNode = null;
1933 nextNode = node.getFirstChild();
1934
1935 while (null == nextNode) {
1936
1937 if (top.equals(node))
1938 break;
1939
1940 nextNode = node.getNextSibling();
1941
1942 if (null == nextNode) {
1943 node = node.getParentNode();
1944
1945 if ((null == node) || (top.equals(node))) {
1946 nextNode = null;
1947 break;
1948 }
1949 }
1950 }
1951
1952 node = nextNode;
1953 }
1954 }
1955
1956 // identifier maintenence
1957 /**
1958 * Introduced in DOM Level 2
1959 * Returns the Element whose ID is given by elementId. If no such element
1960 * exists, returns null. Behavior is not defined if more than one element
1961 * has this ID.
1962 * <p>
1963 * Note: The DOM implementation must have information that says which
1964 * attributes are of type ID. Attributes with the name "ID" are not of type
1965 * ID unless so defined. Implementations that do not know whether
1966 * attributes are of type ID or not are expected to return null.
1967 * @see #getIdentifier
1968 */
1969 public Element getElementById(String elementId) {
1970 return getIdentifier(elementId);
1971 }
1972
1973 /**
1974 * Remove all identifiers from the ID table
1975 */
1976 protected final void clearIdentifiers(){
1977 if (identifiers != null){
1978 identifiers.clear();
1979 }
1980 }
1981
1982 /**
1983 * Registers an identifier name with a specified element node.
1984 * If the identifier is already registered, the new element
1985 * node replaces the previous node. If the specified element
1986 * node is null, removeIdentifier() is called.
1987 *
1988 * @see #getIdentifier
1989 * @see #removeIdentifier
1990 */
1991 public void putIdentifier(String idName, Element element) {
1992
1993 if (element == null) {
1994 removeIdentifier(idName);
1995 return;
1996 }
1997
1998 if (needsSyncData()) {
1999 synchronizeData();
2000 }
2001
2002 if (identifiers == null) {
2003 identifiers = new HashMap<>();
2004 }
2005
2006 identifiers.put(idName, element);
2007
2008 } // putIdentifier(String,Element)
2009
2010 /**
2011 * Returns a previously registered element with the specified
2012 * identifier name, or null if no element is registered.
2013 *
2014 * @see #putIdentifier
2015 * @see #removeIdentifier
2016 */
2017 public Element getIdentifier(String idName) {
2018
2019 if (needsSyncData()) {
2020 synchronizeData();
2021 }
2022
2023 if (identifiers == null) {
2024 return null;
2025 }
2026 Element elem = (Element) identifiers.get(idName);
2027 if (elem != null) {
2028 // check that the element is in the tree
2029 Node parent = elem.getParentNode();
2030 while (parent != null) {
2031 if (parent == this) {
2032 return elem;
2033 }
2034 parent = parent.getParentNode();
2035 }
2036 }
2037 return null;
2038 } // getIdentifier(String):Element
2039
2040 /**
2041 * Removes a previously registered element with the specified
2042 * identifier name.
2043 *
2044 * @see #putIdentifier
2045 * @see #getIdentifier
2046 */
2047 public void removeIdentifier(String idName) {
2048
2049 if (needsSyncData()) {
2050 synchronizeData();
2051 }
2052
2053 if (identifiers == null) {
2054 return;
2055 }
2056
2057 identifiers.remove(idName);
2058
2059 } // removeIdentifier(String)
2060
2061 //
2062 // DOM2: Namespace methods
2063 //
2064 /**
2065 * Introduced in DOM Level 2. <p>
2066 * Creates an element of the given qualified name and namespace URI.
2067 * If the given namespaceURI is null or an empty string and the
2068 * qualifiedName has a prefix that is "xml", the created element
2069 * is bound to the predefined namespace
2070 * "http://www.w3.org/XML/1998/namespace" [Namespaces].
2071 * @param namespaceURI The namespace URI of the element to
2072 * create.
2073 * @param qualifiedName The qualified name of the element type to
2074 * instantiate.
2075 * @return Element A new Element object with the following attributes:
2076 * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2077 * name contains an invalid character.
2078 * @throws DOMException NAMESPACE_ERR: Raised if the qualifiedName has a
2079 * prefix that is "xml" and the namespaceURI is
2080 * neither null nor an empty string nor
2081 * "http://www.w3.org/XML/1998/namespace", or
2082 * if the qualifiedName has a prefix different
2083 * from "xml" and the namespaceURI is null or an
2084 * empty string.
2085 * @since WD-DOM-Level-2-19990923
2086 */
2087 public Element createElementNS(String namespaceURI, String qualifiedName)
2088 throws DOMException {
2089 return new ElementNSImpl(this, namespaceURI, qualifiedName);
2090 }
2091
2092 /**
2093 * NON-DOM: a factory method used by the Xerces DOM parser
2094 * to create an element.
2095 *
2096 * @param namespaceURI The namespace URI of the element to
2097 * create.
2098 * @param qualifiedName The qualified name of the element type to
2099 * instantiate.
2100 * @param localpart The local name of the attribute to instantiate.
2101 *
2102 * @return Element A new Element object with the following attributes:
2103 * @exception DOMException INVALID_CHARACTER_ERR: Raised if the specified
2104 * name contains an invalid character.
2105 */
2106 public Element createElementNS(String namespaceURI, String qualifiedName,
2107 String localpart)
2108 throws DOMException {
2109 return new ElementNSImpl(this, namespaceURI, qualifiedName, localpart);
2110 }
2111
2112 /**
2113 * Introduced in DOM Level 2. <p>
2114 * Creates an attribute of the given qualified name and namespace URI.
2115 * If the given namespaceURI is null or an empty string and the
2116 * qualifiedName has a prefix that is "xml", the created element
2117 * is bound to the predefined namespace
2118 * "http://www.w3.org/XML/1998/namespace" [Namespaces].
2119 *
2120 * @param namespaceURI The namespace URI of the attribute to
2121 * create. When it is null or an empty string,
2122 * this method behaves like createAttribute.
2123 * @param qualifiedName The qualified name of the attribute to
2124 * instantiate.
2125 * @return Attr A new Attr object.
2126 * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2127 * name contains an invalid character.
2128 * @since WD-DOM-Level-2-19990923
2129 */
2130 public Attr createAttributeNS(String namespaceURI, String qualifiedName)
2131 throws DOMException {
2132 return new AttrNSImpl(this, namespaceURI, qualifiedName);
2133 }
2134
2135 /**
2136 * NON-DOM: a factory method used by the Xerces DOM parser
2137 * to create an element.
2138 *
2139 * @param namespaceURI The namespace URI of the attribute to
2140 * create. When it is null or an empty string,
2141 * this method behaves like createAttribute.
2142 * @param qualifiedName The qualified name of the attribute to
2143 * instantiate.
2144 * @param localpart The local name of the attribute to instantiate.
2145 *
2146 * @return Attr A new Attr object.
2147 * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2148 * name contains an invalid character.
2149 */
2150 public Attr createAttributeNS(String namespaceURI, String qualifiedName,
2151 String localpart)
2152 throws DOMException {
2153 return new AttrNSImpl(this, namespaceURI, qualifiedName, localpart);
2154 }
2155
2156 /**
2157 * Introduced in DOM Level 2. <p>
2158 * Returns a NodeList of all the Elements with a given local name and
2159 * namespace URI in the order in which they would be encountered in a
2160 * preorder traversal of the Document tree.
2161 * @param namespaceURI The namespace URI of the elements to match
2162 * on. The special value "*" matches all
2163 * namespaces. When it is null or an empty
2164 * string, this method behaves like
2165 * getElementsByTagName.
2166 * @param localName The local name of the elements to match on.
2167 * The special value "*" matches all local names.
2168 * @return NodeList A new NodeList object containing all the matched
2169 * Elements.
2170 * @since WD-DOM-Level-2-19990923
2171 */
2172 public NodeList getElementsByTagNameNS(String namespaceURI,
2173 String localName) {
2174 return new DeepNodeListImpl(this, namespaceURI, localName);
2175 }
2176
2177 //
2178 // Object methods
2179 //
2180
2181 /** Clone. */
2182 public Object clone() throws CloneNotSupportedException {
2183 CoreDocumentImpl newdoc = (CoreDocumentImpl) super.clone();
2184 newdoc.docType = null;
2185 newdoc.docElement = null;
2186 return newdoc;
2187 }
2188
2189 //
2190 // Public static methods
2191 //
2192
2193 /**
2194 * Check the string against XML's definition of acceptable names for
2195 * elements and attributes and so on using the XMLCharacterProperties
2196 * utility class
2197 */
2198
2199 public static final boolean isXMLName(String s, boolean xml11Version) {
2200
2201 if (s == null) {
2202 return false;
2203 }
2204 if(!xml11Version)
2205 return XMLChar.isValidName(s);
2206 else
2207 return XML11Char.isXML11ValidName(s);
2208
2209 } // isXMLName(String):boolean
2210
2211 /**
2212 * Checks if the given qualified name is legal with respect
2213 * to the version of XML to which this document must conform.
2214 *
2215 * @param prefix prefix of qualified name
2216 * @param local local part of qualified name
2217 */
2218 public static final boolean isValidQName(String prefix, String local, boolean xml11Version) {
2219
2220 // check that both prefix and local part match NCName
2221 if (local == null) return false;
2222 boolean validNCName = false;
2223
2224 if (!xml11Version) {
2225 validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
2226 && XMLChar.isValidNCName(local);
2227 }
2228 else {
2229 validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
2230 && XML11Char.isXML11ValidNCName(local);
2231 }
2232
2233 return validNCName;
2234 }
2235 //
2236 // Protected methods
2237 //
2238
2239 /**
2240 * Uses the kidOK lookup table to check whether the proposed
2241 * tree structure is legal.
2242 */
2243 protected boolean isKidOK(Node parent, Node child) {
2244 if (allowGrammarAccess &&
2245 parent.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
2246 return child.getNodeType() == Node.ELEMENT_NODE;
2247 }
2248 return 0 != (kidOK[parent.getNodeType()] & 1 << child.getNodeType());
2249 }
2250
2251 /**
2252 * Denotes that this node has changed.
2253 */
2254 protected void changed() {
2255 changes++;
2256 }
2257
2258 /**
2259 * Returns the number of changes to this node.
2260 */
2261 protected int changes() {
2262 return changes;
2263 }
2264
2265 // NodeListCache pool
2266
2267 /**
2268 * Returns a NodeListCache for the given node.
2269 */
2270 NodeListCache getNodeListCache(ParentNode owner) {
2271 if (fFreeNLCache == null) {
2272 return new NodeListCache(owner);
2273 }
2274 NodeListCache c = fFreeNLCache;
2275 fFreeNLCache = fFreeNLCache.next;
2276 c.fChild = null;
2277 c.fChildIndex = -1;
2278 c.fLength = -1;
2279 // revoke previous ownership
2280 if (c.fOwner != null) {
2281 c.fOwner.fNodeListCache = null;
2282 }
2283 c.fOwner = owner;
2284 // c.next = null; not necessary, except for confused people...
2285 return c;
2286 }
2287
2288 /**
2289 * Puts the given NodeListCache in the free list.
2290 * Note: The owner node can keep using it until we reuse it
2291 */
2292 void freeNodeListCache(NodeListCache c) {
2293 c.next = fFreeNLCache;
2294 fFreeNLCache = c;
2295 }
2296
2297
2298
2299 /**
2300 * Associate an object to a key on this node. The object can later be
2301 * retrieved from this node by calling <code>getUserData</code> with the
2302 * same key.
2303 * @param n The node to associate the object to.
2304 * @param key The key to associate the object to.
2305 * @param data The object to associate to the given key, or
2306 * <code>null</code> to remove any existing association to that key.
2307 * @param handler The handler to associate to that key, or
2308 * <code>null</code>.
2309 * @return Returns the <code>DOMObject</code> previously associated to
2310 * the given key on this node, or <code>null</code> if there was none.
2311 * @since DOM Level 3
2312 *
2313 * REVISIT: we could use a free list of UserDataRecord here
2314 */
2315 public Object setUserData(Node n, String key,
2316 Object data, UserDataHandler handler) {
2317 if (data == null) {
2318 if (nodeUserData != null) {
2319 Map<String, UserDataRecord> t = nodeUserData.get(n);
2320 if (t != null) {
2321 UserDataRecord r = t.remove(key);
2322 if (r != null) {
2323 return r.fData;
2324 }
2325 }
2326 }
2327 return null;
2328 } else {
2329 Map<String, UserDataRecord> t;
2330 if (nodeUserData == null) {
2331 nodeUserData = new HashMap<>();
2332 t = new HashMap<>();
2333 nodeUserData.put(n, t);
2334 } else {
2335 t = nodeUserData.get(n);
2336 if (t == null) {
2337 t = new HashMap<>();
2338 nodeUserData.put(n, t);
2339 }
2340 }
2341 UserDataRecord r = t.put(key, new UserDataRecord(data, handler));
2342 if (r != null) {
2343 return r.fData;
2344 }
2345 return null;
2346 }
2347 }
2348
2349
2350 /**
2351 * Retrieves the object associated to a key on a this node. The object
2352 * must first have been set to this node by calling
2353 * <code>setUserData</code> with the same key.
2354 * @param n The node the object is associated to.
2355 * @param key The key the object is associated to.
2356 * @return Returns the <code>DOMObject</code> associated to the given key
2357 * on this node, or <code>null</code> if there was none.
2358 * @since DOM Level 3
2359 */
2360 public Object getUserData(Node n, String key) {
2361 if (nodeUserData == null) {
2362 return null;
2363 }
2364 Map<String, UserDataRecord> t = nodeUserData.get(n);
2365 if (t == null) {
2366 return null;
2367 }
2368 UserDataRecord r = t.get(key);
2369 if (r != null) {
2370 return r.fData;
2371 }
2372 return null;
2373 }
2374
2375 protected Map<String, UserDataRecord> getUserDataRecord(Node n) {
2376 if (nodeUserData == null) {
2377 return null;
2378 }
2379 Map<String, UserDataRecord> t = nodeUserData.get(n);
2380 if (t == null) {
2381 return null;
2382 }
2383 return t;
2384 }
2385
2386 /**
2387 * Remove user data table for the given node.
2388 * @param n The node this operation applies to.
2389 * @return The removed table.
2390 */
2391 Map<String, UserDataRecord> removeUserDataTable(Node n) {
2392 if (nodeUserData == null) {
2393 return null;
2394 }
2395 return nodeUserData.get(n);
2396 }
2397
2398 /**
2399 * Set user data table for the given node.
2400 * @param n The node this operation applies to.
2401 * @param data The user data table.
2402 */
2403 void setUserDataTable(Node n, Map<String, UserDataRecord> data) {
2404 if (nodeUserData == null) {
2405 nodeUserData = new HashMap<>();
2406 }
2407
2408 if (data != null) {
2409 nodeUserData.put(n, data);
2410 }
2411 }
2412
2413 /**
2414 * Call user data handlers when a node is deleted (finalized)
2415 * @param n The node this operation applies to.
2416 * @param c The copy node or null.
2417 * @param operation The operation - import, clone, or delete.
2418 */
2419 void callUserDataHandlers(Node n, Node c, short operation) {
2420 if (nodeUserData == null) {
2421 return;
2422 }
2423
2424 if (n instanceof NodeImpl) {
2425 Map<String, UserDataRecord> t = ((NodeImpl) n).getUserDataRecord();
2426 if (t == null || t.isEmpty()) {
2427 return;
2428 }
2429 callUserDataHandlers(n, c, operation, t);
2430 }
2431 }
2432
2433 /**
2434 * Call user data handlers when a node is deleted (finalized)
2435 * @param n The node this operation applies to.
2436 * @param c The copy node or null.
2437 * @param operation The operation - import, clone, or delete.
2438 * @param handlers Data associated with n.
2439 */
2440 void callUserDataHandlers(Node n, Node c, short operation, Map<String, UserDataRecord> userData) {
2441 if (userData == null || userData.isEmpty()) {
2442 return;
2443 }
2444
2445 userData.keySet().stream().forEach((key) -> {
2446 UserDataRecord r = userData.get(key);
2447 if (r.fHandler != null) {
2448 r.fHandler.handle(operation, key, r.fData, n, c);
2449 }
2450 });
2451 }
2452
2453 /**
2454 * Call user data handlers to let them know the nodes they are related to
2455 * are being deleted. The alternative would be to do that on Node but
2456 * because the nodes are used as the keys we have a reference to them that
2457 * prevents them from being gc'ed until the document is. At the same time,
2458 * doing it here has the advantage of avoiding a finalize() method on Node,
2459 * which would affect all nodes and not just the ones that have a user
2460 * data.
2461 */
2462 // Temporarily comment out this method, because
2463 // 1. It seems that finalizers are not guaranteed to be called, so the
2464 // functionality is not implemented.
2465 // 2. It affects the performance greatly in multi-thread environment.
2466 // -SG
2467 /*public void finalize() {
2468 if (userData == null) {
2469 return;
2470 }
2471 Enumeration nodes = userData.keys();
2472 while (nodes.hasMoreElements()) {
2473 Object node = nodes.nextElement();
2474 Hashtable t = (Hashtable) userData.get(node);
2475 if (t != null && !t.isEmpty()) {
2476 Enumeration keys = t.keys();
2477 while (keys.hasMoreElements()) {
2478 String key = (String) keys.nextElement();
2479 UserDataRecord r = (UserDataRecord) t.get(key);
2480 if (r.fHandler != null) {
2481 r.fHandler.handle(UserDataHandler.NODE_DELETED,
2482 key, r.fData, null, null);
2483 }
2484 }
2485 }
2486 }
2487 }*/
2488
2489 protected final void checkNamespaceWF( String qname, int colon1,
2490 int colon2) {
2491
2492 if (!errorChecking) {
2493 return;
2494 }
2495 // it is an error for NCName to have more than one ':'
2496 // check if it is valid QName [Namespace in XML production 6]
2497 // :camera , nikon:camera:minolta, camera:
2498 if (colon1 == 0 || colon1 == qname.length() - 1 || colon2 != colon1) {
2499 String msg =
2500 DOMMessageFormatter.formatMessage(
2501 DOMMessageFormatter.DOM_DOMAIN,
2502 "NAMESPACE_ERR",
2503 null);
2504 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2505 }
2506 }
2507 protected final void checkDOMNSErr(String prefix,
2508 String namespace) {
2509 if (errorChecking) {
2510 if (namespace == null) {
2511 String msg =
2512 DOMMessageFormatter.formatMessage(
2513 DOMMessageFormatter.DOM_DOMAIN,
2514 "NAMESPACE_ERR",
2515 null);
2516 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2517 }
2518 else if (prefix.equals("xml")
2519 && !namespace.equals(NamespaceContext.XML_URI)) {
2520 String msg =
2521 DOMMessageFormatter.formatMessage(
2522 DOMMessageFormatter.DOM_DOMAIN,
2523 "NAMESPACE_ERR",
2524 null);
2525 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2526 }
2527 else if (
2528 prefix.equals("xmlns")
2529 && !namespace.equals(NamespaceContext.XMLNS_URI)
2530 || (!prefix.equals("xmlns")
2531 && namespace.equals(NamespaceContext.XMLNS_URI))) {
2532 String msg =
2533 DOMMessageFormatter.formatMessage(
2534 DOMMessageFormatter.DOM_DOMAIN,
2535 "NAMESPACE_ERR",
2536 null);
2537 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2538 }
2539 }
2540 }
2541
2542 /**
2543 * Checks if the given qualified name is legal with respect
2544 * to the version of XML to which this document must conform.
2545 *
2546 * @param prefix prefix of qualified name
2547 * @param local local part of qualified name
2548 */
2549 protected final void checkQName(String prefix, String local) {
2550 if (!errorChecking) {
2551 return;
2552 }
2553
2554 // check that both prefix and local part match NCName
2555 boolean validNCName = false;
2556 if (!xml11Version) {
2557 validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
2558 && XMLChar.isValidNCName(local);
2559 }
2560 else {
2561 validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
2562 && XML11Char.isXML11ValidNCName(local);
2563 }
2564
2565 if (!validNCName) {
2566 // REVISIT: add qname parameter to the message
2567 String msg =
2568 DOMMessageFormatter.formatMessage(
2569 DOMMessageFormatter.DOM_DOMAIN,
2570 "INVALID_CHARACTER_ERR",
2571 null);
2572 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
2573 }
2574 }
2575
2576 /**
2577 * We could have more xml versions in future , but for now we could
2578 * do with this to handle XML 1.0 and 1.1
2579 */
2580 boolean isXML11Version(){
2581 return xml11Version;
2582 }
2583
2584 boolean isNormalizeDocRequired(){
2585 // REVISIT: Implement to optimize when normalization
2586 // is required
2587 return true;
2588 }
2589
2590 //we should be checking the (elements, attribute, entity etc.) names only when
2591 //version of the document is changed.
2592 boolean isXMLVersionChanged(){
2593 return xmlVersionChanged ;
2594 }
2595 /**
2596 * NON-DOM: kept for backward compatibility
2597 * Store user data related to a given node
2598 * This is a place where we could use weak references! Indeed, the node
2599 * here won't be GC'ed as long as some user data is attached to it, since
2600 * the userData table will have a reference to the node.
2601 */
2602 protected void setUserData(NodeImpl n, Object data) {
2603 setUserData(n, "XERCES1DOMUSERDATA", data, null);
2604 }
2605
2606 /**
2607 * NON-DOM: kept for backward compatibility
2608 * Retreive user data related to a given node
2609 */
2610 protected Object getUserData(NodeImpl n) {
2611 return getUserData(n, "XERCES1DOMUSERDATA");
2612 }
2613
2614
2615 // Event related methods overidden in subclass
2616
2617 protected void addEventListener(NodeImpl node, String type,
2618 EventListener listener,
2619 boolean useCapture) {
2620 // does nothing by default - overidden in subclass
2621 }
2622
2623 protected void removeEventListener(NodeImpl node, String type,
2624 EventListener listener,
2625 boolean useCapture) {
2626 // does nothing by default - overidden in subclass
2627 }
2628
2629 protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
2630 // does nothing by default - overidden in subclass
2631 }
2632
2633 protected boolean dispatchEvent(NodeImpl node, Event event) {
2634 // does nothing by default - overidden in subclass
2635 return false;
2636 }
2637
2638 // Notification methods overidden in subclasses
2639
2640 /**
2641 * A method to be called when some text was changed in a text node,
2642 * so that live objects can be notified.
2643 */
2644 void replacedText(NodeImpl node) {
2645 }
2646
2647 /**
2648 * A method to be called when some text was deleted from a text node,
2649 * so that live objects can be notified.
2650 */
2651 void deletedText(NodeImpl node, int offset, int count) {
2652 }
2653
2654 /**
2655 * A method to be called when some text was inserted into a text node,
2656 * so that live objects can be notified.
2657 */
2658 void insertedText(NodeImpl node, int offset, int count) {
2659 }
2660
2661 /**
2662 * A method to be called when a character data node is about to be modified
2663 */
2664 void modifyingCharacterData(NodeImpl node, boolean replace) {
2665 }
2666
2667 /**
2668 * A method to be called when a character data node has been modified
2669 */
2670 void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) {
2671 }
2672
2673 /**
2674 * A method to be called when a node is about to be inserted in the tree.
2675 */
2676 void insertingNode(NodeImpl node, boolean replace) {
2677 }
2678
2679 /**
2680 * A method to be called when a node has been inserted in the tree.
2681 */
2682 void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
2683 }
2684
2685 /**
2686 * A method to be called when a node is about to be removed from the tree.
2687 */
2688 void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
2689 }
2690
2691 /**
2692 * A method to be called when a node has been removed from the tree.
2693 */
2694 void removedNode(NodeImpl node, boolean replace) {
2695 }
2696
2697 /**
2698 * A method to be called when a node is about to be replaced in the tree.
2699 */
2700 void replacingNode(NodeImpl node) {
2701 }
2702
2703 /**
2704 * A method to be called when a node has been replaced in the tree.
2705 */
2706 void replacedNode(NodeImpl node) {
2707 }
2708
2709 /**
2710 * A method to be called when a character data node is about to be replaced
2711 */
2712 void replacingData(NodeImpl node) {
2713 }
2714
2715 /**
2716 * method to be called when a character data node has been replaced.
2717 */
2718 void replacedCharacterData(NodeImpl node, String oldvalue, String value) {
2719 }
2720
2721
2722 /**
2723 * A method to be called when an attribute value has been modified
2724 */
2725 void modifiedAttrValue(AttrImpl attr, String oldvalue) {
2726 }
2727
2728 /**
2729 * A method to be called when an attribute node has been set
2730 */
2731 void setAttrNode(AttrImpl attr, AttrImpl previous) {
2732 }
2733
2734 /**
2735 * A method to be called when an attribute node has been removed
2736 */
2737 void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
2738 }
2739
2740 /**
2741 * A method to be called when an attribute node has been renamed
2742 */
2743 void renamedAttrNode(Attr oldAt, Attr newAt) {
2744 }
2745
2746 /**
2747 * A method to be called when an element has been renamed
2748 */
2749 void renamedElement(Element oldEl, Element newEl) {
2750 }
2751
2752 /**
2753 * @serialData Serialized fields. Convert Maps to Hashtables for backward
2754 * compatibility.
2755 */
2756 private void writeObject(ObjectOutputStream out) throws IOException {
2757 // Convert Maps to Hashtables
2758 Hashtable<Node, Hashtable<String, UserDataRecord>> nud = null;
2759 if (nodeUserData != null) {
2760 nud = new Hashtable<>();
2761 for (Map.Entry<Node, Map<String, UserDataRecord>> e : nodeUserData.entrySet()) {
2762 //e.getValue() will not be null since an entry is always put with a non-null value
2763 nud.put(e.getKey(), new Hashtable<>(e.getValue()));
2764 }
2765 }
2766
2767 Hashtable<String, Node> ids = (identifiers == null)? null : new Hashtable<>(identifiers);
2768 Hashtable<Node, Integer> nt = (nodeTable == null)? null : new Hashtable<>(nodeTable);
2769
2770 // Write serialized fields
2771 ObjectOutputStream.PutField pf = out.putFields();
2772 pf.put("docType", docType);
2773 pf.put("docElement", docElement);
2774 pf.put("fFreeNLCache", fFreeNLCache);
2775 pf.put("encoding", encoding);
2776 pf.put("actualEncoding", actualEncoding);
2777 pf.put("version", version);
2778 pf.put("standalone", standalone);
2779 pf.put("fDocumentURI", fDocumentURI);
2780
2781 //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField
2782 pf.put("userData", nud);
2783 pf.put("identifiers", ids);
2784 pf.put("changes", changes);
2785 pf.put("allowGrammarAccess", allowGrammarAccess);
2786 pf.put("errorChecking", errorChecking);
2787 pf.put("ancestorChecking", ancestorChecking);
2788 pf.put("xmlVersionChanged", xmlVersionChanged);
2789 pf.put("documentNumber", documentNumber);
2790 pf.put("nodeCounter", nodeCounter);
2791 pf.put("nodeTable", nt);
2792 pf.put("xml11Version", xml11Version);
2793 out.writeFields();
2794 }
2795
2796 @SuppressWarnings("unchecked")
2797 private void readObject(ObjectInputStream in)
2798 throws IOException, ClassNotFoundException {
2799 // We have to read serialized fields first.
2800 ObjectInputStream.GetField gf = in.readFields();
2801 docType = (DocumentTypeImpl)gf.get("docType", null);
2802 docElement = (ElementImpl)gf.get("docElement", null);
2803 fFreeNLCache = (NodeListCache)gf.get("fFreeNLCache", null);
2804 encoding = (String)gf.get("encoding", null);
2805 actualEncoding = (String)gf.get("actualEncoding", null);
2806 version = (String)gf.get("version", null);
2807 standalone = gf.get("standalone", false);
2808 fDocumentURI = (String)gf.get("fDocumentURI", null);
2809
2810 //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField
2811 Hashtable<Node, Hashtable<String, UserDataRecord>> nud =
2812 (Hashtable<Node, Hashtable<String, UserDataRecord>>)gf.get("userData", null);
2813
2814 Hashtable<String, Node> ids = (Hashtable<String, Node>)gf.get("identifiers", null);
2815
2816 changes = gf.get("changes", 0);
2817 allowGrammarAccess = gf.get("allowGrammarAccess", false);
2818 errorChecking = gf.get("errorChecking", true);
2819 ancestorChecking = gf.get("ancestorChecking", true);
2820 xmlVersionChanged = gf.get("xmlVersionChanged", false);
2821 documentNumber = gf.get("documentNumber", 0);
2822 nodeCounter = gf.get("nodeCounter", 0);
2823
2824 Hashtable<Node, Integer> nt = (Hashtable<Node, Integer>)gf.get("nodeTable", null);
2825
2826 xml11Version = gf.get("xml11Version", false);
2827
2828 //convert Hashtables back to HashMaps
2829 if (nud != null) {
2830 nodeUserData = new HashMap<>();
2831 for (Map.Entry<Node, Hashtable<String, UserDataRecord>> e : nud.entrySet()) {
2832 nodeUserData.put(e.getKey(), new HashMap<>(e.getValue()));
2833 }
2834 }
2835
2836 if (ids != null) identifiers = new HashMap<>(ids);
2837 if (nt != null) nodeTable = new HashMap<>(nt);
2838 }
2839 } // class CoreDocumentImpl
--- EOF ---