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.Iterator;
21  
22  import org.apache.commons.collections.Transformer;
23  import org.apache.commons.collections.iterators.TransformIterator;
24  
25  /***
26   * <p>A subset of another configuration. The new Configuration object contains
27   * every key from the parent Configuration that starts with prefix. The prefix
28   * is removed from the keys in the subset.</p>
29   * <p>It is usually not necessary to use this class directly. Instead the
30   * <code>{@link Configuration#subset(String)}</code> method should be used,
31   * which will return a correctly initialized instance.</p>
32   *
33   * @author Emmanuel Bourg
34   * @version $Revision: 529531 $, $Date: 2007-04-17 10:52:41 +0200 (Di, 17 Apr 2007) $
35   */
36  public class SubsetConfiguration extends AbstractConfiguration
37  {
38      /*** The parent configuration. */
39      protected Configuration parent;
40  
41      /*** The prefix used to select the properties. */
42      protected String prefix;
43  
44      /*** The prefix delimiter */
45      protected String delimiter;
46  
47      /***
48       * Create a subset of the specified configuration
49       *
50       * @param parent The parent configuration
51       * @param prefix The prefix used to select the properties
52       */
53      public SubsetConfiguration(Configuration parent, String prefix)
54      {
55          this.parent = parent;
56          this.prefix = prefix;
57      }
58  
59      /***
60       * Create a subset of the specified configuration
61       *
62       * @param parent    The parent configuration
63       * @param prefix    The prefix used to select the properties
64       * @param delimiter The prefix delimiter
65       */
66      public SubsetConfiguration(Configuration parent, String prefix, String delimiter)
67      {
68          this.parent = parent;
69          this.prefix = prefix;
70          this.delimiter = delimiter;
71      }
72  
73      /***
74       * Return the key in the parent configuration associated to the specified
75       * key in this subset.
76       *
77       * @param key The key in the subset.
78       * @return the key as to be used by the parent
79       */
80      protected String getParentKey(String key)
81      {
82          if ("".equals(key) || key == null)
83          {
84              return prefix;
85          }
86          else
87          {
88              return delimiter == null ? prefix + key : prefix + delimiter + key;
89          }
90      }
91  
92      /***
93       * Return the key in the subset configuration associated to the specified
94       * key in the parent configuration.
95       *
96       * @param key The key in the parent configuration.
97       * @return the key in the context of this subset configuration
98       */
99      protected String getChildKey(String key)
100     {
101         if (!key.startsWith(prefix))
102         {
103             throw new IllegalArgumentException("The parent key '" + key + "' is not in the subset.");
104         }
105         else
106         {
107             String modifiedKey = null;
108             if (key.length() == prefix.length())
109             {
110                 modifiedKey = "";
111             }
112             else
113             {
114                 int i = prefix.length() + (delimiter != null ? delimiter.length() : 0);
115                 modifiedKey = key.substring(i);
116             }
117 
118             return modifiedKey;
119         }
120     }
121 
122     /***
123      * Return the parent configuation for this subset.
124      *
125      * @return the parent configuration
126      */
127     public Configuration getParent()
128     {
129         return parent;
130     }
131 
132     /***
133      * Return the prefix used to select the properties in the parent configuration.
134      *
135      * @return the prefix used by this subset
136      */
137     public String getPrefix()
138     {
139         return prefix;
140     }
141 
142     /***
143      * Set the prefix used to select the properties in the parent configuration.
144      *
145      * @param prefix the prefix
146      */
147     public void setPrefix(String prefix)
148     {
149         this.prefix = prefix;
150     }
151 
152     public Configuration subset(String prefix)
153     {
154         return parent.subset(getParentKey(prefix));
155     }
156 
157     public boolean isEmpty()
158     {
159         return !getKeys().hasNext();
160     }
161 
162     public boolean containsKey(String key)
163     {
164         return parent.containsKey(getParentKey(key));
165     }
166 
167     public void addPropertyDirect(String key, Object value)
168     {
169         parent.addProperty(getParentKey(key), value);
170     }
171 
172     public void setProperty(String key, Object value)
173     {
174         parent.setProperty(getParentKey(key), value);
175     }
176 
177     public void clearProperty(String key)
178     {
179         parent.clearProperty(getParentKey(key));
180     }
181 
182     public Object getProperty(String key)
183     {
184         return parent.getProperty(getParentKey(key));
185     }
186 
187     public Iterator getKeys(String prefix)
188     {
189         return new TransformIterator(parent.getKeys(getParentKey(prefix)), new Transformer()
190         {
191             public Object transform(Object obj)
192             {
193                 return getChildKey((String) obj);
194             }
195         });
196     }
197 
198     public Iterator getKeys()
199     {
200         return new TransformIterator(parent.getKeys(prefix), new Transformer()
201         {
202             public Object transform(Object obj)
203             {
204                 return getChildKey((String) obj);
205             }
206         });
207     }
208 
209     protected Object interpolate(Object base)
210     {
211         if (delimiter == null && "".equals(prefix))
212         {
213             return super.interpolate(base);
214         }
215         else
216         {
217             SubsetConfiguration config = new SubsetConfiguration(parent, "");
218             return config.interpolate(base);
219         }
220     }
221 
222     protected String interpolate(String base)
223     {
224         return super.interpolate(base);
225     }
226 
227     /***
228      * {@inheritDoc}
229      *
230      * Change the behaviour of the parent configuration if it supports this feature.
231      */
232     public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
233     {
234         if (parent instanceof AbstractConfiguration)
235         {
236             ((AbstractConfiguration) parent).setThrowExceptionOnMissing(throwExceptionOnMissing);
237         }
238         else
239         {
240             super.setThrowExceptionOnMissing(throwExceptionOnMissing);
241         }
242     }
243 
244     /***
245      * {@inheritDoc}
246      *
247      * The subset inherits this feature from its parent if it supports this feature.
248      */
249     public boolean isThrowExceptionOnMissing()
250     {
251         if (parent instanceof AbstractConfiguration)
252         {
253             return ((AbstractConfiguration) parent).isThrowExceptionOnMissing();
254         }
255         else
256         {
257             return super.isThrowExceptionOnMissing();
258         }
259     }
260 
261     /***
262      * Returns the list delimiter. This property will be fetched from the parent
263      * configuration if supported.
264      *
265      * @return the list delimiter
266      * @since 1.4
267      */
268     public char getListDelimiter()
269     {
270         return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent)
271                 .getListDelimiter()
272                 : super.getListDelimiter();
273     }
274 
275     /***
276      * Sets the list delimiter. If the parent configuration supports this
277      * feature, the delimiter will be set at the parent.
278      *
279      * @param delim the new list delimiter
280      * @since 1.4
281      */
282     public void setListDelimiter(char delim)
283     {
284         if (parent instanceof AbstractConfiguration)
285         {
286             ((AbstractConfiguration) parent).setListDelimiter(delim);
287         }
288         else
289         {
290             super.setListDelimiter(delim);
291         }
292     }
293 
294     /***
295      * Returns a flag whether string properties should be checked for list
296      * delimiter characters. This implementation ensures that this flag is kept
297      * in sync with the parent configuration if this object supports this
298      * feature.
299      *
300      * @return the delimiter parsing disabled flag
301      * @since 1.4
302      */
303     public boolean isDelimiterParsingDisabled()
304     {
305         return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent)
306                 .isDelimiterParsingDisabled()
307                 : super.isDelimiterParsingDisabled();
308     }
309 
310     /***
311      * Sets a flag whether list parsing is disabled. This implementation will
312      * also set the flag at the parent configuration if this object supports
313      * this feature.
314      *
315      * @param delimiterParsingDisabled the delimiter parsing disabled flag
316      * @since 1.4
317      */
318     public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
319     {
320         if (parent instanceof AbstractConfiguration)
321         {
322             ((AbstractConfiguration) parent)
323                     .setDelimiterParsingDisabled(delimiterParsingDisabled);
324         }
325         else
326         {
327             super.setDelimiterParsingDisabled(delimiterParsingDisabled);
328         }
329     }
330 }