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 }