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 }