001: package net.myvietnam.mvncore.configuration;
002:
003: /* ====================================================================
004: * The Apache Software License, Version 1.1
005: *
006: * Copyright (c) 1999-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.util.ArrayList;
058: import java.util.Collection;
059: import java.util.Collections;
060: import java.util.Iterator;
061: import java.util.List;
062:
063: /**
064: * <p>A base class for converters that transform a normal configuration
065: * object into a hierarchical configuration.</p>
066: * <p>This class provides a default mechanism for iterating over the keys in a
067: * configuration and to throw corresponding element start and end events. By
068: * handling these events a hierarchy can be constructed that is equivalent to
069: * the keys in the original configuration.</p>
070: * <p>Concrete sub classes will implement event handlers that generate SAX
071: * events for XML processing or construct a
072: * <code>HierarchicalConfiguration</code> root node. All in all with this class
073: * it is possible to treat a default configuration as if it was a hierarchical
074: * configuration, which can be sometimes useful.</p>
075: * @see HierarchicalConfiguration
076: *
077: * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
078: * @version $Id: HierarchicalConfigurationConverter.java,v 1.1 2003/12/09 08:25:30 huumai Exp $
079: */
080: abstract class HierarchicalConfigurationConverter {
081: /**
082: * Processes the specified configuration object. This method implements
083: * the iteration over the configuration's keys. All defined keys are
084: * translated into a set of element start and end events represented by
085: * calls to the <code>elementStart()</code> and
086: * <code>elementEnd()</code> methods.
087: * @param config the configuration to be processed
088: */
089: public void process(Configuration config) {
090: if (config != null) {
091: ConfigurationKey keyEmpty = new ConfigurationKey();
092: ConfigurationKey keyLast = keyEmpty;
093:
094: for (Iterator it = config.getKeys(); it.hasNext();) {
095: String key = (String) it.next();
096: ConfigurationKey keyAct = new ConfigurationKey(key);
097: closeElements(keyLast, keyAct);
098: String elem = openElements(keyLast, keyAct);
099: fireValue(elem, config.getProperty(key));
100: keyLast = keyAct;
101: } /* for */
102:
103: closeElements(keyLast, keyEmpty); // close all open
104: }
105: }
106:
107: /**
108: * An event handler method that is called when an element starts.
109: * Concrete sub classes must implement it to perform a proper event
110: * handling.
111: * @param name the name of the new element
112: * @param value the element's value; can be <b>null</b> if the element
113: * does not have any value
114: */
115: protected abstract void elementStart(String name, Object value);
116:
117: /**
118: * An event handler method that is called when an element ends. For each
119: * call of <code>elementStart()</code> there will be a corresponding call
120: * of this method. Concrete sub classes must implement it to perform a
121: * proper event handling.
122: * @param name the name of the ending element
123: */
124: protected abstract void elementEnd(String name);
125:
126: /**
127: * Fires all necessary element end events for the specified keys. This
128: * method is called for each key obtained from the configuration to be
129: * converted. It calculates the common part of the actual and the last
130: * processed key and thus determines how many elements must be
131: * closed.
132: * @param keyLast the last processed key
133: * @param keyAct the actual key
134: */
135: protected void closeElements(ConfigurationKey keyLast,
136: ConfigurationKey keyAct) {
137: ConfigurationKey keyDiff = keyAct.differenceKey(keyLast);
138: Iterator it = reverseIterator(keyDiff);
139: if (it.hasNext()) {
140: // Skip first because it has already been closed by fireValue()
141: it.next();
142: } /* if */
143:
144: while (it.hasNext()) {
145: elementEnd((String) it.next());
146: } /* while */
147: }
148:
149: /**
150: * Helper method for determining a reverse iterator for the specified key.
151: * This implementation returns an iterator that returns the parts of the
152: * given key in reverse order, ignoring indices.
153: * @param key the key
154: * @return a reverse iterator for the parts of this key
155: */
156: protected Iterator reverseIterator(ConfigurationKey key) {
157: List list = new ArrayList();
158: for (ConfigurationKey.KeyIterator it = key.iterator(); it
159: .hasNext();) {
160: list.add(it.nextKey());
161: } /* for */
162:
163: Collections.reverse(list);
164: return list.iterator();
165: }
166:
167: /**
168: * Fires all necessary element start events for the specified key. This
169: * method is called for each key obtained from the configuration to be
170: * converted. It ensures that all elements "between" the last key and the
171: * actual key are opened.
172: * @param keyLast the last processed key
173: * @param keyAct the actual key
174: * @return the name of the last element on the path
175: */
176: protected String openElements(ConfigurationKey keyLast,
177: ConfigurationKey keyAct) {
178: ConfigurationKey.KeyIterator it = keyLast.differenceKey(keyAct)
179: .iterator();
180:
181: for (it.nextKey(); it.hasNext(); it.nextKey()) {
182: elementStart(it.currentKey(), null);
183: } /* for */
184:
185: return it.currentKey();
186: }
187:
188: /**
189: * Fires all necessary element start events with the actual element values.
190: * This method is called for each key obtained from the configuration to be
191: * processed with the last part of the key as argument. The value can be
192: * either a single value or a collection.
193: * @param name the name of the actual element
194: * @param value the element's value
195: */
196: protected void fireValue(String name, Object value) {
197: if (value != null && value instanceof Collection) {
198: for (Iterator it = ((Collection) value).iterator(); it
199: .hasNext();) {
200: fireValue(name, it.next());
201: } /* for */
202: } /* if */
203:
204: else {
205: elementStart(name, value);
206: elementEnd(name);
207: } /* else */
208: }
209: }
|