001: /**
002: * org/ozone-db/xml/DOMFactory.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 softwarebuero m&b (SMB). All rights reserved.
024: */package org.ozoneDB.xml;
025:
026: import java.lang.reflect.*;
027: import java.io.*;
028: import java.util.*;
029: import org.w3c.dom.Document;
030: import org.w3c.dom.DocumentType;
031: import org.w3c.dom.html.HTMLDocument;
032: import org.ozoneDB.xml.dom.html.HTMLDocumentImpl;
033: import org.openxml.parser.*;
034: import org.openxml.io.*;
035: import org.openxml.source.*;
036: import org.openxml.source.holders.*;
037: import org.openxml.util.Log;
038:
039: /**
040: * Factory for XML, HTML and DTD documents, parsers and printers. The factory
041: * has methods for creating new documents, parsers and printers. The exact
042: * type is determined by the document class, this might be {@link Document}
043: * ({@link #DOCUMENT_XML}), {@link HTMLDocument} ({@link #DOCUMENT_HTML}),
044: * {@link DocumentType} ({@link #DOCUMENT_DTD}) or a user document derived from
045: * {@link XMLDocument}.
046: * <P>
047: * The default document type is controlled by the <TT>openxml.document.class</TT>
048: * propety in the OpenXML properties file ({@link <A HREF="properties.html">
049: * openxml.prop</A>}). The parser and printer classes for XML, HTML and DTD
050: * documents are also controlled by the property file.
051: * <P>
052: * The method {@link #createDocument} does not guarantee that it will return
053: * {@link Document}, although this is the default behavior. To obtain a
054: * {@link Document} either pass its class as argument, or call {@link
055: * #createXMLDocument}.
056: * <P>
057: * A newly created parser is only guaranteed to extend {@link Parser}, even
058: * if {@link #DOCUMENT_XML} has been specified as the document type. To create
059: * a document from a user class, either use {@link Source}, or the following
060: * code:
061: * <PRE>
062: * Parser parser;
063: *
064: * parser = DOMFactory.createParser( reader, sourceURI, docClass );
065: * if ( parser instanceof XMLParser )
066: * doc = ( (XMLParser) parser ).parseDocument( null, docClass );
067: * else
068: * doc = parser.parseDocument();
069: * </PRE>
070: *
071: *
072: * @version $Revision: 1.1 $ $Date: 2001/12/18 11:03:24 $
073: * @author <a href="mailto:arkin@trendline.co.il">Assaf Arkin</a>
074: * @see org.w3c.dom.Document
075: * @see XMLElement
076: * @see XMLCollection
077: */
078: public class DOMFactory extends java.lang.Object {
079:
080: /**
081: * Creates and returns a new XML document. The document type is {@link
082: * Document}.
083: *
084: * @return A new XML document
085: * @see Document
086: */
087: public static Document createXMLDocument() {
088: return (Document) createDocument(DOCUMENT_XML);
089: }
090:
091: /**
092: * Creates and returns a new HTML document. The document type is
093: * {@link HTMLDocument}.
094: *
095: * @return A new XML document
096: * @see HTMLDocument
097: */
098: public static HTMLDocument createHTMLDocument() {
099: return (HTMLDocument) createDocument(DOCUMENT_HTML);
100: }
101:
102: /**
103: * Creates and returns a new DTD document. The document type is
104: * {@link DTDDocument}.
105: *
106: * @return A new DTD document
107: * @see DTDDocument
108: */
109: public static DTDDocument createDTDDocument() {
110: return (DTDDocument) createDocument(DOCUMENT_DTD);
111: }
112:
113: /**
114: * Creates and returns a new XML/HTML/DTD document. The document type is
115: * based on <TT>docClass</TT>, which dictates whether the document is XML,
116: * HTML or DTD. If <TT>docClass</TT> is null, the class type is read from
117: * the property <TT>openxml.document.class</TT>, and if that property is
118: * missing, the default {@link Document} is used.
119: * <P>
120: * Note that the returned document type may or may not be {@link Document},
121: * but it must extend {@link Document}, and that is also true for non-XML
122: * documents.
123: *
124: * @return A new XML/HTML/DTD document
125: * @see Document
126: */
127: public static Document createDocument(Class docClass) {
128: // Returns the document type which is either docClass (must extend
129: // Document), or the class from the properties file, or XMLDocument.
130: docClass = getDocClass(docClass);
131: // Instantiate a new document, report any error encountered and default
132: // to XMLDocument is necessary.
133: if (docClass != null && docClass != Document.class
134: && docClass != XMLDocument.class) {
135: try {
136: return (Document) docClass.newInstance();
137: } catch (Exception except) {
138: Log
139: .error("DOMFactory.createDocument: Could not create new instance of document class ["
140: + docClass.getName()
141: + "] -- defaulting to Document");
142: Log.error(except);
143: }
144: }
145: return new XMLDocument();
146: }
147:
148: /**
149: * Creates and returns a new XML/HTML/DTD parser. The parser type is
150: * determined by the document class provided in <TT>docClass</TT>, which
151: * dictates whether the parser is XML, HTML or DTD. If <TT>docClass</TT> is
152: * null, the same rules that govern {@link #createDocument} apply here.
153: * <P>
154: * The parser is only guaranteed to extend {@link Parser} and will use
155: * {@link #createDocument} to create an instance of the parsed document.
156: * To create a document of a user class, either use {@link Source}, or
157: * the following code:
158: * <PRE>
159: * Parser parser;
160: *
161: * parser = DOMFactory.createParser( reader, sourceURI, docClass );
162: * if ( parser instanceof XMLParser )
163: * doc = ( (XMLParser) parser ).parseDocument( null, docClass );
164: * else
165: * doc = parser.parseDocument();
166: * </PRE>
167: *
168: * @param reader A reader to the document source
169: * @param sourceURI The source URI
170: * @param docClass The requested document type
171: * @return A new parser
172: */
173: public static Parser createParser(Reader reader, String sourceURI,
174: Class docClass) {
175: String name;
176: Class parserClass;
177: Constructor cnst;
178:
179: // Get the specified document class, or the default document class.
180: // Either one is expected as the output of the parser, so work with it.
181: // Based on the document type decide which is the default parser and
182: // the name of the parser class property.
183: docClass = getDocClass(docClass);
184: if (HTMLDocument.class.isAssignableFrom(docClass)) {
185: name = "openxml.parser.html.class";
186: parserClass = HTMLParser.class;
187: } else if (DTDDocument.class.isAssignableFrom(docClass)) {
188: name = "openxml.parser.dtd.class";
189: parserClass = DTDParser.class;
190: } else {
191: name = "openxml.parser.xml.class";
192: parserClass = XMLParser.class;
193: }
194:
195: // Given the property name, read its value and if valid, attempt to
196: // load the named parser class. Make sure this class is indeed a
197: // parser. There is no way to check its ability to produce a document
198: // of the requested type: the parser for HTML documents might be rigged
199: // to only produce DTD documents. Such is life.
200: name = getProperty(name);
201: if (name != null) {
202: try {
203: parserClass = Class.forName(name);
204: if (!Parser.class.isAssignableFrom(parserClass)) {
205: parserClass = XMLParser.class;
206: Log
207: .error("DOMFactory.createParser: Parser class ["
208: + name
209: + "] is not a supported parser -- defaulting to XMLParser");
210: }
211: } catch (ClassNotFoundException except) {
212: Log
213: .error("DOMFactory.createParser: Could not locate parser class ["
214: + name + "] -- defaulting to XMLParser");
215: }
216: }
217:
218: // Parser class known, find the constructor which accepts a reader and
219: // sourceURI. This is the minimalist constructor for a parser and is
220: // supported by all three parsers. Using that constructor create a new
221: // instance of the parser and return it.
222: if (parserClass != null && parserClass != XMLParser.class) {
223: try {
224: cnst = parserClass.getConstructor(_parserSignature);
225: if (cnst != null) {
226: return (Parser) cnst.newInstance(new Object[] {
227: reader, sourceURI });
228: }
229: } catch (Exception except) {
230: Log
231: .error("DOMFactory.createParser: Could not create new instance of parser class ["
232: + parserClass.getName()
233: + "] -- defaulting to XMLParser");
234: Log.error(except);
235: }
236: }
237: // Anything fails, or if specifically requested, return the default
238: // XML parser.
239: return new XMLParser(reader, sourceURI);
240: }
241:
242: /**
243: * Creates and returns a new XML parser.
244: *
245: * @param reader A reader to the document source
246: * @param sourceURI The source URI
247: * @return A new parser
248: */
249: public static Parser createParser(Reader reader, String sourceName)
250: throws IOException {
251: return createParser(reader, sourceName, DOCUMENT_XML);
252: }
253:
254: /**
255: * Creates and returns a new XML/HTML/DTD parser. The parser type is
256: * determined by the document class provided in <TT>docClass</TT>, which
257: * dictates whether the parser is XML, HTML or DTD. If <TT>docClass</TT> is
258: * null, the same rules that govern {@link #createDocument} apply here.
259: * <P>
260: * The parser is only guaranteed to extend {@link Parser} and will use
261: * {@link #createDocument} to create an instance of the parsed document.
262: * To create a document of a user class, either use {@link Source}, or
263: * the following code:
264: * <PRE>
265: * Parser parser;
266: *
267: * parser = DOMFactory.createParser( reader, sourceURI, docClass );
268: * if ( parser instanceof XMLParser )
269: * doc = ( (XMLParser) parser ).parseDocument( null, docClass );
270: * else
271: * doc = parser.parseDocument();
272: * </PRE>
273: *
274: * @param input An input stream to the document source
275: * @param sourceURI The source URI
276: * @param docClass The requested document type
277: * @return A new parser
278: */
279: public static Parser createParser(InputStream stream,
280: String sourceName, Class docClass) throws IOException {
281: return createParser(new BufferedReader(new InputStreamReader(
282: stream)), sourceName, docClass);
283: }
284:
285: /**
286: * Creates and returns a new XML parser.
287: *
288: * @param input An input stream to the document source
289: * @param sourceURI The source URI
290: * @return A new parser
291: */
292: public static Parser createParser(InputStream stream,
293: String sourceName) throws IOException {
294: return createParser(new BufferedReader(new InputStreamReader(
295: stream)), sourceName, DOCUMENT_XML);
296: }
297:
298: /**
299: * Creates and returns a new XML/HTML/DTD printer. The printer type is
300: * determined by the document class provided in <TT>docClass</TT>, which
301: * dictates whether the printer is XML, HTML or DTD. If <TT>docClass</TT> is
302: * null, the same rules that govern {@link #createDocument} apply here.
303: *
304: * @param writer A writer for the document output
305: * @param mode The printing mode
306: * @param docClass The document type
307: * @return A new printer
308: *
309: * @deprecated
310: * This method has become obsolete in favor of the <a
311: * href="x3p/package-summary.html">X3P Publisher and Producer APIs</a>.
312: * This method is temporarily provided for backward compatibility but
313: * will not be included in release 1.1.
314: */
315: public static Printer createPrinter(Writer writer, int mode,
316: Class docClass) throws IOException {
317: Class printerClass;
318: String name;
319: Constructor cnst;
320:
321: // Get the specified document class, or the default document class.
322: // Either one is expected as the input to the parser, so work with it.
323: // Based on the document type decide which is the default printer and
324: // the name of the printer class property.
325: docClass = getDocClass(docClass);
326: if (HTMLDocument.class.isAssignableFrom(docClass)) {
327: name = "openxml.printer.html.class";
328: printerClass = HTMLPrinter.class;
329: } else if (DTDDocument.class.isAssignableFrom(docClass)) {
330: name = "openxml.printer.dtd.class";
331: printerClass = DTDPrinter.class;
332: } else {
333: name = "openxml.printer.xml.class";
334: printerClass = XMLPrinter.class;
335: }
336:
337: // Given the property name, read its value and if valid, attempt to
338: // load the named printer class. Make sure this class is indeed a
339: // printer. There is no way to check its ability to generate a document
340: // of the requested type: the printer for HTML documents might be rigged
341: // to only generate DTD documents. Such is life.
342: name = getProperty(name);
343: if (name != null) {
344: try {
345: printerClass = Class.forName(name);
346: if (!Printer.class.isAssignableFrom(printerClass)) {
347: printerClass = XMLPrinter.class;
348: Log
349: .error("DOMFactory.createPrinter: Printer class ["
350: + name
351: + "] is not a supported printer -- defaulting to XMLPrinter");
352: }
353: } catch (ClassNotFoundException except) {
354: Log
355: .error("DOMFactory.createPrinter: Could not locate printer class ["
356: + name
357: + "] -- defaulting to XMLPrinter");
358: }
359: }
360:
361: // Printer class known, find the constructor which accepts a writer and
362: // printing mode. This is the minimalist constructor for a printer and
363: // is supported by all three printers. Using that constructor create a
364: // new instance of the printer and return it.
365: if (printerClass != null && printerClass != XMLPrinter.class) {
366: try {
367: cnst = printerClass.getConstructor(_printerSignature);
368: return (Printer) cnst.newInstance(new Object[] {
369: writer, new Integer(mode) });
370: } catch (Exception except) {
371: Log
372: .error("DOMFactory.createPrinter: Could not create new instance of printer class ["
373: + printerClass.getName()
374: + "] -- defaulting to XMLPrinter");
375: Log.error(except);
376: }
377: }
378: // Anything fails, or if specifically requested, return the default
379: // XML printer.
380: return new XMLPrinter(writer, mode);
381: }
382:
383: /**
384: * Creates and returns a new XML printer.
385: *
386: * @param writer A writer for the document output
387: * @param mode The printing mode
388: * @return A new printer
389: *
390: * @deprecated
391: * This method has become obsolete in favor of the <a
392: * href="x3p/package-summary.html">X3P Publisher and Producer APIs</a>.
393: * This method is temporarily provided for backward compatibility but
394: * will not be included in release 1.1.
395: */
396: public static Printer createPrinter(Writer writer, int mode)
397: throws IOException {
398: return createPrinter(writer, mode, DOCUMENT_XML);
399: }
400:
401: /**
402: * Creates and returns a new XML/HTML/DTD printer. The printer type is
403: * determined by the document class provided in <TT>docClass</TT>, which
404: * dictates whether the printer is XML, HTML or DTD. If <TT>docClass</TT> is
405: * null, the same rules that govern {@link #createDocument} apply here.
406: *
407: * @param output A stream for the document output
408: * @param mode The printing mode
409: * @param docClass The document type
410: * @return A new printer
411: *
412: * @deprecated
413: * This method has become obsolete in favor of the <a
414: * href="x3p/package-summary.html">X3P Publisher and Producer APIs</a>.
415: * This method is temporarily provided for backward compatibility but
416: * will not be included in release 1.1.
417: */
418: public static Printer createPrinter(OutputStream stream, int mode,
419: Class docClass) throws IOException {
420: return createPrinter(new XMLStreamWriter(stream), mode,
421: docClass);
422: }
423:
424: /**
425: * Creates and returns a new XML printer.
426: *
427: * @param output A stream for the document output
428: * @param mode The printing mode
429: * @return A new printer
430: *
431: * @deprecated
432: * This method has become obsolete in favor of the <a
433: * href="x3p/package-summary.html">X3P Publisher and Producer APIs</a>.
434: * This method is temporarily provided for backward compatibility but
435: * will not be included in release 1.1.
436: */
437: public static Printer createPrinter(OutputStream stream, int mode)
438: throws IOException {
439: return createPrinter(new XMLStreamWriter(stream), mode,
440: DOCUMENT_XML);
441: }
442:
443: /**
444: * Returns the property from the OpenXML properties file.
445: *
446: * @param name The property name
447: * @return Property value or null
448: */
449: public static String getProperty(String name) {
450: return getProperties().getProperty(name);
451: }
452:
453: /**
454: * Returns the properties list from the OpenXML properties file. If this
455: * property list is changed, changes will affect the behavior of the factory
456: * and other OpenXML elements.
457: *
458: * @return The properties list
459: */
460: public static Properties getProperties() {
461: String className;
462: Class docClass;
463:
464: if (_xmlProps == null) {
465: _xmlProps = new Properties();
466: try {
467: _xmlProps.load(DOMFactory.class
468: .getResourceAsStream(RESOURCE_PROPS));
469: } catch (Exception except) {
470: Log
471: .error("DOMFactory.getProperties: Failed to load properties from resource ["
472: + RESOURCE_PROPS + "]");
473: Log.error(except);
474: }
475: }
476: return _xmlProps;
477: }
478:
479: /**
480: * Returns the specified document class, or the properties file specified
481: * class, or the default. If the specified document class is not valid, a
482: * runtime exception is thrown. If the specified class is null, the name
483: * is read from the properties file and used as the based class. If that
484: * property is missing or not a valid class, the default document class
485: * ({@link Document}) is used.
486: *
487: * @param docClass The specified document class, or null
488: * @return A valid document class, extending {@link Document}
489: */
490: public static Class getDocClass(Class docClass) {
491: String prop;
492:
493: // If the specified document class is invalid, throw an exception,
494: // as we do not want to assume default behavior.
495: if (docClass != null) {
496: if (docClass == DocumentType.class
497: || docClass == DTDDocument.class) {
498: return DTDDocument.class;
499: }
500: if (!Document.class.isAssignableFrom(docClass)) {
501: throw new IllegalArgumentException(
502: "Requested document class is not a valid class.");
503: }
504: }
505: // Read the property from the properties file and if not missing,
506: // attempt to load the named class. If the named class does not extend
507: // document, sadly it must be disposed of.
508: prop = getProperty("openxml.document.class");
509: if (prop != null) {
510: try {
511: docClass = Class.forName(prop);
512: if (!Document.class.isAssignableFrom(docClass)) {
513: docClass = null;
514: Log
515: .error("DOMFactory.getDocClass: Document class ["
516: + prop
517: + "] is not a supported document class -- defaulting to Document");
518: }
519: } catch (ClassNotFoundException except) {
520: Log
521: .error("DOMFactory.getDocClass: Could not locate document class ["
522: + prop + "] -- defaulting to Document");
523: }
524: }
525: // The default is Document.
526: if (docClass == null) {
527: docClass = DOCUMENT_XML;
528: }
529: return docClass;
530: }
531:
532: public static Source newSource() {
533: getHolderFinder();
534: return new SourceImpl();
535: }
536:
537: /**
538: * Returns a singleton holder finder. This finder has default holder factories
539: * registered for handling network, file, JAR and CLASSPATH document sources,
540: * and mapping for built-in DTDs. Additional holder factories and Xcatalogs
541: * may be specified in the properties file and are loaded and registered the
542: * first time this method is called.
543: *
544: * @return An holder finder
545: */
546: public static HolderFinder getHolderFinder() {
547: XCatalog catalog;
548: String prop;
549: StringTokenizer tokenizer;
550:
551: if (_finder == null) {
552: _finder = HolderFinderImpl.getHolderFinder();
553: catalog = XCatalogFactory.findCatalog(MAIN_CATALOG);
554: if (catalog != null) {
555: _finder.registerFactory(XCatalogFactory
556: .asHolderFactory(catalog));
557: }
558:
559: prop = DOMFactory.getProperty("openxml.holder.factories");
560: if (prop != null) {
561: tokenizer = new StringTokenizer(prop, ";");
562: while (tokenizer.hasMoreTokens()) {
563: prop = tokenizer.nextToken();
564: try {
565: _finder.registerFactory((HolderFactory) Class
566: .forName(prop).newInstance());
567: Log
568: .info("DOMHolderFactory.<init>: Registered holder factory ["
569: + prop + "]");
570: } catch (Exception except) {
571: Log
572: .error("DOMHolderFactory.<init>: Failed to register holder factory ["
573: + prop
574: + "] -- class not found or could not be instantiated");
575: }
576: }
577: }
578:
579: prop = DOMFactory.getProperty("openxml.holder.catalogs");
580: if (prop != null) {
581: tokenizer = new StringTokenizer(prop, ";");
582: while (tokenizer.hasMoreTokens()) {
583: prop = tokenizer.nextToken();
584: try {
585: catalog = XCatalogFactory.findCatalog(prop);
586:
587: _finder.registerFactory(XCatalogFactory
588: .asHolderFactory(catalog));
589: Log
590: .info("DOMHolderFactory.<init>: Registered XCatalog from ["
591: + prop + "]");
592: } catch (Exception except) {
593: Log
594: .error("DOMHolderFactory.<init>: Failed to register XCatalog from ["
595: + prop
596: + "] -- catalog not found or could not be loaded");
597: }
598: }
599: }
600: }
601: return _finder;
602: }
603:
604: /**
605: * XML document class. Can be used to request a new XML document, XML
606: * parser or XML printer. Will produce a document of type {@link
607: * Document}.
608: */
609: public final static Class DOCUMENT_XML = Document.class;
610:
611: /**
612: * HTML document class. Can be used to request a new HTML document, HTML
613: * parser or HTML printer. Will produce a document of type {@link
614: * HTMLDocument}.
615: */
616: public final static Class DOCUMENT_HTML = HTMLDocumentImpl.class;
617:
618: /**
619: * DTD document class. Can be used to request a new DTD document, DTD
620: * parser or DTD printer. Will produce a document of type {@link
621: * DTDDocument}.
622: */
623: public final static Class DOCUMENT_DTD = DocumentType.class;
624:
625: /**
626: * Holds the properties for the factory. This object is created on-demand
627: * when a property is first accessed.
628: */
629: private static Properties _xmlProps;
630:
631: /**
632: * Holds a singleton holder finder.
633: */
634: private static HolderFinder _finder;
635:
636: /**
637: * The signature for the constructor of a parser class (class that
638: * implemented {@link Parser}). Accepts <TT>reader</TT> and
639: * <TT>docClass</TT>.
640: */
641: private final static Class[] _parserSignature = new Class[] {
642: Reader.class, String.class };
643:
644: /**
645: * The signature for the constructor of a printer class (class that
646: * implemented {@link Printer}). Accepts <TT>writer</TT> and <TT>mode</TT>.
647: */
648: private final static Class[] _printerSignature = new Class[] {
649: Writer.class, int.class };
650:
651: /**
652: * Identifies the properties file as a resource. The named resource will
653: * be loaded with the DOMFactory class loader. If the resource exists in
654: * a different package, the full package path must be included, preceded
655: * with '/'.
656: */
657: private final static String RESOURCE_PROPS = "openxml.properties";
658:
659: private final static String MAIN_CATALOG = "res:/org/openxml/source/dtd-catalog/catalog.xml";
660:
661: }
|