1
/* Copyright 2002-2004 Elliotte Rusty Harold
3
This library is free software; you can redistribute it and/or modify
4
it under the terms of version 2.1 of the GNU Lesser General Public
5
License as published by the Free Software Foundation.
7
This library is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU Lesser General Public License for more details.
12
You should have received a copy of the GNU Lesser General Public
13
License along with this library; if not, write to the
14
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15
Boston, MA 02111-1307 USA
17
You can contact Elliotte Rusty Harold by sending e-mail to
18
elharo@metalab.unc.edu. Please include the word "XOM" in the
19
subject line. The XOM home page is located at http://www.xom.nu/
26
* The <code>Document</code> class represents
27
* a complete XML document including its root element,
31
* @author Elliotte Rusty Harold
35
public class Document extends ParentNode {
39
* Creates a new <code>Document</code> object with the
40
* specified root element.
43
* @param root the root element of this document
45
* @throws NullPointerException if <code>root</code> is null
46
* @throws MultipleParentException if <code>root</code> already
49
public Document(Element root) {
50
_insertChild(root, 0);
56
* Creates a copy of this document.
59
* @param doc the document to copy
61
* @throws NullPointerException if <code>doc</code> is null
63
public Document(Document doc) {
65
insertChild(doc.getRootElement().copy(), 0);
66
int count = doc.getChildCount();
67
for (int i = 0; i < count; i++) {
68
Node child = doc.getChild(i);
69
if (!(child.isElement())) {
70
this.insertChild(child.copy(), i);
73
this.actualBaseURI = doc.actualBaseURI;
78
final void insertionAllowed(Node child, int position) {
81
throw new NullPointerException(
82
"Tried to insert a null child in the document");
84
else if (child.getParent() != null) {
85
throw new MultipleParentException("Child already has a parent.");
87
else if (child.isComment() || child.isProcessingInstruction()) {
90
else if (child.isDocType()) {
91
if (position <= getRootPosition()) {
92
DocType oldDocType = getDocType();
93
if (oldDocType != null) {
94
throw new IllegalAddException(
95
"Tried to insert a second DOCTYPE"
101
throw new IllegalAddException(
102
"Cannot add a document type declaration "
103
+ "after the root element"
107
else if (child.isElement()) {
108
if (getChildCount() == 0) return;
110
throw new IllegalAddException(
111
"Cannot add a second root element to a Document."
116
throw new IllegalAddException("Cannot add a "
117
+ child.getClass().getName() + " to a Document.");
123
private int getRootPosition() {
125
// This looks like an infinite loop but it isn't
126
// because all documents have root elements
127
for (int i = 0; ; i++) {
128
Node child = getChild(i);
129
if (child.isElement()) {
139
* Returns this document's document type declaration,
140
* or null if it doesn't have one.
143
* @return the document type declaration
148
public final DocType getDocType() {
150
for (int i = 0; i < getChildCount(); i++) {
151
Node child = getChild(i);
152
if (child.isDocType()) {
153
return (DocType) child;
163
* Sets this document's document type declaration.
164
* If this document already has a document type declaration,
165
* then it's inserted at that position. Otherwise, it's inserted
166
* at the beginning of the document.
169
* @param doctype the document type declaration
171
* @throws MultipleParentException if <code>doctype</code> belongs
172
* to another document
173
* @throws NullPointerException if <code>doctype</code> is null
176
public void setDocType(DocType doctype) {
178
DocType oldDocType = getDocType();
179
if (doctype == null) {
180
throw new NullPointerException("Null DocType");
182
else if (doctype == oldDocType) return;
183
else if (doctype.getParent() != null) {
184
throw new MultipleParentException("DocType belongs to another document");
187
if (oldDocType == null) insertChild(doctype, 0);
189
int position = indexOf(oldDocType);
190
super.removeChild(position);
191
fastInsertChild(doctype, position);
192
oldDocType.setParent(null);
193
doctype.setParent(this);
201
* Returns this document's root element.
202
* This is guaranteed to be non-null.
205
* @return the root element
207
public final Element getRootElement() {
209
// This looks like an infinite loop but it isn't because
210
// all documents have root elements.
211
for (int i = 0; ; i++) {
212
Node child = getChild(i);
213
if (child.isElement()) {
214
return (Element) child;
223
* Replaces the current root element with a different root element.
226
* @param root the new root element
228
* @throws MultipleParentException if root has a parent
229
* @throws NullPointerException if root is null
231
public void setRootElement(Element root) {
233
Element oldRoot = this.getRootElement();
234
if (root == oldRoot) return;
235
else if (root == null) {
236
throw new NullPointerException("Root element cannot be null");
238
else if (root.getParent() != null) {
239
throw new MultipleParentException(root.getQualifiedName()
240
+ " already has a parent");
243
fillInBaseURI(oldRoot);
244
int index = indexOf(oldRoot);
246
oldRoot.setParent(null);
247
children[index] = root;
248
root.setParent(this);
255
* Sets the URI from which this document was loaded, and
256
* against which relative URLs in this document will be resolved.
257
* Setting the base URI to null or the empty string removes any
261
* @param URI the base URI of this document
263
* @throws MalformedURIException if <code>URI</code> is
264
* not a legal absolute URI
266
public void setBaseURI(String URI) {
267
setActualBaseURI(URI);
273
* Returns the absolute URI from which this document was loaded.
274
* This method returns the empty string if the base URI is not
275
* known; for instance if the document was created in memory with
276
* a constructor rather than by parsing an existing document.
279
* @return the base URI of this document
281
public final String getBaseURI() {
282
return getActualBaseURI();
288
* Removes the child of this document at the specified position.
289
* Indexes begin at 0 and count up to one less than the number
290
* of children of this document. The root element cannot be
291
* removed. Instead, use <code>setRootElement</code> to replace
292
* the existing root element with a different element.
295
* @param position index of the node to remove
297
* @return the node which was removed
299
* @throws IndexOutOfBoundsException if the index is negative or
300
* greater than the number of children of this document - 1
301
* @throws WellformednessException if the index points
302
* to the root element
304
public Node removeChild(int position) {
306
if (position == getRootPosition()) {
307
throw new WellformednessException(
308
"Cannot remove the root element"
311
return super.removeChild(position);
318
* Removes the specified child from this document.
319
* The root element cannot be removed.
320
* Instead, use <code>setRootElement</code> to replace the
321
* existing root element with a different element.
324
* @param child node to remove
326
* @return the node which was removed
328
* @throws NoSuchChildException if the node is not a
330
* @throws WellformednessException if child is the root element
332
public Node removeChild(Node child) {
334
if (child == getRootElement()) {
335
throw new WellformednessException(
336
"Cannot remove the root element");
338
return super.removeChild(child);
345
* Replaces an existing child with a new child node.
346
* If <code>oldChild</code> is not a child of this node,
347
* then a <code>NoSuchChildException</code> is thrown.
348
* The root element can only be replaced by another element.
351
* @param oldChild the node removed from the tree
352
* @param newChild the node inserted into the tree
354
* @throws MultipleParentException if <code>newChild</code> already
356
* @throws NoSuchChildException if <code>oldChild</code>
357
* is not a child of this node
358
* @throws NullPointerException if either argument is null
359
* @throws IllegalAddException if <code>newChild</code> is an
360
* attribute or a text node
361
* @throws WellformednessException if <code>newChild</code>
362
* <code>oldChild</code> is an element and
363
* <code>newChild</code> is not
365
public void replaceChild(Node oldChild, Node newChild) {
367
if (oldChild == getRootElement()
368
&& newChild != null && newChild.isElement()) {
369
setRootElement((Element) newChild);
371
else if (oldChild == getDocType()
372
&& newChild != null && newChild.isDocType()) {
373
setDocType((DocType) newChild);
376
super.replaceChild(oldChild, newChild);
384
* Returns the value of the document as defined by XPath 1.0.
385
* This is the same as the value of the root element, which
386
* is the complete PCDATA content of the root element, without
387
* any tags, comments, or processing instructions after all
388
* entity and character references have been resolved.
391
* @return value of the root element of this document
394
public final String getValue() {
395
return getRootElement().getValue();
401
* Returns the actual complete, well-formed XML document as a
402
* <code>String</code>. Significant white space is preserved.
403
* Insignificant white space in tags, the prolog, the epilog,
404
* and the internal DTD subset is not preserved.
405
* Entity and character references are not preserved.
406
* The entire document is contained in this one string.
409
* @return a string containing this entire XML document
411
public final String toXML() {
413
StringBuffer result = new StringBuffer();
416
result.append("<?xml version=\"1.0\"?>\n");
419
for (int i = 0; i < getChildCount(); i++) {
420
result.append(getChild(i).toXML());
424
return result.toString();
431
* Returns a complete copy of this document.
434
* @return a deep copy of this <code>Document</code> object
437
return new Document(this);
441
boolean isDocument() {
448
* Returns a string representation of this document suitable
449
* for debugging and diagnosis. This is <em>not</em>
450
* the XML representation of this document.
453
* @return a non-XML string representation of this document
455
public final String toString() {
456
return "[" + getClass().getName() + ": "
457
+ getRootElement().getQualifiedName() + "]";
b'\\ No newline at end of file'