1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Set;
26
27 /***
28 * <p>A base class for converters that transform a normal configuration
29 * object into a hierarchical configuration.</p>
30 * <p>This class provides a default mechanism for iterating over the keys in a
31 * configuration and to throw corresponding element start and end events. By
32 * handling these events a hierarchy can be constructed that is equivalent to
33 * the keys in the original configuration.</p>
34 * <p>Concrete sub classes will implement event handlers that generate SAX
35 * events for XML processing or construct a
36 * <code>HierarchicalConfiguration</code> root node. All in all with this class
37 * it is possible to treat a default configuration as if it was a hierarchical
38 * configuration, which can be sometimes useful.</p>
39 * @see HierarchicalConfiguration
40 *
41 * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
42 * @version $Id: HierarchicalConfigurationConverter.java 155408 2005-02-26 12:56:39Z dirkv $
43 */
44 abstract class HierarchicalConfigurationConverter
45 {
46 /***
47 * Processes the specified configuration object. This method implements
48 * the iteration over the configuration's keys. All defined keys are
49 * translated into a set of element start and end events represented by
50 * calls to the <code>elementStart()</code> and
51 * <code>elementEnd()</code> methods.
52 *
53 * @param config the configuration to be processed
54 */
55 public void process(Configuration config)
56 {
57 if (config != null)
58 {
59 ConfigurationKey keyEmpty = new ConfigurationKey();
60 ConfigurationKey keyLast = keyEmpty;
61 Set keySet = new HashSet();
62
63 for (Iterator it = config.getKeys(); it.hasNext();)
64 {
65 String key = (String) it.next();
66 if (keySet.contains(key))
67 {
68
69 continue;
70 }
71 ConfigurationKey keyAct = new ConfigurationKey(key);
72 closeElements(keyLast, keyAct);
73 String elem = openElements(keyLast, keyAct, config, keySet);
74 fireValue(elem, config.getProperty(key));
75 keyLast = keyAct;
76 }
77
78
79 closeElements(keyLast, keyEmpty);
80 }
81 }
82
83 /***
84 * An event handler method that is called when an element starts.
85 * Concrete sub classes must implement it to perform a proper event
86 * handling.
87 *
88 * @param name the name of the new element
89 * @param value the element's value; can be <b>null</b> if the element
90 * does not have any value
91 */
92 protected abstract void elementStart(String name, Object value);
93
94 /***
95 * An event handler method that is called when an element ends. For each
96 * call of <code>elementStart()</code> there will be a corresponding call
97 * of this method. Concrete sub classes must implement it to perform a
98 * proper event handling.
99 *
100 * @param name the name of the ending element
101 */
102 protected abstract void elementEnd(String name);
103
104 /***
105 * Fires all necessary element end events for the specified keys. This
106 * method is called for each key obtained from the configuration to be
107 * converted. It calculates the common part of the actual and the last
108 * processed key and thus determines how many elements must be
109 * closed.
110 *
111 * @param keyLast the last processed key
112 * @param keyAct the actual key
113 */
114 protected void closeElements(ConfigurationKey keyLast, ConfigurationKey keyAct)
115 {
116 ConfigurationKey keyDiff = keyAct.differenceKey(keyLast);
117 Iterator it = reverseIterator(keyDiff);
118 if (it.hasNext())
119 {
120
121 it.next();
122 }
123
124 while (it.hasNext())
125 {
126 elementEnd((String) it.next());
127 }
128 }
129
130 /***
131 * Helper method for determining a reverse iterator for the specified key.
132 * This implementation returns an iterator that returns the parts of the
133 * given key in reverse order, ignoring indices.
134 *
135 * @param key the key
136 * @return a reverse iterator for the parts of this key
137 */
138 protected Iterator reverseIterator(ConfigurationKey key)
139 {
140 List list = new ArrayList();
141 for (ConfigurationKey.KeyIterator it = key.iterator(); it.hasNext();)
142 {
143 list.add(it.nextKey());
144 }
145
146 Collections.reverse(list);
147 return list.iterator();
148 }
149
150 /***
151 * Fires all necessary element start events for the specified key. This
152 * method is called for each key obtained from the configuration to be
153 * converted. It ensures that all elements "between" the last key and the
154 * actual key are opened and their values are set.
155 *
156 * @param keyLast the last processed key
157 * @param keyAct the actual key
158 * @param config the configuration to process
159 * @param keySet the set with the processed keys
160 * @return the name of the last element on the path
161 */
162 protected String openElements(ConfigurationKey keyLast, ConfigurationKey keyAct, Configuration config, Set keySet)
163 {
164 ConfigurationKey.KeyIterator it = keyLast.differenceKey(keyAct).iterator();
165 ConfigurationKey k = keyLast.commonKey(keyAct);
166 for (it.nextKey(); it.hasNext(); it.nextKey())
167 {
168 k.append(it.currentKey(true));
169 elementStart(it.currentKey(true), config.getProperty(k.toString()));
170 keySet.add(k.toString());
171 }
172 return it.currentKey(true);
173 }
174
175 /***
176 * Fires all necessary element start events with the actual element values.
177 * This method is called for each key obtained from the configuration to be
178 * processed with the last part of the key as argument. The value can be
179 * either a single value or a collection.
180 *
181 * @param name the name of the actual element
182 * @param value the element's value
183 */
184 protected void fireValue(String name, Object value)
185 {
186 if (value != null && value instanceof Collection)
187 {
188 for (Iterator it = ((Collection) value).iterator(); it.hasNext();)
189 {
190 fireValue(name, it.next());
191 }
192 }
193 else
194 {
195 elementStart(name, value);
196 elementEnd(name);
197 }
198 }
199 }