001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.configuration;
019:
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Set;
027:
028: /**
029: * <p>A base class for converters that transform a normal configuration
030: * object into a hierarchical configuration.</p>
031: * <p>This class provides a default mechanism for iterating over the keys in a
032: * configuration and to throw corresponding element start and end events. By
033: * handling these events a hierarchy can be constructed that is equivalent to
034: * the keys in the original configuration.</p>
035: * <p>Concrete sub classes will implement event handlers that generate SAX
036: * events for XML processing or construct a
037: * <code>HierarchicalConfiguration</code> root node. All in all with this class
038: * it is possible to treat a default configuration as if it was a hierarchical
039: * configuration, which can be sometimes useful.</p>
040: * @see HierarchicalConfiguration
041: *
042: * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
043: * @version $Id: HierarchicalConfigurationConverter.java 439648 2006-09-02 20:42:10Z oheger $
044: */
045: abstract class HierarchicalConfigurationConverter {
046: /**
047: * Processes the specified configuration object. This method implements
048: * the iteration over the configuration's keys. All defined keys are
049: * translated into a set of element start and end events represented by
050: * calls to the <code>elementStart()</code> and
051: * <code>elementEnd()</code> methods.
052: *
053: * @param config the configuration to be processed
054: */
055: public void process(Configuration config) {
056: if (config != null) {
057: ConfigurationKey keyEmpty = new ConfigurationKey();
058: ConfigurationKey keyLast = keyEmpty;
059: Set keySet = new HashSet();
060:
061: for (Iterator it = config.getKeys(); it.hasNext();) {
062: String key = (String) it.next();
063: if (keySet.contains(key)) {
064: // this key has already been processed by openElements
065: continue;
066: }
067: ConfigurationKey keyAct = new ConfigurationKey(key);
068: closeElements(keyLast, keyAct);
069: String elem = openElements(keyLast, keyAct, config,
070: keySet);
071: fireValue(elem, config.getProperty(key));
072: keyLast = keyAct;
073: }
074:
075: // close all open
076: closeElements(keyLast, keyEmpty);
077: }
078: }
079:
080: /**
081: * An event handler method that is called when an element starts.
082: * Concrete sub classes must implement it to perform a proper event
083: * handling.
084: *
085: * @param name the name of the new element
086: * @param value the element's value; can be <b>null</b> if the element
087: * does not have any value
088: */
089: protected abstract void elementStart(String name, Object value);
090:
091: /**
092: * An event handler method that is called when an element ends. For each
093: * call of <code>elementStart()</code> there will be a corresponding call
094: * of this method. Concrete sub classes must implement it to perform a
095: * proper event handling.
096: *
097: * @param name the name of the ending element
098: */
099: protected abstract void elementEnd(String name);
100:
101: /**
102: * Fires all necessary element end events for the specified keys. This
103: * method is called for each key obtained from the configuration to be
104: * converted. It calculates the common part of the actual and the last
105: * processed key and thus determines how many elements must be
106: * closed.
107: *
108: * @param keyLast the last processed key
109: * @param keyAct the actual key
110: */
111: protected void closeElements(ConfigurationKey keyLast,
112: ConfigurationKey keyAct) {
113: ConfigurationKey keyDiff = keyAct.differenceKey(keyLast);
114: Iterator it = reverseIterator(keyDiff);
115: if (it.hasNext()) {
116: // Skip first because it has already been closed by fireValue()
117: it.next();
118: }
119:
120: while (it.hasNext()) {
121: elementEnd((String) it.next());
122: }
123: }
124:
125: /**
126: * Helper method for determining a reverse iterator for the specified key.
127: * This implementation returns an iterator that returns the parts of the
128: * given key in reverse order, ignoring indices.
129: *
130: * @param key the key
131: * @return a reverse iterator for the parts of this key
132: */
133: protected Iterator reverseIterator(ConfigurationKey key) {
134: List list = new ArrayList();
135: for (ConfigurationKey.KeyIterator it = key.iterator(); it
136: .hasNext();) {
137: list.add(it.nextKey());
138: }
139:
140: Collections.reverse(list);
141: return list.iterator();
142: }
143:
144: /**
145: * Fires all necessary element start events for the specified key. This
146: * method is called for each key obtained from the configuration to be
147: * converted. It ensures that all elements "between" the last key and the
148: * actual key are opened and their values are set.
149: *
150: * @param keyLast the last processed key
151: * @param keyAct the actual key
152: * @param config the configuration to process
153: * @param keySet the set with the processed keys
154: * @return the name of the last element on the path
155: */
156: protected String openElements(ConfigurationKey keyLast,
157: ConfigurationKey keyAct, Configuration config, Set keySet) {
158: ConfigurationKey.KeyIterator it = keyLast.differenceKey(keyAct)
159: .iterator();
160: ConfigurationKey k = keyLast.commonKey(keyAct);
161: for (it.nextKey(); it.hasNext(); it.nextKey()) {
162: k.append(it.currentKey(true));
163: elementStart(it.currentKey(true), config.getProperty(k
164: .toString()));
165: keySet.add(k.toString());
166: }
167: return it.currentKey(true);
168: }
169:
170: /**
171: * Fires all necessary element start events with the actual element values.
172: * This method is called for each key obtained from the configuration to be
173: * processed with the last part of the key as argument. The value can be
174: * either a single value or a collection.
175: *
176: * @param name the name of the actual element
177: * @param value the element's value
178: */
179: protected void fireValue(String name, Object value) {
180: if (value != null && value instanceof Collection) {
181: for (Iterator it = ((Collection) value).iterator(); it
182: .hasNext();) {
183: fireValue(name, it.next());
184: }
185: } else {
186: elementStart(name, value);
187: elementEnd(name);
188: }
189: }
190: }
|