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