KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jcckit > util > FlatConfigData


1 /*
2  * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved
3  *
4  * This library is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details
13  * (http://www.gnu.org/copyleft/lesser.html).
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19 package jcckit.util;
20
21 /**
22  * An implementation of <tt>ConfigData</tt> based on a flat
23  * representation of the hierachically organized key-value pairs.
24  * Concrete subclasses must implement the methods
25  * {@link #getValue} and {@link #createConfigData} in accordance
26  * with the Template Method pattern and Factory Method pattern,
27  * respectively.
28  * <p>
29  * In a flat representation of hierachically organized key-value
30  * pairs all key-value pairs are stored in a single <tt>Hashtable</tt>.
31  * Its key is the <em>full key</em> of the configuration data (i.e. the key
32  * including its path).
33  * <p>
34  * Example (using the notation for a <tt>.properties</tt> file):
35  * <pre>
36  * title = example
37  * symbolAttributes/className = jcckit.graphic.BasicDrawingAttributes
38  * symbolAttributes/fillColor = 0xcaffee
39  * symbolAttributes/lineColor = 0xff0000
40  * </pre>
41  * The following table shows the result of some method calls at a
42  * <tt>FlatConfigData</tt> instance prepared with
43  * this example:
44  * <p>
45  * <center>
46  * <table border=1 cellspacing=1 cellpadding=5>
47  * <tr><th>Method call</th><th>Result</th></tr>
48  * <tr><td>get(&quot;title&quot;)</td><td>example</td></tr>
49  * <tr><td>getNode(&quot;symbolAttributes&quot;).get(&quot;fillColor&quot;)
50  * </td><td>0xcaffee</td></tr>
51  * </table>
52  * </center>
53  * <p>
54  * In addition <tt>FlatConfigData</tt> implements <b>inheritance</b>
55  * of key-value pairs.
56  * Basically a node in the tree of key-value pairs
57  * may extend another node in the tree.
58  * The extended node inherit all key-value pairs from the extending
59  * one including the key-value pairs of all descendants.
60  * The value of a inherited key-value pair may be overridden.
61  * Also new key-value pairs may be placed in the inherited node or
62  * anywhere in the subtree.
63  * Note, that the extending node has to be a node which is not a
64  * descendant of the extended node (otherwise a circulary chain
65  * of references occurs). As a consequence not more than 20 inheritance
66  * levels are allowed.
67  * <p>
68  * The implementation of this kind of inheritance in a flat hashtable
69  * is done by an additional key-value pair of the form
70  * <pre>
71  * <i>extending-node</i><b>/</b> = <i>extended-node</i><b>/</b>
72  * </pre>
73  * Example:
74  * <pre>
75  * A/a/priority = high
76  * A/a/alpha/hello = universe
77  * A/a/alpha/answer = 42
78  * <b>A/b/1/ = A/a/</b>
79  * A/b/1/alpha/hello = world
80  * A/b/1/alpha/question = 6 * 7
81  * </pre>
82  * The following table shows the result of various method calls
83  * applied at the node <tt>A/b/1/</tt> of a <tt>FlatConfigData</tt>
84  * instance prepared with this example:
85  * <p>
86  * <center>
87  * <table border=1 cellspacing=1 cellpadding=5>
88  * <tr><th>Method call</th><th>Result</th><th>Comment</th></tr>
89  * <tr><td>get(&quot;priority&quot;)</td><td>high</td><td>inherited</td></tr>
90  * <tr><td>getNode(&quot;alpha&quot;).get(&quot;hello&quot;)
91  * </td><td>world</td><td>overridden</td></tr>
92  * <tr><td>getNode(&quot;alpha&quot;).get(&quot;question&quot;)
93  * </td><td>6 * 7</td><td>added</td></tr>
94  * <tr><td>getNode(&quot;alpha&quot;).get(&quot;answer&quot;)
95  * </td><td>42</td><td>inherited</td></tr>
96  * </table>
97  * </center>
98  *
99  * @author Franz-Josef Elmer
100  */

101 public abstract class FlatConfigData implements ConfigData {
102   private final String JavaDoc _path;
103
104   /** Creates a new instance for the specified path. */
105   public FlatConfigData(String JavaDoc path) {
106     _path = path;
107   }
108
109   /**
110    * Returns the full key.
111    * @param key A (relative) key. <tt>null</tt> is not allowed.
112    * @return the path concatenated with <tt>key</tt> or <tt>key</tt>
113    * if the path is undefined.
114    */

115   public String JavaDoc getFullKey(String JavaDoc key) {
116     return _path == null ? key : _path + key;
117   }
118
119   /**
120    * Returns the value associated with this key.
121    * @param key The relative key. <tt>null</tt> is not allowed.
122    * @return the associated value. Will be <tt>null</tt> if no value exists
123    * for <tt>key</tt>.
124    */

125   public String JavaDoc get(String JavaDoc key) {
126     return get(_path, key, 0);
127   }
128
129   /**
130    * Obtains a value in accordance with hierarchy (<tt>path</tt>) and
131    * inheritance (recursive calls of this routine).
132    */

133   private String JavaDoc get(String JavaDoc path, String JavaDoc key, int numberOfLevels) {
134     String JavaDoc result = null;
135     if (numberOfLevels < 20) {
136       String JavaDoc fullKey = path == null ? key : path + key;
137       result = getValue(fullKey);
138       if (result == null) {
139         // posAfterDelim is the index in path just after '/'
140
int posAfterDelim = path == null ? -1 : path.length();
141         String JavaDoc replacement;
142         while (posAfterDelim > 0) {
143           // look for a sub-tree
144
replacement = getValue(path.substring(0, posAfterDelim));
145           if (replacement != null) {
146             // sub-tree found, add last part of the original path
147
result = get(replacement + path.substring(posAfterDelim), key,
148                          numberOfLevels + 1);
149             // break whether result is null or not.
150
break;
151           }
152           // remove last element from the path
153
posAfterDelim = path.lastIndexOf('/', posAfterDelim - 2) + 1;
154         }
155       }
156     }
157     return result;
158   }
159
160   /**
161    * Returns the <tt>ConfigData</tt> object associated with this key.
162    * @param key The relative key.
163    * @return the associated value. Will never return <tt>null</tt>.
164    * Instead an empty <tt>ConfigData</tt> is returned.
165    */

166   public ConfigData getNode(String JavaDoc key) {
167     String JavaDoc path = (_path == null ? key : _path + key) + '/';
168     return createConfigData(path);
169   }
170
171   /**
172    * Returns the value for the specified full key from the flat
173    * representation of the hierarchically organized key-value pairs.
174    * @param fullKey The full key including path. <tt>null</tt> is not allowed.
175    * @return the value or <tt>null</tt> if not found.
176    */

177   protected abstract String JavaDoc getValue(String JavaDoc fullKey);
178
179   /**
180    * Returns the <tt>FlatConfigData</tt> object for the specified full path.
181    * In general <tt>path</tt> will be used in the constructor with
182    * path argument.
183    * @param path The full path.
184    * @return a new instance in any case.
185    */

186   protected abstract ConfigData createConfigData(String JavaDoc path);
187 }
188
Popular Tags