001: /**
002: * org/ozone-db/xml/dom/DocumentImpl.java
003: *
004: * The contents of this file are subject to the OpenXML Public
005: * License Version 1.0; you may not use this file except in compliance
006: * with the License. You may obtain a copy of the License at
007: * http://www.openxml.org/license.html
008: *
009: * THIS SOFTWARE IS DISTRIBUTED ON AN "AS IS" BASIS WITHOUT WARRANTY
010: * OF ANY KIND, EITHER EXPRESSED OR IMPLIED. THE INITIAL DEVELOPER
011: * AND ALL CONTRIBUTORS SHALL NOT BE LIABLE FOR ANY DAMAGES AS A
012: * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
013: * DERIVATIVES. SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING
014: * RIGHTS AND LIMITATIONS UNDER THE LICENSE.
015: *
016: * The Initial Developer of this code under the License is Assaf Arkin.
017: * Portions created by Assaf Arkin are Copyright (C) 1998, 1999.
018: * All Rights Reserved.
019: */
020:
021: /**
022: * Changes for Persistent DOM running with ozone are
023: * Copyright 1999 by SMB GmbH. All rights reserved.
024: */package org.ozoneDB.xml.dom;
025:
026: import java.util.*;
027: import java.io.*;
028: import org.w3c.dom.*;
029: import org.ozoneDB.*;
030: import org.ozoneDB.xml.dom.*;
031: import org.ozoneDB.xml.util.XMLContainerHelper;
032:
033: /**
034: * Implements an XML document, and also derived to implement an HTML document.
035: * Provides access to the top level element in the document ({@link
036: * #getDocumentElement}), to the DTD if one exists ({@link #getDoctype},
037: * and to all node operations.
038: * <P>
039: * Several methods create new nodes of all basic types (comment, text, element,
040: * etc.). These methods create new nodes but do not place them in the document
041: * tree. The nodes may be placed in the document tree using {@link
042: * org.w3c.dom.Node#appendChild} or {@link org.w3c.dom.Node#insertBefore}, or
043: * they may be placed in some other document tree.
044: * <P>
045: * Notes:
046: * <OL>
047: * <LI>Node type is {@link org.w3c.dom.Node#DOCUMENT_NODE}
048: * <LI>Node supports childern
049: * <LI>Node name is always "#document"
050: * <LI>Node does not have a value
051: * <LI>Node may not be added to other nodes
052: * </OL>
053: * This class contains some extensions beyond the DOM API definition. For a list
054: * of all extensions, see {@link org.openxml.XMLDocument}.
055: *
056: *
057: * @version $Revision: 1.2 $ $Date: 2002/07/21 19:02:02 $
058: * @author <a href="mailto:arkin@trendline.co.il">Assaf Arkin</a>
059: * @see org.w3c.dom.Document
060: * @see NodeImpl
061: * @see org.w3c.dom.DOMImplementation
062: */
063: public class DocumentImpl extends NodeImpl implements DocumentProxy {
064:
065: final static long serialVersionUID = 1;
066:
067: private XMLContainerHelper container;
068:
069: public void writeExternal(ObjectOutput out) throws IOException {
070: super .writeExternal(out);
071: out.writeObject(container);
072: }
073:
074: public void readExternal(ObjectInput in) throws IOException,
075: ClassNotFoundException {
076: super .readExternal(in);
077: container = (XMLContainerHelper) in.readObject();
078: }
079:
080: /**
081: * Set the container of this document. This is needed to find the
082: * container that is responsible for a given Node.
083: */
084: public void setContainer(XMLContainerHelper _container) {
085: this .container = _container;
086: }
087:
088: /**
089: * Get the container of this document. This is needed to find the
090: * container that is responsible for a given Node.
091: */
092: public XMLContainerHelper getContainer() {
093: return this .container;
094: }
095:
096: public Node importNode(Node importedNode, boolean deep)
097: throws DOMException {
098: throw new DOMExceptionImpl(
099: DOMException.NOT_SUPPORTED_ERR,
100: "Document.importNode() : ozone's persistent DOM doesn't support DOM level 2 yet.");
101: }
102:
103: public Element createElementNS(java.lang.String namespaceURI,
104: java.lang.String qualifiedName) throws DOMException {
105: throw new DOMExceptionImpl(
106: DOMException.NOT_SUPPORTED_ERR,
107: "Document.createElementNS() : ozone's persistent DOM doesn't support DOM level 2 yet.");
108: }
109:
110: public Attr createAttributeNS(java.lang.String namespaceURI,
111: java.lang.String qualifiedName) throws DOMException {
112: throw new DOMExceptionImpl(
113: DOMException.NOT_SUPPORTED_ERR,
114: "Document.createAttributeNS() : ozone's persistent DOM doesn't support DOM level 2 yet.");
115: }
116:
117: public NodeList getElementsByTagNameNS(
118: java.lang.String namespaceURI, java.lang.String localName) {
119: throw new DOMExceptionImpl(
120: DOMException.NOT_SUPPORTED_ERR,
121: "Document.getElementsByTagNameNS() : ozone's persistent DOM doesn't support DOM level 2 yet.");
122: }
123:
124: public Element getElementById(java.lang.String elementId) {
125: throw new DOMExceptionImpl(
126: DOMException.NOT_SUPPORTED_ERR,
127: "Document.getElementById() : ozone's persistent DOM doesn't support DOM level 2 yet.");
128: }
129:
130: public Document createDocument(java.lang.String namespaceURI,
131: java.lang.String qualifiedName, DocumentType doctype)
132: throws DOMException {
133: throw new DOMExceptionImpl(
134: DOMException.NOT_SUPPORTED_ERR,
135: "Document.createDocument() : ozone's persistent DOM doesn't support DOM level 2 yet.");
136: }
137:
138: public DocumentType createDocumentType(
139: java.lang.String qualifiedName, java.lang.String publicId,
140: java.lang.String systemId) throws DOMException {
141: throw new DOMExceptionImpl(
142: DOMException.NOT_SUPPORTED_ERR,
143: "Document.createDocumentType() : ozone's persistent DOM doesn't support DOM level 2 yet.");
144: }
145:
146: public short getNodeType() {
147: return DOCUMENT_NODE;
148: }
149:
150: public final void setNodeValue(String value) {
151: throw new DOMExceptionImpl(DOMException.NO_DATA_ALLOWED_ERR,
152: "This node type does not support values.");
153: }
154:
155: public final DocumentType getDoctype() {
156: return (DocumentType) _docType;
157: }
158:
159: public final void setDoctype(DocumentType docType) {
160: _docType = (DocumentTypeProxy) docType;
161: }
162:
163: public final DOMImplementation getImplementation() {
164: return this ;
165: }
166:
167: public Element getDocumentElement() {
168: Node child;
169:
170: // Returns the top-level element in this document. Might not exist, or
171: // might be the first of many.
172: child = getFirstChild();
173: while (child != null) {
174: if (child instanceof Element) {
175: return (Element) child;
176: }
177: child = child.getNextSibling();
178: }
179: return null;
180: }
181:
182: public void setElementTypes(Hashtable elementTypes) {
183: _elementTypes = elementTypes;
184: }
185:
186: public Element createElement(String tagName) throws DOMException {
187: ElementProxy elem = null;
188: try {
189: elem = (ElementProxy) database().createObject(
190: ElementImpl.class.getName());
191: ((NodeProxy) elem).init(this , tagName, null, true);
192: } catch (Exception except) {
193: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
194: except.getMessage());
195: }
196: return elem;
197: }
198:
199: public final DocumentFragment createDocumentFragment() {
200: DocumentFragmentProxy fragment = null;
201: try {
202: fragment = (DocumentFragmentProxy) database().createObject(
203: DocumentFragmentImpl.class.getName());
204: fragment.init((DocumentProxy) this , null);
205: } catch (Exception except) {
206: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
207: except.getMessage());
208: }
209: return (DocumentFragment) fragment;
210: }
211:
212: public final Text createTextNode(String data) {
213: TextProxy text = null;
214: try {
215: text = (TextProxy) database().createObject(
216: TextImpl.class.getName());
217: text.init(this , data);
218: } catch (Exception except) {
219: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
220: except.getMessage());
221: }
222: return (Text) text;
223: }
224:
225: public final Comment createComment(String data) {
226: CommentProxy comment = null;
227: try {
228: comment = (CommentProxy) database().createObject(
229: CommentImpl.class.getName());
230: comment.init(this , data);
231: } catch (Exception except) {
232: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
233: except.getMessage());
234: }
235: return (Comment) comment;
236: }
237:
238: public final CDATASection createCDATASection(String data)
239: throws DOMException {
240: CDATASectionProxy section = null;
241: try {
242: section = (CDATASectionProxy) database().createObject(
243: CDATASectionImpl.class.getName());
244: section.init(this , data);
245: } catch (Exception except) {
246: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
247: except.getMessage());
248: }
249: return (CDATASection) section;
250: }
251:
252: public final ProcessingInstruction createProcessingInstruction(
253: String target, String data) throws DOMException {
254: ProcessingInstructionProxy pi = null;
255: try {
256: pi = (ProcessingInstructionProxy) database().createObject(
257: ProcessingInstructionImpl.class.getName());
258: pi.init(this , target, data);
259: } catch (Exception except) {
260: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
261: except.getMessage());
262: }
263: return (ProcessingInstruction) pi;
264: }
265:
266: public final Attr createAttribute(String name) throws DOMException {
267: AttrProxy attr = null;
268: try {
269: attr = (AttrProxy) database().createObject(
270: AttrImpl.class.getName());
271: attr.init(this , name, "");
272: } catch (Exception except) {
273: except.printStackTrace();
274: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
275: except.getMessage());
276: }
277: return attr;
278: }
279:
280: /**
281: * Creates an attribute with the default value specified in the DTD.
282: * This method is not defined in the DOM but is used by the parser.
283: *
284: * @param name The name of the attribute
285: * @param defValue The default value of the attribute
286: */
287: public final Attr createAttribute(String name, String defValue)
288: throws DOMException {
289: AttrProxy attr = null;
290: try {
291: attr = (AttrProxy) database().createObject(
292: AttrImpl.class.getName());
293: attr.init(this , name, defValue == null ? "" : defValue);
294: } catch (Exception except) {
295: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
296: except.getMessage());
297: }
298: return (Attr) attr;
299: }
300:
301: public final EntityReference createEntityReference(String name)
302: throws DOMException {
303: EntityReferenceProxy entity = null;
304: try {
305: entity = (EntityReferenceProxy) database().createObject(
306: EntityReferenceImpl.class.getName());
307: // entity.init (this, name);
308: } catch (Exception except) {
309: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
310: except.getMessage());
311: }
312: return (EntityReference) entity;
313: }
314:
315: /**
316: * Creates a document type with the specified name (the name follows the
317: * <TT>!DOCTYPE</TT> entity) and associates it with the document.
318: * If the document is HTML or already has a DTD associated with it, throws
319: * an exception. This method is not defined in the DOM but is used by the
320: * parser.
321: *
322: * @param name The name of the document type
323: * @param name The public identifier, if exists
324: * @param name The system identifier, if exists
325: * @throws org.w3c.dom.DOMException <T>NOT_SUPPORTED_ERR</TT>
326: * Document is HTML or already has a DTD
327: */
328: /*
329: public final DocumentType createDocumentType (String name, String publicId,
330: String systemId)
331: throws DOMException {
332: if (isHtmlDoc () || _docType != null)
333: throw new DOMExceptionImpl (DOMException.NOT_SUPPORTED_ERR);
334: _docType = new DocumentTypeImpl( this, name );
335: return _docType;
336: }
337: */
338:
339: public final NodeList getElementsByTagName(String tagName) {
340: return (NodeList) new org.ozoneDB.xml.dom.ElementListImpl(this ,
341: tagName);
342: }
343:
344: /**
345: * Return true if certain feature for specific DOM version supported by this
346: * implementation.
347: *
348: * @param feature Name of feature to check
349: * @param version Optional version number
350: * @return True if supported
351: */
352: public boolean hasFeature(String feature, String version) {
353: // Versions higher than 1.0 not supported yet.
354: if (version != null) {
355: if (version.indexOf("1.0") < 0
356: && version.indexOf("Level 1") < 0) {
357: return false;
358: }
359: }
360: // Supports XML and HTML.
361: if (feature != null) {
362: if (feature.equalsIgnoreCase("XML")
363: || feature.equalsIgnoreCase("HTML")) {
364: return true;
365: }
366: }
367: return false;
368: }
369:
370: /**
371: * Register an application-defined element type. The element's tag name and
372: * class are specified. When a so named element is created with {@link
373: * #createElement}, an object of the specified class is created and returned.
374: * This allows applications to define classes for specific element types.
375: *
376: * @param tagName The name of the element tag
377: * @param elementClass Class derived from {@link org.openxml.XMLElement},
378: * used to construct the element
379: */
380: public void registerElement(String tagName, Class elementClass) {
381: if (tagName == null || tagName.length() == 0) {
382: throw new NullPointerException(
383: "Argument 'tagName' is null or an empty string.");
384: }
385: if (elementClass == null
386: || elementClass.isAssignableFrom(Element.class)) {
387: throw new IllegalArgumentException(
388: "Argument 'elementClass' is null or does not extend Element.");
389: }
390: if (_elementTypes == null) {
391: _elementTypes = new Hashtable();
392: }
393: _elementTypes.put(tagName, elementClass);
394: }
395:
396: public void assignDoctype(DocumentTypeProxy docType) {
397: if (docType == null) {
398: throw new NullPointerException(
399: "Argument 'docType' is null.");
400: }
401: if (_docType != null) {
402: throw new IllegalStateException(
403: "Document type already assigned to this document.");
404: }
405: _docType = docType;
406: }
407:
408: /**
409: * Implements a lock mechanism. The lock mechanism differs from synchronization
410: * in that it supports multiple function calls, while still preserving the lock
411: * granted to a specific thread. Caution is required as other threads may obtain
412: * access without first acquiring lock. All implementations of the lock mechanism
413: * should use {@link #lock}, {@link #unlock} and {@link #acquire} to assure true
414: * synchronization.
415: * <P>
416: * Calling {@link #lock} obtains a lock to the current running thread. If the
417: * lock has already been granted to some other thread, this call will block
418: * until the lock is released or until the lock has timed out.
419: * <P>
420: * Calling {@link #unlock} releases the lock from the current thread, if still
421: * held by the current thread. A thread may call {@link #lock} any number of
422: * times and must call {@link #unlock} that number of times to release the
423: * lock. The thread may call {@link #unlock} an additional number of times if
424: * necessary by the implementation.
425: * <P>
426: * If a thread requires access to a resource that might be lock, it should
427: * acquire it by calling {@link #acquire}. This method will block until the
428: * resource is unlocked by some other thread. If the resource is unlocked,
429: * or locked by the current thread, {@link #acquire} will return immediately.
430: */
431:
432: /**
433: * Obtains a lock, preventing other threads from gaining access to the locked
434: * resource. The lock is retained across multiple calls, until {@link #unlock}
435: * is called. Attempts to call {@link #acquire} from another thread will be
436: * blocked.
437: * <P>
438: * If the resource is already locked by another thread, the method will
439: * block until the lock is released or until the block has timedout.
440: * <P>
441: * {@link #lock} may be called any number of times, and {@link #unlock} must
442: * be called that number of times to release the lock.
443: *
444: * @see #unlock
445: */
446: public void lock() throws RuntimeException {
447: synchronized (this ) {
448: // If current thread is locking, increase the lock count.
449: // Otherwise, wait for lock to be released, acquire the lock and
450: // increase the lock count. If timeout has reached or thread has
451: // been interrupted, acquire() will throw an exception.
452: if (_lockThread == Thread.currentThread()) {
453: ++_lockCount;
454: } else {
455: acquire(Long.MAX_VALUE);
456: _lockThread = Thread.currentThread();
457: ++_lockCount;
458: }
459: }
460: }
461:
462: /**
463: * Releases a lock, so other thread may gain access to the resource. {@link
464: * #unlock} must be called as many times as {@link #lock} was called to
465: * release the lock. {@link #unlock} may be called an additional number of
466: * times, if so required by the implementation (e.g. to assure that a lock
467: * is released at the end of a thread).
468: *
469: * @see #lock
470: */
471: public void unlock() {
472: // If the current thread is locking, decrease the lock count.
473: // If the lock count is zero, release the lock, otherwise unlock()
474: // must be called again to release lock.
475: if (_lockThread == Thread.currentThread()) {
476: synchronized (this ) {
477: --_lockCount;
478: if (_lockCount == 0) {
479: _lockThread = null;
480: notify();
481: }
482: }
483: }
484: }
485:
486: /**
487: * Called to acquire access to a possible locked resource. If the resource
488: * was locked by the current thread, the method will return immediately.
489: * If the resource was locked by some other thread, the method will block
490: * until the resource is unlocked, or the block has timed out.
491: *
492: * @see #lock
493: * @see #unlock
494: */
495: public void acquire(long lockTimeout) throws RuntimeException {
496: long start;
497: long timeout;
498:
499: synchronized (this ) {
500: try {
501: // If the application is not locked by the current thread,
502: // wait until the application is unlocked, then proceed.
503: if (_lockThread != Thread.currentThread()
504: && _lockThread != null) {
505: // Remember when started waiting for lock to implement
506: // timeout. Repeat until lock is released by other thread,
507: // or until timeout has expired.
508: start = System.currentTimeMillis();
509: while (_lockThread != null
510: && lockTimeout > (System
511: .currentTimeMillis() - start)) {
512: // If the locking thread is dead, release the lock
513: // immediately, otherwise, wait for it to be released.
514: if (!_lockThread.isAlive()) {
515: _lockThread = null;
516: } else {
517: wait(lockTimeout
518: - System.currentTimeMillis()
519: - start);
520: }
521: }
522: // Timeout, throw an exception.
523: if (_lockThread != null) {
524: throw new RuntimeException(
525: "Timeout waiting for lock to be released.");
526: }
527: }
528: } catch (InterruptedException except) {
529: // Thread interrupted, throw an exception.
530: throw new RuntimeException(
531: "Timeout waiting for lock to be released.");
532: }
533: }
534: }
535:
536: public synchronized boolean equals(Object other) {
537: DocumentProxy otherX;
538:
539: // Use Node's equals method to perform the first tests of equality.
540: // If these tests do not pass, return false.
541: if (!super .equals(other)) {
542: return false;
543: }
544:
545: // Very simple equality test: are the document types equal.
546: // There's nothing else about the document to compare.
547: synchronized (other) {
548: otherX = (DocumentProxy) other;
549: return getDoctype() == null && otherX.getDoctype() == null
550: || getDoctype() != null
551: && otherX.getDoctype() != null
552: && _docType.equals(otherX.getDoctype());
553: }
554: }
555:
556: public Object clone() {
557: DocumentProxy clone = null;
558: try {
559: clone = (DocumentProxy) database().createObject(
560: DocumentImpl.class.getName());
561: cloneInto((NodeProxy) clone, true);
562: } catch (Exception except) {
563: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
564: except.getMessage());
565: }
566: return clone;
567: }
568:
569: public Node cloneNode(boolean deep) {
570: DocumentProxy clone = null;
571: try {
572: clone = (DocumentProxy) database().createObject(
573: DocumentImpl.class.getName());
574: cloneInto((NodeProxy) clone, deep);
575: } catch (Exception except) {
576: throw new DOMExceptionImpl(DOMExceptionImpl.PDOM_ERR,
577: except.getMessage());
578: }
579: return clone;
580: }
581:
582: public String toString() {
583: return "Document: ";
584: }
585:
586: public synchronized void cloneInto(NodeProxy into, boolean deep) {
587: DocumentTypeProxy docType;
588:
589: // Use the Node cloning method. The DOM object does not need cloning,
590: // but the document type (which contains entities, notations, etc)
591: // must be cloned.
592: super .cloneInto(into, deep);
593: if (deep) {
594: if (_docType != null) {
595: ((DocumentProxy) into)
596: .setDoctype((DocumentTypeProxy) _docType
597: .clone());
598: }
599: // If application elements are defined, clone the list as well.
600: if (_elementTypes != null) {
601: ((DocumentProxy) into)
602: .setElementTypes((Hashtable) _elementTypes
603: .clone());
604: }
605: } else {
606: ((DocumentProxy) into).setDoctype(_docType);
607: ((DocumentProxy) into).setElementTypes(_elementTypes);
608: }
609: // ((DocumentProxy)into).setElementFactory (_elementFactory);
610: }
611:
612: protected final boolean supportsChildern() {
613: return true;
614: }
615:
616: public DocumentImpl() {
617: super (null, "#document", null, false);
618: _ownerDocument = this ;
619: }
620:
621: protected DocumentImpl(String rootElement) {
622: super (null, rootElement != null ? rootElement : "", null,
623: rootElement != null);
624: _ownerDocument = this ;
625: }
626:
627: public void onDelete() throws Exception {
628: clearDocument();
629: }
630:
631: private void deleteAllChildern(Node node) throws Exception {
632: Node dummyNode;
633: while (node != null) {
634: if (node.hasChildNodes()) {
635: deleteAllChildern(node.getFirstChild());
636: }
637: dummyNode = node.getNextSibling();
638: System.out.println(node.getNodeName());
639: database().deleteObject((OzoneRemote) node);
640: node = dummyNode;
641: }
642: }
643:
644: /**
645: * Deletes all child Nodes from this Document to provide a fresh and
646: * empty Document. Attention: This methods permanently removes the
647: * existing content!
648: */
649: public void clearDocument() throws Exception {
650: // System.out.println( "clearDocument() " );
651: if (_docType != null) {
652: database().deleteObject(_docType);
653: }
654: Node node = getFirstChild();
655: while (node != null) {
656: deleteAllChildern(node);
657: node = getNextSibling();
658: }
659: }
660:
661: /**
662: * The document type definition of this document (per DOM API). Only available
663: * if document was created with a DTD and not available for HTML documents.
664: */
665: private DocumentTypeProxy _docType;
666:
667: /**
668: * Holds a reference to the locking thread. When unlocked, this reference is
669: * null. This object is used both to designate a lock, identify the locking
670: * thread, and perform a {@link Object#wait}.
671: *
672: * @see #lock
673: * @see #unlock
674: */
675: private Thread _lockThread;
676:
677: /**
678: * Implement counting on locks, allowing {@link #lock} to be called a
679: * multiple number of times, and {@link #unlock} to still retain the lock
680: * on the outer calls.
681: *
682: * @see #lock
683: * @see #unlock
684: */
685: private int _lockCount;
686:
687: /**
688: * Holds names and classes of application element types. When an element
689: * with a particular tag name is created, the matching {@link java.lang.Class}
690: * is used to create the element object. This reference is null unless an
691: * element has been defined.
692: */
693: private Hashtable _elementTypes;
694: }
|