001: package net.myvietnam.mvncore.configuration;
002:
003: /* ====================================================================
004: * The Apache Software License, Version 1.1
005: *
006: * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
007: * reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowledgement:
023: * "This product includes software developed by the
024: * Apache Software Foundation (http://www.apache.org/)."
025: * Alternately, this acknowledgement may appear in the software itself,
026: * if and wherever such third-party acknowledgements normally appear.
027: *
028: * 4. The names "The Jakarta Project", "Commons", and "Apache Software
029: * Foundation" must not be used to endorse or promote products derived
030: * from this software without prior written permission. For written
031: * permission, please contact apache@apache.org.
032: *
033: * 5. Products derived from this software may not be called "Apache"
034: * nor may "Apache" appear in their names without prior written
035: * permission of the Apache Software Foundation.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
041: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
042: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
043: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
044: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
045: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
046: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
047: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
048: * SUCH DAMAGE.
049: * ====================================================================
050: *
051: * This software consists of voluntary contributions made by many
052: * individuals on behalf of the Apache Software Foundation. For more
053: * information on the Apache Software Foundation, please see
054: * <http://www.apache.org/>.
055: */
056:
057: import java.io.IOException;
058: import java.io.Writer;
059: import java.util.NoSuchElementException;
060:
061: import org.apache.commons.digester.Digester;
062: import org.dom4j.Document;
063: import org.dom4j.DocumentException;
064: import org.dom4j.io.DOMWriter;
065: import org.dom4j.io.OutputFormat;
066: import org.dom4j.io.SAXReader;
067: import org.dom4j.io.XMLWriter;
068: import org.xml.sax.SAXException;
069:
070: /**
071: * <p>A helper class that supports XML-like processing for configuration
072: * objects.</p>
073: * <p>This class provides a set of methods that all have something to do with
074: * treating a <code>Configuration</code> object as a XML document. So a
075: * configuration can be transformed into a <code>Document</code> (either
076: * dom4j or w3c), saved as an XML file or passed to Digester.</p>
077: * <p><strong>Implementation note:</strong> This class is not thread safe.</p>
078: *
079: * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
080: * @version $Id: ConfigurationXMLDocument.java,v 1.1 2003/12/09 08:25:30 huumai Exp $
081: */
082: public class ConfigurationXMLDocument {
083: /** Constant for the class element.*/
084: protected static final String ELEM_CLASS = "config/class";
085:
086: /** Constant for the property element.*/
087: protected static final String ELEM_PROPERTY = "config/class/property";
088:
089: /** Constant for the name attribute.*/
090: protected static final String ATTR_NAME = "name";
091:
092: /** Constant for the value attribute.*/
093: protected static final String ATTR_VALUE = "value";
094:
095: /** Stores the configuration object this object operates on.*/
096: private Configuration configuration;
097:
098: /**
099: * Creates a new instance of <code>ConfigurationXMLDocument</code>
100: * and sets the configuration object to be processed.
101: * @param config the configuration object
102: */
103: public ConfigurationXMLDocument(Configuration config) {
104: setConfiguration(config);
105: }
106:
107: /**
108: * Returns the <code>Configuration</code> object for this document.
109: * @return the <code>Configuration</code> object
110: */
111: public Configuration getConfiguration() {
112: return configuration;
113: }
114:
115: /**
116: * Sets the <code>Configuration</code> object this document operates on.
117: * @param configuration the <code>Configuration</code> object
118: */
119: public void setConfiguration(Configuration configuration) {
120: this .configuration = configuration;
121: }
122:
123: /**
124: * Returns a <code>XMLReader</code> object for the specified configuration
125: * object. This reader can then be used to perform XML-like processing on
126: * the configuration.
127: * @param config the configuration object
128: * @return a XMLReader for this configuration
129: */
130: public static ConfigurationXMLReader createXMLReader(
131: Configuration config) {
132: if (config instanceof HierarchicalConfiguration) {
133: return new HierarchicalConfigurationXMLReader(
134: (HierarchicalConfiguration) config);
135: } /* if */
136: else {
137: return new BaseConfigurationXMLReader(config);
138: } /* else */
139: }
140:
141: /**
142: * Returns a <code>XMLReader</code> object for the actual configuration
143: * object.
144: * @return a XMLReader for the actual configuration
145: */
146: public ConfigurationXMLReader createXMLReader() {
147: return createXMLReader((String) null);
148: }
149:
150: /**
151: * Returns a <code>ConfigurationXMLReader</code> object for the subset
152: * configuration specified by the given prefix. If no properties are found
153: * under this prefix, a <code>NoSuchElementException</code>
154: * exception will be thrown.
155: * @param prefix the prefix of the configuration keys that belong to the
156: * subset; can be <b>null</b>, then the whole configuration is affected
157: * @return a XMLReader for the specified subset configuration
158: */
159: public ConfigurationXMLReader createXMLReader(String prefix) {
160: return createXMLReader(configForKey(prefix));
161: }
162:
163: /**
164: * Transforms the wrapped configuration into a dom4j document.
165: * @param prefix a prefix for the keys to process; can be <b>null</b>,
166: * then all keys in the configuration will be added to the document
167: * @param rootName the name of the root element in the document; can be
168: * <b>null</b>, then a default name will be used
169: * @return the document
170: * @throws DocumentException if an error occurs
171: */
172: public Document getDocument(String prefix, String rootName)
173: throws DocumentException {
174: ConfigurationXMLReader xmlReader = createXMLReader(prefix);
175: if (rootName != null) {
176: xmlReader.setRootName(rootName);
177: } /* if */
178:
179: SAXReader reader = new SAXReader(xmlReader);
180: return reader.read(getClass().getName());
181: }
182:
183: /**
184: * Transforms the wrapped configuration into a dom4j document. The root
185: * element will be given a default name.
186: * @param prefix a prefix for the keys to process; can be <b>null</b>,
187: * then all keys in the configuration will be added to the document
188: * @return the document
189: * @throws DocumentException if an error occurs
190: */
191: public Document getDocument(String prefix) throws DocumentException {
192: return getDocument(prefix, null);
193: }
194:
195: /**
196: * Transforms the wrapped configuration into a dom4j document. The root
197: * element will be given a default name.
198: * @return the document
199: * @throws DocumentException if an error occurs
200: */
201: public Document getDocument() throws DocumentException {
202: return getDocument(null, null);
203: }
204:
205: /**
206: * Transforms the wrapped configuration into a w3c document.
207: * @param prefix a prefix for the keys to process; can be <b>null</b>,
208: * then all keys in the configuration will be added to the document
209: * @param rootName the name of the root element in the document; can be
210: * <b>null</b>, then a default name will be used
211: * @return the document
212: * @throws DocumentException if an error occurs
213: */
214: public org.w3c.dom.Document getW3cDocument(String prefix,
215: String rootName) throws DocumentException {
216: return toW3cDocument(getDocument(prefix, rootName));
217: }
218:
219: /**
220: * Transforms the wrapped configuration into a w3c document. The root
221: * element will be given a default name.
222: * @param prefix a prefix for the keys to process; can be <b>null</b>,
223: * then all keys in the configuration will be added to the document
224: * @return the document
225: * @throws DocumentException if an error occurs
226: */
227: public org.w3c.dom.Document getW3cDocument(String prefix)
228: throws DocumentException {
229: return getW3cDocument(prefix, null);
230: }
231:
232: /**
233: * Transforms the wrapped configuration into a w3c document. The root
234: * element will be given a default name.
235: * @return the document
236: * @throws DocumentException if an error occurs
237: */
238: public org.w3c.dom.Document getW3cDocument()
239: throws DocumentException {
240: return getW3cDocument(null, null);
241: }
242:
243: /**
244: * Converts a dom4j document into a w3c document.
245: * @param doc the dom4j document
246: * @return the w3c document
247: * @throws DocumentException if an error occurs
248: */
249: static org.w3c.dom.Document toW3cDocument(Document doc)
250: throws DocumentException {
251: return new DOMWriter().write(doc);
252: }
253:
254: /**
255: * Helper method for constructing a subset if necessary. Depending on
256: * the passed in key this method either returns the wrapped configuration
257: * or the specified subset of it.
258: * @param key the key
259: * @return the configuration for that key
260: */
261: private Configuration configForKey(String key) {
262: Configuration conf = (key == null) ? getConfiguration()
263: : getConfiguration().subset(key);
264:
265: // undefined?
266: if (conf == null
267: || (conf instanceof CompositeConfiguration && ((CompositeConfiguration) conf)
268: .getNumberOfConfigurations() < 2)) {
269: throw new NoSuchElementException("No subset with key "
270: + key);
271: } /* if */
272:
273: return conf;
274: }
275:
276: /**
277: * <p>Creates and initializes an object specified in the configuration
278: * using Digester.</p>
279: * <p>This method first constructs a subset configuration with the keys
280: * starting with the given prefix. It then transforms this subset into a
281: * XML document and let that be processed by Digester. The result of this
282: * processing is returned.</p>
283: * <p>The method is intended to be used for creating simple objects that
284: * are specified somewhere in the configuration in a standard way. The
285: * following fragment shows how a configuration file must look like to be
286: * understood by the default Digester rule set used by this method:</p>
287: * <p><pre>
288: * ...
289: * <class name="mypackage.MyClass"/>
290: * <args>
291: * <property name="myFirstProperty" value="myFirstValue"/>
292: * <property name="MySecondProperty" value="mySecondValue"/>
293: * ...
294: * </args>
295: * ...
296: * </pre></p>
297: * @param prefix the prefix of the keys that are passed to Digester; can
298: * be <b>null</b>, then the whole configuration will be processed
299: * @return the result of the Digester processing
300: * @throws IOException if an IOException occurs
301: * @throws SAXException if a SAXException occurs
302: */
303: public Object callDigester(String prefix) throws IOException,
304: SAXException {
305: Digester digester = getDefaultDigester(prefix);
306: return digester.parse(getClass().getName());
307: }
308:
309: /**
310: * Returns a default Digester instance. This instance is used for the
311: * simple object creation feature.
312: * @param prefix the prefix of the keys to be processed; can be
313: * <b>null</b>, then the whole configuration is meant
314: * @return the default Digester instance
315: */
316: protected Digester getDefaultDigester(String prefix) {
317: Digester digester = createDefaultDigester(prefix);
318: setupDefaultDigester(digester);
319:
320: return digester;
321: }
322:
323: /**
324: * Creates the default Digester instance for the given prefix. This method
325: * is called by <code>getDefaultDigester()</code>.
326: * @param prefix the prefix of the keys to be processed; can be
327: * <b>null</b>, then the whole configuration is meant
328: * @return the default Digester instance
329: */
330: protected Digester createDefaultDigester(String prefix) {
331: return new Digester(createXMLReader(prefix));
332: }
333:
334: /**
335: * Initializes the default digester instance used for simple object
336: * creation. Here all needed properties and rules can be set. This base
337: * implementation sets default rules for object creation as explained in
338: * the comment for the <code>callDigester()</code> methods.
339: * @param digester the digester instance to be initialized
340: */
341: protected void setupDefaultDigester(Digester digester) {
342: digester.addObjectCreate(ELEM_CLASS, ATTR_NAME, Object.class);
343: digester.addSetProperty(ELEM_PROPERTY, ATTR_NAME, ATTR_VALUE);
344: }
345:
346: /**
347: * Writes a configuration (or parts of it) to the given writer.
348: * @param out the output writer
349: * @param prefix the prefix of the subset to write; if <b>null</b>, the
350: * whole configuration is written
351: * @param root the name of the root element of the resulting document;
352: * <b>null</b> for a default name
353: * @param pretty flag for the pretty print mode
354: * @throws IOException if an IO error occurs
355: * @throws DocumentException if there is an error during processing
356: */
357: public void write(Writer out, String prefix, String root,
358: boolean pretty) throws IOException, DocumentException {
359: OutputFormat format = (pretty) ? OutputFormat
360: .createPrettyPrint() : OutputFormat
361: .createCompactFormat();
362:
363: XMLWriter writer = new XMLWriter(out, format);
364: writer.write(getDocument(prefix, root));
365: }
366:
367: /**
368: * Writes a configuration (or parts of it) to the given writer.
369: * This overloaded version always uses pretty print mode.
370: * @param out the output writer
371: * @param prefix the prefix of the subset to write; if <b>null</b>, the
372: * whole configuration is written
373: * @param root the name of the root element of the resulting document;
374: * <b>null</b> for a default name
375: * @throws IOException if an IO error occurs
376: * @throws DocumentException if there is an error during processing
377: */
378: public void write(Writer out, String prefix, String root)
379: throws IOException, DocumentException {
380: write(out, prefix, root, true);
381: }
382:
383: /**
384: * Writes a configuration (or parts of it) to the given writer.
385: * The resulting document's root element will be given a default name.
386: * @param out the output writer
387: * @param prefix the prefix of the subset to write; if <b>null</b>, the
388: * whole configuration is written
389: * @param pretty flag for the pretty print mode
390: * @throws IOException if an IO error occurs
391: * @throws DocumentException if there is an error during processing
392: */
393: public void write(Writer out, String prefix, boolean pretty)
394: throws IOException, DocumentException {
395: write(out, prefix, null, pretty);
396: }
397:
398: /**
399: * Writes a configuration (or parts of it) to the given writer.
400: * The resulting document's root element will be given a default name.
401: * This overloaded version always uses pretty print mode.
402: * @param out the output writer
403: * @param prefix the prefix of the subset to write; if <b>null</b>, the
404: * whole configuration is written
405: * @throws IOException if an IO error occurs
406: * @throws DocumentException if there is an error during processing
407: */
408: public void write(Writer out, String prefix) throws IOException,
409: DocumentException {
410: write(out, prefix, true);
411: }
412:
413: /**
414: * Writes the wrapped configuration to the given writer.
415: * The resulting document's root element will be given a default name.
416: * @param out the output writer
417: * @param pretty flag for the pretty print mode
418: * @throws IOException if an IO error occurs
419: * @throws DocumentException if there is an error during processing
420: */
421: public void write(Writer out, boolean pretty) throws IOException,
422: DocumentException {
423: write(out, null, null, pretty);
424: }
425:
426: /**
427: * Writes the wrapped configuration to the given writer.
428: * The resulting document's root element will be given a default name.
429: * This overloaded version always uses pretty print mode.
430: * @param out the output writer
431: * @throws IOException if an IO error occurs
432: * @throws DocumentException if there is an error during processing
433: */
434: public void write(Writer out) throws IOException, DocumentException {
435: write(out, true);
436: }
437: }
|