Coverage details for org.chwf.config.ConfigImpl

LineHitsSource
1 /*
2 Chrysalis Web Framework [http://chrysalis.sourceforge.net]
3 Copyright (c) 2002, 2003, 2004, Paul Strack
4  
5 All rights reserved.
6  
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9  
10 1. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12  
13 2. Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation
15 and/or other materials provided with the distribution.
16  
17 3. Neither the name of the copyright holder nor the names of its contributors
18 may be used to endorse or promote products derived from this software without
19 specific prior written permission.
20  
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
25 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32  
33 package org.chwf.config;
34  
35 import java.text.NumberFormat;
36 import java.text.ParseException;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.Iterator;
40 import java.util.Locale;
41 import java.util.Map;
42 import java.util.SortedMap;
43 import java.util.StringTokenizer;
44 import java.util.TreeMap;
45  
46 import org.chwf.util.SequencedHashMap;
47  
48 /**
49  * <p>A class for retrieving configuration data from properties and XML files.
50  * See the package documentation for further information.</p>
51  *
52  * @author <a href="mailto:pfstrack@users.sourceforge.net">Paul Strack</a>
53  */
54 class ConfigImpl implements Config, RawConfig {
55  
56   /** Empty string array. */
571  private static final String[] EMPTY_STRING_ARRAY = new String[0];
58  
59   /** Empty string. */
60   private static final String EMPTY_STRING = "";
61  
62   /** Name of configuration resource. */
63   private final String resource;
64  
65   /** Localized name of resource. */
66   private final String localizedResource;
67  
68   /** Map containing configuration data. */
69   private final Map data;
70  
71   /** Reference to parent configuration object. */
72   private final ConfigImpl parentConfig;
73  
74   /** Reference to config for parent locale. */
75   private final ConfigImpl parentLocale;
76  
77   /** Reference to config for parent locale. */
78   private final boolean disableChildConfig;
79  
80   /** Property for disabling parent configuration: "disable.parent.config". */
81   private static final String DISABLE_PARENT_CONFIG = "disable.parent.config";
82  
83   /** Property for disabling child configuration: "disable.child.config". */
84   private static final String DISABLE_CHILD_CONFIG = "disable.child.config";
85  
86   /** Constant for wildcard. */
87   private static final String WILDCARD = "*";
88  
89   /** Character delimiting values in delimited lists. */
90   private static final String LIST_DELIMITER = ",";
91  
92   /** No-parameter constructor for subclasses. */
931  ConfigImpl() {
941    this.resource = null;
951    this.localizedResource = null;
961    this.data = null;
971    this.parentConfig = null;
981    this.parentLocale = null;
991    this.disableChildConfig = false;
1001  }
101  
102   /**
103    * Class-based constructor for subclasses.
104    *
105    * @param contextClass The context class.
106    * @param locale The locale.
107    */
108378  ConfigImpl(Class contextClass, Locale locale) {
109378    this.resource = contextClass.getName();
110378    this.localizedResource =
111       this.resource + ConfigFactory.deriveLocaleName(locale);
112378    this.data = null;
113378    this.parentConfig = null;
114378    this.parentLocale = null;
115378    this.disableChildConfig = false;
116378  }
117  
118   /**
119    * Constructor using the resource name.
120    *
121    * @param resource The name of the resource.
122    * @param locale The locale.
123    */
1243544  ConfigImpl(String resource, Locale locale) {
1253544    this.resource = resource;
1263544    this.localizedResource =
127       this.resource + ConfigFactory.deriveLocaleName(locale);
1283544    this.data = ConfigFactory.initData(localizedResource);
129  
1303542    boolean disableChildConfig = false;
1313542    if (this.data.get(DISABLE_PARENT_CONFIG) == null) {
1323525      this.parentConfig =
133         (ConfigImpl) ConfigFactory.deriveParentConfig(resource, locale);
1343525      disableChildConfig = this.parentConfig.disableChildConfig;
1353525      if (this.parentConfig.disableChildConfig) {
1361        data.clear();
137       }
138     } else {
13917      this.parentConfig = (ConfigImpl) ConfigFactory.TERMINAL_CONFIG;
140     }
1413542    if (this.data.get(DISABLE_CHILD_CONFIG) != null) {
1421      disableChildConfig = true;
143     }
144  
1453542    this.disableChildConfig = disableChildConfig;
1463542    this.parentLocale =
147       (ConfigImpl) ConfigFactory.deriveParentLocale(resource, locale);
1483542  }
149  
150   /**
151    * Gets the named property value.<p>
152    *
153    * @param key The search key.
154    * @return The property value.
155    * @throws ConfigurationException If the property is not found.
156    */
157   public String get(String key) throws ConfigurationException {
158135    String value = getRawValue(key);
159135    if (value != null) {
160121      return value;
161     } else {
16214      Object[] arguments = { this.localizedResource, key };
16314      throw new ConfigurationException(
164         ConfigurationException.MESSAGE_PROPERTY_NOT_FOUND,
165         arguments);
166     }
167   }
168  
169   /**
170    * Gets the named property value.<p>
171    *
172    * @param key The search key.
173    * @param defaultValue A default value.
174    * @return The property value or the default value if the property is
175    * not found.
176    */
177   public String get(String key, String defaultValue) {
178642    String value = getRawValue(key);
179642    if (value != null) {
180178      return value;
181     } else {
182464      return defaultValue;
183     }
184   }
185  
186   /**
187    * Gets the named property value as an int value. String values in the
188    * configuration file are parsed using the system locale.<p>
189    *
190    * @param key The search key.
191    * @return The property value as an int.
192    * @throws ConfigurationException If the property is not found or is not a
193    * number.
194    */
195   public int getInt(String key) throws ConfigurationException {
196     try {
1976      return NumberFormat.getNumberInstance().parse(get(key)).intValue();
198     } catch (ParseException ex) {
1992      throw new ConfigurationException(
200         ConfigurationException.MESSAGE_PROPERTY_NOT_NUMBER,
201         new Object[] { this.localizedResource, key });
202     }
203   }
204  
205   /**
206    * Gets the named property value as an int value. String values in the
207    * configuration file are parsed using the system locale.<p>
208    *
209    * @param key The search key.
210    * @param defaultValue A default value.
211    * @return The property value as an int, or the default value if
212    * the property is not found or is not a number.
213    */
214   public int getInt(String key, int defaultValue) {
215     try {
2166      return NumberFormat.getNumberInstance().parse(get(key)).intValue();
217     } catch (Exception ex) {
2184      return defaultValue;
219     }
220   }
221  
222   /**
223    * Gets the named property value as a double value. String values in the
224    * configuration file are parsed using the system locale.<p>
225    *
226    * @param key The search key.
227    * @return The property value as a double.
228    * @throws ConfigurationException If the property is not found or is not a
229    * number.
230    */
231   public double getDouble(String key) throws ConfigurationException {
232     try {
2336      return NumberFormat.getNumberInstance().parse(get(key)).doubleValue();
234     } catch (ParseException ex) {
2352      throw new ConfigurationException(
236         ConfigurationException.MESSAGE_PROPERTY_NOT_NUMBER,
237         new Object[] { this.localizedResource, key });
238     }
239   }
240  
241   /**
242    * Gets the named property value as a double value. String values in the
243    * configuration file are parsed using the system locale.<p>
244    *
245    * @param key The search key.
246    * @param defaultValue A default value.
247    * @return The property value as a double, or the default value if
248    * the property is not found or is not a number.
249    */
250   public double getDouble(String key, double defaultValue) {
251     try {
2526      return NumberFormat.getNumberInstance().parse(get(key)).doubleValue();
253     } catch (Exception ex) {
2544      return defaultValue;
255     }
256   }
257  
258   /**
259    * Gets the named property value as a boolean value.<p>
260    *
261    * @param key The search key.
262    * @return The property value as a boolean.
263    * @throws ConfigurationException If the property is not found.
264    */
265   public boolean getBoolean(String key) throws ConfigurationException {
2666    return Boolean.valueOf(get(key)).booleanValue();
267   }
268  
269   /**
270    * Gets the named property value as a boolean value.<p>
271    *
272    * @param key The search key.
273    * @param defaultValue A default value.
274    * @return The property value as a boolean, or the default value if
275    * the property is not found.
276    */
277   public boolean getBoolean(String key, boolean defaultValue) {
278108    String value = getRawValue(key);
279108    if (value != null) {
2803      return Boolean.valueOf(get(key)).booleanValue();
281     } else {
282105      return defaultValue;
283     }
284   }
285  
286   /**
287    * <p>Gets an array of strings for a property key. This is done in
288    * one of two ways:</p>
289    *
290    * <ul>
291    * <li>Get a list of all entries beginning with the specified key.</li>
292    * <li>If there is only one entry corresponding to the key, parse
293    * that entry as a comma-delimited list.</li>
294    * </ul>
295    *
296    * <p>Note that this method never returns an empty list. If the property
297    * is not found, a <code>ConfigurationException</code> is thrown. If
298    * you need to allow for the possibility of empty lists, use the values
299    * of the <code>getMap()</code> method instead:</p>
300    *
301    * <pre>
302    * Collection list = config.getMap().values();</pre>
303    *
304    * @param key The search key.
305    * @return The list of property values.
306    * @throws ConfigurationException If the property is not found.
307    */
308   public String[] getList(String key) throws ConfigurationException {
30984    String[] list = getList(key, null);
31084    if (list == null) {
31174      throw new ConfigurationException(
312         ConfigurationException.MESSAGE_PROPERTY_NOT_FOUND,
313         new Object[] { this.localizedResource, key });
314     } else {
31510      return list;
316     }
317   }
318  
319   /**
320    * <p>As {@link #getList(java.lang.String)}, but with a default.</p>
321    *
322    * @param key The search key.
323    * @param defaultList The default list.
324    * @return The list of property values or the default if not found.
325    */
326   public String[] getList(String key, String[] defaultList) {
327410    SortedMap map = getMap(key);
328410    Collection data = map.values();
329410    if (data.size() == 0) {
330344      return defaultList;
33166    } else if (
332       (data.size() == 1)
333         && (map.keySet().iterator().next().equals(EMPTY_STRING))) {
33447      return getDelimitedList(key);
335     } else {
33619      String[] list = new String[data.size()];
33719      data.toArray(list);
33819      return list;
339     }
340   }
341  
342   /**
343    * <p>Gets a sorted map consisting of all entries beginning with
344    * the specified key. The keys in the sorted map will be the
345    * property keys with the search key (and any leading ".")
346    * removed. For example, suppose the following values are in
347    * the property file:</p>
348    *
349    * <pre>
350    * map.value.1=Value 1
351    * map.value.2=Value 2
352    * map.value.3=Value 3</pre>
353    *
354    * <p>The method call <code>config.getMap("map.value")</code> will
355    * result in a map with 3 values, whose keys are "1", "2" and "3".</p>
356    *
357    * <p>Note that the sort order is based on <i>string</i> ordering,
358    * not numeric ordering. This means if you have more than 10 values,
359    * you will have to number them more carefully if the ordering is
360    * important:</p>
361    *
362    * <pre>
363    * map.value.01=Value 1
364    * map.value.02=Value 2
365    * map.value.03=Value 3
366    * map.value.04=Value 4
367    * map.value.05=Value 5
368    * map.value.06=Value 6
369    * map.value.07=Value 7
370    * map.value.08=Value 8
371    * map.value.09=Value 9
372    * map.value.10=Value 10
373    * map.value.11=Value 11</pre>
374    *
375    * @param key The search key, which is used as the initial part
376    * of property keys.
377    * @return The sorted map, or an empty map if no property keys
378    * beginning with the search key.
379    */
380   public SortedMap getMap(String key) {
3811375    Map map = getOrderedMap(key);
3821375    if (map.isEmpty()) {
3831232      return ConfigFactory.EMPTY_SORTED_MAP;
384     }
385143    return new TreeMap(map);
386   }
387  
388   /**
389    * <p>Gets an ordered map consisting of all entries beginning with the
390    * specified key. The keys in the sorted map will be the property keys
391    * with the search key (and any leading ".") removed. If the configuration
392    * data is in an XML file, the data retains the sequential ordering of the
393    * elements in the XML file. For example, suppose the XML data were as
394    * follows:</p>
395    *
396    * <pre>
397    * &lt;map.values>
398    * &lt;value name="C">Item 1&lt;/data>
399    * &lt;value name="B">Item 2&lt;/data>
400    * &lt;value name="A">Item 3&lt;/data>
401    * &lt;map.values></pre>
402    *
403    * <p>The method call <code>config.getOrderedMap("map.values")</code> will
404    * result in a map with 3 values, whose keys are "C", "B" and "A" in that
405    * order. Compare this to the <code>getMap()</code> method, which sorts keys
406    * in alphabetical order rather than in sequential order.</p>
407    *
408    * <p>This method only functions correctly for configuration data in XML
409    * files, because property files are not ordered. Using this method for
410    * data in property files will result in keys sorted in pseudo-random
411    * order.</p>
412    *
413    * @param key The search key, which is used as the initial part
414    * of property keys.
415    * @return The ordered map, or an empty map if no property keys
416    * beginning with the search key.
417    */
418   public Map getOrderedMap(String key) {
419952    Map map = getMapInConfig(key);
420952    if (map.isEmpty()) {
421928      map = getParentConfig().getMapWithContext(this.resource, key);
422     }
423952    if (map.isEmpty()) {
424829      map = this.parentLocale.getMap(key);
425     }
426952    return map;
427   }
428  
429   /**
430    * Get the raw configuration data. Parent configuration data is not included.
431    *
432    * @return The list of keys for the current Config object.
433    */
434   public Map getRawData() {
4352    return Collections.unmodifiableMap(this.data);
436   }
437  
438   /**
439    * <p>Gets a sorted map based on the context.
440    *
441    * @param context The search context.
442    * @param key The search key, which is used as the initial part
443    * of property keys.
444    * @return The sorted map, or an empty map if no property keys
445    * beginning with the search key.
446    */
447   Map getMapWithContext(String context, String key) {
4483886    String baseName = ConfigFactory.stripLastName(this.resource);
4493886    String searchContext = context.substring(baseName.length());
4503886    Map map = null;
451  
45219027    while (searchContext.length() > 0) {
45311350      String prefixedKey = searchContext + ConfigFactory.KEY_DELIMITER + key;
45411350      map = getMapInConfig(prefixedKey);
45511350      if (!map.isEmpty()) {
45695        return map;
457       }
45811255      searchContext = ConfigFactory.stripLastNameAndPeriod(searchContext);
459     }
460  
4613791    map = getMapInConfig(key);
4623791    if (map.isEmpty()) {
4633787      map = getParentConfig().getMapWithContext(context, key);
464     }
465  
4663791    return map;
467   }
468  
469   /**
470    * Gets configuration property using the specified context name.
471    * This method checks all possible prefixed and suffixed keys and recurses
472    * up the configuration search path. It has package-level access so it
473    * can be overridden in the TerminalConfig subclass.
474    *
475    * @param context The search context.
476    * @param key The search key.
477    * @return The value or <code>null</code> if it does not exist.
478    */
479   String getWithContext(String context, String key) {
4805699    String baseName = ConfigFactory.stripLastName(this.resource);
4815699    String searchContext = context.substring(baseName.length());
4825699    String value = null;
483  
48427911    while (searchContext.length() > 0) {
48516742      String prefixedKey = searchContext + ConfigFactory.KEY_DELIMITER + key;
48616742      value = (String) data.get(prefixedKey);
48716742      if (value != null) {
488229        return value;
489       }
49016513      searchContext = ConfigFactory.stripLastNameAndPeriod(searchContext);
491     }
492  
4935470    value = (String) data.get(key);
4945470    if (value == null) {
4955460      value = getParentConfig().getWithContext(context, key);
496     }
497  
4985470    return value;
499   }
500  
501   /**
502    * Get parent Config object in search path.
503    *
504    * @return The parent configuration.
505    */
506   private ConfigImpl getParentConfig() {
50711511    return this.parentConfig;
508   }
509  
510   /**
511    * Get raw data value.
512    *
513    * @param key The search key.
514    * @return The value or <code>null</code> if it does not exist.
515    */
516   public String getRawValue(String key) {
5171401    String value = (String) data.get(key);
5181401    if (value == null) {
5191336      value = getParentConfig().getWithContext(this.resource, key);
520     }
5211401    if (value == null) {
5221097      value = this.parentLocale.getRawValue(key);
523     }
5241401    return value;
525   }
526  
527   /**
528    * Get raw data map in a single Config object.
529    *
530    * @param key The search key.
531    * @return A map with values beginning with the given key.
532    */
533   private Map getMapInConfig(String key) {
53416093    int keyLength = key.length();
53516093    Map map = new SequencedHashMap();
53616093    Iterator i = this.data.keySet().iterator();
53771720    while (i.hasNext()) {
53839534      String dataKey = i.next().toString();
53939534      String mapKey = deriveMapKey(key, dataKey);
54039534      if (mapKey != null) {
541299        if ((mapKey.length() > 0)
542           && (mapKey.charAt(0) == ConfigFactory.KEY_DELIMITER)) {
543211          mapKey = mapKey.substring(1);
544         }
545299        map.put(mapKey, data.get(dataKey));
546       }
547     }
54816093    return map;
549   }
550  
551   /**
552    * Derive submap key from key pattern and actual key
553    *
554    * @param key The key pattern (with wildcards)
555    * @param dataKey The actual key
556    * @return The derived submap key or null if it does not match
557    */
558   private static String deriveMapKey(String key, String dataKey) {
55939534    StringBuffer mapKey = new StringBuffer();
56039534    StringTokenizer tokenizer = new StringTokenizer(key, WILDCARD);
56139534    int lastMatchPosition = -1;
56239534    boolean isFirstToken = true;
56379429    while (tokenizer.hasMoreTokens()) {
56439596      String token = tokenizer.nextToken();
56539596      int currentMatchPosition = dataKey.indexOf(token, lastMatchPosition);
56639596      if (isFirstToken) {
56739534        if (!dataKey.startsWith(token)) {
56839192          return null;
569         }
570       }
571404      if (lastMatchPosition >= currentMatchPosition) {
57243        return null;
573       } else {
574361        if (!isFirstToken) {
57519          String wildcard =
576             dataKey.substring(lastMatchPosition, currentMatchPosition);
57719          mapKey.append(wildcard);
578         }
579361        lastMatchPosition = currentMatchPosition + token.length();
580       }
581361      isFirstToken = false;
582     }
583299    mapKey.append(dataKey.substring(lastMatchPosition));
584299    return mapKey.toString();
585   }
586  
587   /**
588    * Gets the named property value by parsing it as a comma-delimited list.
589    *
590    * @param key The search key.
591    * @return An array containing the list of property values.
592    * @throws ConfigurationException If the property is not found.
593    */
594   private String[] getDelimitedList(String key) throws ConfigurationException {
59547    String value = get(key).trim();
59647    if (value.length() == 0) {
5971      return EMPTY_STRING_ARRAY;
598     }
59946    StringTokenizer t = new StringTokenizer(value, LIST_DELIMITER);
60046    String[] array = new String[t.countTokens()];
60146    int count = 0;
602147    while (t.hasMoreTokens()) {
60355      array[count++] = t.nextToken().trim();
604     }
60546    return array;
606   }
607  
608 }

this report was generated by version 1.0.5 of jcoverage.
visit www.jcoverage.com for updates.

copyright © 2003, jcoverage ltd. all rights reserved.
Java is a trademark of Sun Microsystems, Inc. in the United States and other countries.