View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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                      // this key has already been processed by openElements
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              // close all open
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             // Skip first because it has already been closed by fireValue()
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 }