KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nu > xom > Document


1 /* Copyright 2002-2004 Elliotte Rusty Harold
2    
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.
6    
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.
11    
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
16    
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/
20 */

21
22 package nu.xom;
23
24 /**
25  * <p>
26  * The <code>Document</code> class represents
27  * a complete XML document including its root element,
28  * prolog, and epilog.
29  * </p>
30  *
31  * @author Elliotte Rusty Harold
32  * @version 1.0
33  *
34  */

35 public class Document extends ParentNode {
36
37     /**
38      * <p>
39      * Creates a new <code>Document</code> object with the
40      * specified root element.
41      * </p>
42      *
43      * @param root the root element of this document
44      *
45      * @throws NullPointerException if <code>root</code> is null
46      * @throws MultipleParentException if <code>root</code> already
47      * has a parent
48      */

49     public Document(Element root) {
50         _insertChild(root, 0);
51     }
52
53     
54     /**
55      * <p>
56      * Creates a copy of this document.
57      * </p>
58      *
59      * @param doc the document to copy
60      *
61      * @throws NullPointerException if <code>doc</code> is null
62      */

63     public Document(Document doc) {
64
65       insertChild(doc.getRootElement().copy(), 0);
66       for (int i = 0; i < doc.getChildCount(); i++) {
67           Node child = doc.getChild(i);
68           if (!(child.isElement())) {
69               this.insertChild(child.copy(), i);
70           }
71       }
72       this.actualBaseURI = doc.actualBaseURI;
73
74     }
75
76
77     final void insertionAllowed(Node child, int position) {
78         
79         if (child == null) {
80             throw new NullPointerException JavaDoc(
81              "Tried to insert a null child in the document");
82         }
83         else if (child.getParent() != null) {
84             throw new MultipleParentException("Child already has a parent.");
85         }
86         else if (child.isComment() || child.isProcessingInstruction()) {
87             return;
88         }
89         else if (child.isDocType()) {
90             if (position <= getRootPosition()) {
91                 DocType oldDocType = getDocType();
92                 if (oldDocType != null) {
93                     throw new IllegalAddException(
94                       "Tried to insert a second DOCTYPE"
95                     );
96                 }
97                 return;
98             }
99             else {
100                 throw new IllegalAddException(
101                   "Cannot add a document type declaration "
102                   + "after the root element"
103                 );
104             }
105         }
106         else if (child.isElement()) {
107             if (getChildCount() == 0) return;
108             else {
109                 throw new IllegalAddException(
110                   "Cannot add a second root element to a Document."
111                 );
112             }
113         }
114         else {
115             throw new IllegalAddException("Cannot add a "
116              + child.getClass().getName() + " to a Document.");
117         }
118
119     }
120     
121
122     private int getRootPosition() {
123         
124         // This looks like an infinite loop but it isn't
125
// because all documents have root elements
126
for (int i = 0; ; i++) {
127              Node child = getChild(i);
128              if (child.isElement()) {
129                 return i;
130              }
131          }
132         
133     }
134     
135     
136     /**
137      * <p>
138      * Returns this document's document type declaration,
139      * or null if it doesn't have one.
140      * </p>
141      *
142      * @return the document type declaration
143      *
144      * @see #setDocType
145      *
146      */

147     public final DocType getDocType() {
148         
149         for (int i = 0; i < getChildCount(); i++) {
150              Node child = getChild(i);
151              if (child.isDocType()) {
152                 return (DocType) child;
153              }
154          }
155          return null;
156          
157     }
158
159     
160     /**
161      * <p>
162      * Sets this document's document type declaration.
163      * If this document already has a document type declaration,
164      * then it's inserted at that position. Otherwise, it's inserted
165      * at the beginning of the document.
166      * </p>
167      *
168      * @param doctype the document type declaration
169      *
170      * @throws MultipleParentException if <code>doctype</code> belongs
171      * to another document
172      * @throws NullPointerException if <code>doctype</code> is null
173      *
174      */

175     public void setDocType(DocType doctype) {
176         
177         DocType oldDocType = getDocType();
178         if (doctype == null) {
179             throw new NullPointerException JavaDoc("Null DocType");
180         }
181         else if (doctype == oldDocType) return;
182         else if (doctype.getParent() != null) {
183             throw new MultipleParentException("DocType belongs to another document");
184         }
185         
186         if (oldDocType == null) insertChild(doctype, 0);
187         else {
188             int position = indexOf(oldDocType);
189             children.remove(position);
190             children.add(position, doctype);
191             oldDocType.setParent(null);
192             doctype.setParent(this);
193         }
194         
195     }
196
197
198     /**
199      * <p>
200      * Returns this document's root element.
201      * This is guaranteed to be non-null.
202      * </p>
203      *
204      * @return the root element
205      */

206     public final Element getRootElement() {
207         
208         // This looks like an infinite loop but it isn't because
209
// all documents have root elements.
210
for (int i = 0; ; i++) {
211              Node child = getChild(i);
212              if (child.isElement()) {
213                 return (Element) child;
214              }
215          }
216         
217     }
218
219     
220     /**
221      * <p>
222      * Replaces the current root element with a different root element.
223      * </p>
224      *
225      * @param root the new root element
226      *
227      * @throws MultipleParentException if root has a parent
228      * @throws NullPointerException if root is null
229      */

230     public void setRootElement(Element root) {
231         
232         Element oldRoot = this.getRootElement();
233         if (root == oldRoot) return;
234         else if (root == null) {
235             throw new NullPointerException JavaDoc("Root element cannot be null");
236         }
237         else if (root.getParent() != null) {
238             throw new MultipleParentException(root.getQualifiedName()
239               + " already has a parent");
240         }
241         
242         fillInBaseURI(oldRoot);
243         int index = indexOf(oldRoot);
244         
245         oldRoot.setParent(null);
246         children.remove(index);
247         children.add(index, root);
248         root.setParent(this);
249         
250     }
251     
252     
253     /**
254      * <p>
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
258      * existing base URI.
259      * </p>
260      *
261      * @param URI the base URI of this document
262      *
263      * @throws MalformedURIException if <code>URI</code> is
264      * not a legal absolute URI
265      */

266     public void setBaseURI(String JavaDoc URI) {
267         setActualBaseURI(URI);
268     }
269     
270     
271     /**
272      * <p>
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.
277      * </p>
278      *
279      * @return the base URI of this document
280      */

281     public final String JavaDoc getBaseURI() {
282         return getActualBaseURI();
283     }
284
285     
286     /**
287      * <p>
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.
293      * </p>
294      *
295      * @param position index of the node to remove
296      *
297      * @return the node which was removed
298      *
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
303      */

304     public Node removeChild(int position) {
305         
306         if (position == getRootPosition()) {
307             throw new WellformednessException(
308               "Cannot remove the root element"
309             );
310         }
311         return super.removeChild(position);
312         
313     }
314
315     
316     /**
317      * <p>
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.
322      * </p>
323      *
324      * @param child node to remove
325      *
326      * @return the node which was removed
327      *
328      * @throws NoSuchChildException if the node is not a
329      * child of this node
330      * @throws WellformednessException if child is the root element
331      */

332     public Node removeChild(Node child) {
333         
334         if (child == getRootElement()) {
335             throw new WellformednessException(
336               "Cannot remove the root element");
337         }
338         return super.removeChild(child);
339         
340     }
341
342     
343     /**
344      * <p>
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.
349      * </p>
350      *
351      * @param oldChild the node removed from the tree
352      * @param newChild the node inserted into the tree
353      *
354      * @throws MultipleParentException if <code>newChild</code> already
355      * has a parent
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
364      */

365     public void replaceChild(Node oldChild, Node newChild) {
366           
367         if (oldChild == getRootElement()
368           && newChild != null && newChild.isElement()) {
369             setRootElement((Element) newChild);
370         }
371         else if (oldChild == getDocType()
372           && newChild != null && newChild.isDocType()) {
373             setDocType((DocType) newChild);
374         }
375         else {
376             super.replaceChild(oldChild, newChild);
377         }
378         
379     }
380
381
382     /**
383      * <p>
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.
389      * </p>
390      *
391      * @return value of the root element of this document
392      *
393      */

394     public final String JavaDoc getValue() {
395         return getRootElement().getValue();
396     }
397
398     
399     /**
400      * <p>
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.
407      * </p>
408      *
409      * @return a string containing this entire XML document
410      */

411     public final String JavaDoc toXML() {
412     
413         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
414
415         // XML declaration
416
result.append("<?xml version=\"1.0\"?>\n");
417         
418         // children
419
for (int i = 0; i < getChildCount(); i++) {
420             result.append(getChild(i).toXML());
421             result.append("\n");
422         }
423         
424         return result.toString();
425         
426     }
427
428     
429     /**
430      * <p>
431      * Returns a complete copy of this document.
432      * </p>
433      *
434      * @return a deep copy of this <code>Document</code> object
435      */

436     public Node copy() {
437         return new Document(this);
438     }
439
440     
441     boolean isDocument() {
442         return true;
443     }
444
445     
446     /**
447      * <p>
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.
451      * </p>
452      *
453      * @return a non-XML string representation of this document
454      */

455     public final String JavaDoc toString() {
456         return "[" + getClass().getName() + ": "
457           + getRootElement().getQualifiedName() + "]";
458     }
459
460     
461 }
Popular Tags