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
025 /**
026 * <p>A subset of another configuration. The new Configuration object contains
027 * every key from the parent Configuration that starts with prefix. The prefix
028 * is removed from the keys in the subset.</p>
029 * <p>It is usually not necessary to use this class directly. Instead the
030 * <code>{@link Configuration#subset(String)}</code> method should be used,
031 * which will return a correctly initialized instance.</p>
032 *
033 * @author Emmanuel Bourg
034 * @version $Revision: 529531 $, $Date: 2007-04-17 10:52:41 +0200 (Di, 17 Apr 2007) $
035 */
036 public class SubsetConfiguration extends AbstractConfiguration
037 {
038 /** The parent configuration. */
039 protected Configuration parent;
040
041 /** The prefix used to select the properties. */
042 protected String prefix;
043
044 /** The prefix delimiter */
045 protected String delimiter;
046
047 /**
048 * Create a subset of the specified configuration
049 *
050 * @param parent The parent configuration
051 * @param prefix The prefix used to select the properties
052 */
053 public SubsetConfiguration(Configuration parent, String prefix)
054 {
055 this.parent = parent;
056 this.prefix = prefix;
057 }
058
059 /**
060 * Create a subset of the specified configuration
061 *
062 * @param parent The parent configuration
063 * @param prefix The prefix used to select the properties
064 * @param delimiter The prefix delimiter
065 */
066 public SubsetConfiguration(Configuration parent, String prefix, String delimiter)
067 {
068 this.parent = parent;
069 this.prefix = prefix;
070 this.delimiter = delimiter;
071 }
072
073 /**
074 * Return the key in the parent configuration associated to the specified
075 * key in this subset.
076 *
077 * @param key The key in the subset.
078 * @return the key as to be used by the parent
079 */
080 protected String getParentKey(String key)
081 {
082 if ("".equals(key) || key == null)
083 {
084 return prefix;
085 }
086 else
087 {
088 return delimiter == null ? prefix + key : prefix + delimiter + key;
089 }
090 }
091
092 /**
093 * Return the key in the subset configuration associated to the specified
094 * key in the parent configuration.
095 *
096 * @param key The key in the parent configuration.
097 * @return the key in the context of this subset configuration
098 */
099 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 }