001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.configuration;
019
020 import java.util.Iterator;
021
022 import org.apache.commons.collections.Transformer;
023 import org.apache.commons.collections.iterators.TransformIterator;
024 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
025
026 /**
027 * <p>A subset of another configuration. The new Configuration object contains
028 * every key from the parent Configuration that starts with prefix. The prefix
029 * is removed from the keys in the subset.</p>
030 * <p>It is usually not necessary to use this class directly. Instead the
031 * <code>{@link Configuration#subset(String)}</code> method should be used,
032 * which will return a correctly initialized instance.</p>
033 *
034 * @author Emmanuel Bourg
035 * @version $Revision: 930693 $, $Date: 2010-04-04 16:02:59 +0200 (So, 04. Apr 2010) $
036 */
037 public class SubsetConfiguration extends AbstractConfiguration
038 {
039 /** The parent configuration. */
040 protected Configuration parent;
041
042 /** The prefix used to select the properties. */
043 protected String prefix;
044
045 /** The prefix delimiter */
046 protected String delimiter;
047
048 /**
049 * Create a subset of the specified configuration
050 *
051 * @param parent The parent configuration
052 * @param prefix The prefix used to select the properties
053 */
054 public SubsetConfiguration(Configuration parent, String prefix)
055 {
056 this.parent = parent;
057 this.prefix = prefix;
058 }
059
060 /**
061 * Create a subset of the specified configuration
062 *
063 * @param parent The parent configuration
064 * @param prefix The prefix used to select the properties
065 * @param delimiter The prefix delimiter
066 */
067 public SubsetConfiguration(Configuration parent, String prefix, String delimiter)
068 {
069 this.parent = parent;
070 this.prefix = prefix;
071 this.delimiter = delimiter;
072 }
073
074 /**
075 * Return the key in the parent configuration associated to the specified
076 * key in this subset.
077 *
078 * @param key The key in the subset.
079 * @return the key as to be used by the parent
080 */
081 protected String getParentKey(String key)
082 {
083 if ("".equals(key) || key == null)
084 {
085 return prefix;
086 }
087 else
088 {
089 return delimiter == null ? prefix + key : prefix + delimiter + key;
090 }
091 }
092
093 /**
094 * Return the key in the subset configuration associated to the specified
095 * key in the parent configuration.
096 *
097 * @param key The key in the parent configuration.
098 * @return the key in the context of this subset configuration
099 */
100 protected String getChildKey(String key)
101 {
102 if (!key.startsWith(prefix))
103 {
104 throw new IllegalArgumentException("The parent key '" + key + "' is not in the subset.");
105 }
106 else
107 {
108 String modifiedKey = null;
109 if (key.length() == prefix.length())
110 {
111 modifiedKey = "";
112 }
113 else
114 {
115 int i = prefix.length() + (delimiter != null ? delimiter.length() : 0);
116 modifiedKey = key.substring(i);
117 }
118
119 return modifiedKey;
120 }
121 }
122
123 /**
124 * Return the parent configuration for this subset.
125 *
126 * @return the parent configuration
127 */
128 public Configuration getParent()
129 {
130 return parent;
131 }
132
133 /**
134 * Return the prefix used to select the properties in the parent configuration.
135 *
136 * @return the prefix used by this subset
137 */
138 public String getPrefix()
139 {
140 return prefix;
141 }
142
143 /**
144 * Set the prefix used to select the properties in the parent configuration.
145 *
146 * @param prefix the prefix
147 */
148 public void setPrefix(String prefix)
149 {
150 this.prefix = prefix;
151 }
152
153 public Configuration subset(String prefix)
154 {
155 return parent.subset(getParentKey(prefix));
156 }
157
158 public boolean isEmpty()
159 {
160 return !getKeys().hasNext();
161 }
162
163 public boolean containsKey(String key)
164 {
165 return parent.containsKey(getParentKey(key));
166 }
167
168 public void addPropertyDirect(String key, Object value)
169 {
170 parent.addProperty(getParentKey(key), value);
171 }
172
173 protected void clearPropertyDirect(String key)
174 {
175 parent.clearProperty(getParentKey(key));
176 }
177
178 public Object getProperty(String key)
179 {
180 return parent.getProperty(getParentKey(key));
181 }
182
183 public Iterator getKeys(String prefix)
184 {
185 return new TransformIterator(parent.getKeys(getParentKey(prefix)), new Transformer()
186 {
187 public Object transform(Object obj)
188 {
189 return getChildKey((String) obj);
190 }
191 });
192 }
193
194 public Iterator getKeys()
195 {
196 return new TransformIterator(parent.getKeys(prefix), new Transformer()
197 {
198 public Object transform(Object obj)
199 {
200 return getChildKey((String) obj);
201 }
202 });
203 }
204
205 protected Object interpolate(Object base)
206 {
207 if (delimiter == null && "".equals(prefix))
208 {
209 return super.interpolate(base);
210 }
211 else
212 {
213 SubsetConfiguration config = new SubsetConfiguration(parent, "");
214 ConfigurationInterpolator interpolator = config.getInterpolator();
215 getInterpolator().registerLocalLookups(interpolator);
216 if (parent instanceof AbstractConfiguration)
217 {
218 interpolator.setParentInterpolator(((AbstractConfiguration) parent).getInterpolator());
219 }
220 return config.interpolate(base);
221 }
222 }
223
224 protected String interpolate(String base)
225 {
226 return super.interpolate(base);
227 }
228
229 /**
230 * {@inheritDoc}
231 *
232 * Change the behaviour of the parent configuration if it supports this feature.
233 */
234 public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
235 {
236 if (parent instanceof AbstractConfiguration)
237 {
238 ((AbstractConfiguration) parent).setThrowExceptionOnMissing(throwExceptionOnMissing);
239 }
240 else
241 {
242 super.setThrowExceptionOnMissing(throwExceptionOnMissing);
243 }
244 }
245
246 /**
247 * {@inheritDoc}
248 *
249 * The subset inherits this feature from its parent if it supports this feature.
250 */
251 public boolean isThrowExceptionOnMissing()
252 {
253 if (parent instanceof AbstractConfiguration)
254 {
255 return ((AbstractConfiguration) parent).isThrowExceptionOnMissing();
256 }
257 else
258 {
259 return super.isThrowExceptionOnMissing();
260 }
261 }
262
263 /**
264 * Returns the list delimiter. This property will be fetched from the parent
265 * configuration if supported.
266 *
267 * @return the list delimiter
268 * @since 1.4
269 */
270 public char getListDelimiter()
271 {
272 return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent)
273 .getListDelimiter()
274 : super.getListDelimiter();
275 }
276
277 /**
278 * Sets the list delimiter. If the parent configuration supports this
279 * feature, the delimiter will be set at the parent.
280 *
281 * @param delim the new list delimiter
282 * @since 1.4
283 */
284 public void setListDelimiter(char delim)
285 {
286 if (parent instanceof AbstractConfiguration)
287 {
288 ((AbstractConfiguration) parent).setListDelimiter(delim);
289 }
290 else
291 {
292 super.setListDelimiter(delim);
293 }
294 }
295
296 /**
297 * Returns a flag whether string properties should be checked for list
298 * delimiter characters. This implementation ensures that this flag is kept
299 * in sync with the parent configuration if this object supports this
300 * feature.
301 *
302 * @return the delimiter parsing disabled flag
303 * @since 1.4
304 */
305 public boolean isDelimiterParsingDisabled()
306 {
307 return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent)
308 .isDelimiterParsingDisabled()
309 : super.isDelimiterParsingDisabled();
310 }
311
312 /**
313 * Sets a flag whether list parsing is disabled. This implementation will
314 * also set the flag at the parent configuration if this object supports
315 * this feature.
316 *
317 * @param delimiterParsingDisabled the delimiter parsing disabled flag
318 * @since 1.4
319 */
320 public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
321 {
322 if (parent instanceof AbstractConfiguration)
323 {
324 ((AbstractConfiguration) parent)
325 .setDelimiterParsingDisabled(delimiterParsingDisabled);
326 }
327 else
328 {
329 super.setDelimiterParsingDisabled(delimiterParsingDisabled);
330 }
331 }
332 }