View Javadoc

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