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 018package org.apache.commons.configuration2; 019 020import java.util.Iterator; 021 022import org.apache.commons.configuration2.convert.ListDelimiterHandler; 023 024/** 025 * <p>A subset of another configuration. The new Configuration object contains 026 * every key from the parent Configuration that starts with prefix. The prefix 027 * is removed from the keys in the subset.</p> 028 * <p>It is usually not necessary to use this class directly. Instead the 029 * {@link Configuration#subset(String)} method should be used, 030 * which will return a correctly initialized instance.</p> 031 * 032 * @author Emmanuel Bourg 033 * @version $Id: SubsetConfiguration.java 1624601 2014-09-12 18:04:36Z oheger $ 034 */ 035public class SubsetConfiguration extends AbstractConfiguration 036{ 037 /** The parent configuration. */ 038 protected Configuration parent; 039 040 /** The prefix used to select the properties. */ 041 protected String prefix; 042 043 /** The prefix delimiter */ 044 protected String delimiter; 045 046 /** 047 * Create a subset of the specified configuration 048 * 049 * @param parent The parent configuration (must not be <b>null</b>) 050 * @param prefix The prefix used to select the properties 051 * @throws IllegalArgumentException if the parent configuration is <b>null</b> 052 */ 053 public SubsetConfiguration(Configuration parent, String prefix) 054 { 055 this(parent, prefix, null); 056 } 057 058 /** 059 * Create a subset of the specified configuration 060 * 061 * @param parent The parent configuration (must not be <b>null</b>) 062 * @param prefix The prefix used to select the properties 063 * @param delimiter The prefix delimiter 064 * @throws IllegalArgumentException if the parent configuration is <b>null</b> 065 */ 066 public SubsetConfiguration(Configuration parent, String prefix, String delimiter) 067 { 068 if (parent == null) 069 { 070 throw new IllegalArgumentException( 071 "Parent configuration must not be null!"); 072 } 073 074 this.parent = parent; 075 this.prefix = prefix; 076 this.delimiter = delimiter; 077 initInterpolator(); 078 } 079 080 /** 081 * Return the key in the parent configuration associated to the specified 082 * key in this subset. 083 * 084 * @param key The key in the subset. 085 * @return the key as to be used by the parent 086 */ 087 protected String getParentKey(String key) 088 { 089 if ("".equals(key) || key == null) 090 { 091 return prefix; 092 } 093 else 094 { 095 return delimiter == null ? prefix + key : prefix + delimiter + key; 096 } 097 } 098 099 /** 100 * Return the key in the subset configuration associated to the specified 101 * key in the parent configuration. 102 * 103 * @param key The key in the parent configuration. 104 * @return the key in the context of this subset configuration 105 */ 106 protected String getChildKey(String key) 107 { 108 if (!key.startsWith(prefix)) 109 { 110 throw new IllegalArgumentException("The parent key '" + key + "' is not in the subset."); 111 } 112 else 113 { 114 String modifiedKey = null; 115 if (key.length() == prefix.length()) 116 { 117 modifiedKey = ""; 118 } 119 else 120 { 121 int i = prefix.length() + (delimiter != null ? delimiter.length() : 0); 122 modifiedKey = key.substring(i); 123 } 124 125 return modifiedKey; 126 } 127 } 128 129 /** 130 * Return the parent configuration for this subset. 131 * 132 * @return the parent configuration 133 */ 134 public Configuration getParent() 135 { 136 return parent; 137 } 138 139 /** 140 * Return the prefix used to select the properties in the parent configuration. 141 * 142 * @return the prefix used by this subset 143 */ 144 public String getPrefix() 145 { 146 return prefix; 147 } 148 149 /** 150 * Set the prefix used to select the properties in the parent configuration. 151 * 152 * @param prefix the prefix 153 */ 154 public void setPrefix(String prefix) 155 { 156 this.prefix = prefix; 157 } 158 159 @Override 160 public Configuration subset(String prefix) 161 { 162 return parent.subset(getParentKey(prefix)); 163 } 164 165 @Override 166 protected boolean isEmptyInternal() 167 { 168 return !getKeysInternal().hasNext(); 169 } 170 171 @Override 172 protected boolean containsKeyInternal(String key) 173 { 174 return parent.containsKey(getParentKey(key)); 175 } 176 177 @Override 178 public void addPropertyDirect(String key, Object value) 179 { 180 parent.addProperty(getParentKey(key), value); 181 } 182 183 @Override 184 protected void clearPropertyDirect(String key) 185 { 186 parent.clearProperty(getParentKey(key)); 187 } 188 189 @Override 190 protected Object getPropertyInternal(String key) 191 { 192 return parent.getProperty(getParentKey(key)); 193 } 194 195 @Override 196 protected Iterator<String> getKeysInternal(String prefix) 197 { 198 return new SubsetIterator(parent.getKeys(getParentKey(prefix))); 199 } 200 201 @Override 202 protected Iterator<String> getKeysInternal() 203 { 204 return new SubsetIterator(parent.getKeys(prefix)); 205 } 206 207 /** 208 * {@inheritDoc} 209 * 210 * Change the behavior of the parent configuration if it supports this feature. 211 */ 212 @Override 213 public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing) 214 { 215 if (parent instanceof AbstractConfiguration) 216 { 217 ((AbstractConfiguration) parent).setThrowExceptionOnMissing(throwExceptionOnMissing); 218 } 219 else 220 { 221 super.setThrowExceptionOnMissing(throwExceptionOnMissing); 222 } 223 } 224 225 /** 226 * {@inheritDoc} 227 * 228 * The subset inherits this feature from its parent if it supports this feature. 229 */ 230 @Override 231 public boolean isThrowExceptionOnMissing() 232 { 233 if (parent instanceof AbstractConfiguration) 234 { 235 return ((AbstractConfiguration) parent).isThrowExceptionOnMissing(); 236 } 237 else 238 { 239 return super.isThrowExceptionOnMissing(); 240 } 241 } 242 243 /** 244 * {@inheritDoc} If the parent configuration extends 245 * {@link AbstractConfiguration}, the list delimiter handler is obtained 246 * from there. 247 */ 248 @Override 249 public ListDelimiterHandler getListDelimiterHandler() 250 { 251 return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent) 252 .getListDelimiterHandler() : super.getListDelimiterHandler(); 253 } 254 255 /** 256 * {@inheritDoc} If the parent configuration extends 257 * {@link AbstractConfiguration}, the list delimiter handler is passed to 258 * the parent. 259 */ 260 @Override 261 public void setListDelimiterHandler( 262 ListDelimiterHandler listDelimiterHandler) 263 { 264 if (parent instanceof AbstractConfiguration) 265 { 266 ((AbstractConfiguration) parent) 267 .setListDelimiterHandler(listDelimiterHandler); 268 } 269 else 270 { 271 super.setListDelimiterHandler(listDelimiterHandler); 272 } 273 } 274 275 /** 276 * Initializes the {@code ConfigurationInterpolator} for this sub configuration. 277 * This is a standard {@code ConfigurationInterpolator} which also references 278 * the {@code ConfigurationInterpolator} of the parent configuration. 279 */ 280 private void initInterpolator() 281 { 282 getInterpolator().setParentInterpolator(getParent().getInterpolator()); 283 } 284 285 /** 286 * A specialized iterator to be returned by the {@code getKeys()} 287 * methods. This implementation wraps an iterator from the parent 288 * configuration. The keys returned by this iterator are correspondingly 289 * transformed. 290 */ 291 private class SubsetIterator implements Iterator<String> 292 { 293 /** Stores the wrapped iterator. */ 294 private final Iterator<String> parentIterator; 295 296 /** 297 * Creates a new instance of {@code SubsetIterator} and 298 * initializes it with the parent iterator. 299 * 300 * @param it the iterator of the parent configuration 301 */ 302 public SubsetIterator(Iterator<String> it) 303 { 304 parentIterator = it; 305 } 306 307 /** 308 * Checks whether there are more elements. Delegates to the parent 309 * iterator. 310 * 311 * @return a flag whether there are more elements 312 */ 313 @Override 314 public boolean hasNext() 315 { 316 return parentIterator.hasNext(); 317 } 318 319 /** 320 * Returns the next element in the iteration. This is the next key from 321 * the parent configuration, transformed to correspond to the point of 322 * view of this subset configuration. 323 * 324 * @return the next element 325 */ 326 @Override 327 public String next() 328 { 329 return getChildKey(parentIterator.next()); 330 } 331 332 /** 333 * Removes the current element from the iteration. Delegates to the 334 * parent iterator. 335 */ 336 @Override 337 public void remove() 338 { 339 parentIterator.remove(); 340 } 341 } 342}