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 package org.apache.commons.configuration; 018 019 import java.io.Reader; 020 import java.io.Writer; 021 import java.math.BigDecimal; 022 import java.math.BigInteger; 023 import java.util.Collection; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Properties; 027 028 import org.apache.commons.configuration.event.ConfigurationErrorListener; 029 import org.apache.commons.configuration.event.ConfigurationListener; 030 import org.apache.commons.configuration.tree.ConfigurationNode; 031 import org.apache.commons.configuration.tree.ExpressionEngine; 032 033 /** 034 * Wraps a HierarchicalConfiguration and allows subtrees to be access via a configured path with 035 * replaceable tokens derived from the ConfigurationInterpolator. When used with injection frameworks 036 * such as Spring it allows components to be injected with subtrees of the configuration. 037 * @since 1.6 038 * @author <a 039 * href="http://commons.apache.org/configuration/team-list.html">Commons 040 * Configuration team</a> 041 * @version $Id: PatternSubtreeConfigurationWrapper.java 1210175 2011-12-04 18:49:51Z oheger $ 042 */ 043 public class PatternSubtreeConfigurationWrapper extends AbstractHierarchicalFileConfiguration 044 { 045 /** 046 * Prevent recursion while resolving unprefixed properties. 047 */ 048 private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>() 049 { 050 @Override 051 protected synchronized Boolean initialValue() 052 { 053 return Boolean.FALSE; 054 } 055 }; 056 057 /** The wrapped configuration */ 058 private final AbstractHierarchicalFileConfiguration config; 059 060 /** The path to the subtree */ 061 private final String path; 062 063 /** True if the path ends with '/', false otherwise */ 064 private final boolean trailing; 065 066 /** True if the constructor has finished */ 067 private boolean init; 068 069 /** 070 * Constructor 071 * @param config The Configuration to be wrapped. 072 * @param path The base path pattern. 073 */ 074 public PatternSubtreeConfigurationWrapper(AbstractHierarchicalFileConfiguration config, String path) 075 { 076 this.config = config; 077 this.path = path; 078 this.trailing = path.endsWith("/"); 079 this.init = true; 080 } 081 082 @Override 083 public Object getReloadLock() 084 { 085 return config.getReloadLock(); 086 } 087 088 @Override 089 public void addProperty(String key, Object value) 090 { 091 config.addProperty(makePath(key), value); 092 } 093 094 @Override 095 public void clear() 096 { 097 getConfig().clear(); 098 } 099 100 @Override 101 public void clearProperty(String key) 102 { 103 config.clearProperty(makePath(key)); 104 } 105 106 @Override 107 public boolean containsKey(String key) 108 { 109 return config.containsKey(makePath(key)); 110 } 111 112 @Override 113 public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) 114 { 115 return config.getBigDecimal(makePath(key), defaultValue); 116 } 117 118 @Override 119 public BigDecimal getBigDecimal(String key) 120 { 121 return config.getBigDecimal(makePath(key)); 122 } 123 124 @Override 125 public BigInteger getBigInteger(String key, BigInteger defaultValue) 126 { 127 return config.getBigInteger(makePath(key), defaultValue); 128 } 129 130 @Override 131 public BigInteger getBigInteger(String key) 132 { 133 return config.getBigInteger(makePath(key)); 134 } 135 136 @Override 137 public boolean getBoolean(String key, boolean defaultValue) 138 { 139 return config.getBoolean(makePath(key), defaultValue); 140 } 141 142 @Override 143 public Boolean getBoolean(String key, Boolean defaultValue) 144 { 145 return config.getBoolean(makePath(key), defaultValue); 146 } 147 148 @Override 149 public boolean getBoolean(String key) 150 { 151 return config.getBoolean(makePath(key)); 152 } 153 154 @Override 155 public byte getByte(String key, byte defaultValue) 156 { 157 return config.getByte(makePath(key), defaultValue); 158 } 159 160 @Override 161 public Byte getByte(String key, Byte defaultValue) 162 { 163 return config.getByte(makePath(key), defaultValue); 164 } 165 166 @Override 167 public byte getByte(String key) 168 { 169 return config.getByte(makePath(key)); 170 } 171 172 @Override 173 public double getDouble(String key, double defaultValue) 174 { 175 return config.getDouble(makePath(key), defaultValue); 176 } 177 178 @Override 179 public Double getDouble(String key, Double defaultValue) 180 { 181 return config.getDouble(makePath(key), defaultValue); 182 } 183 184 @Override 185 public double getDouble(String key) 186 { 187 return config.getDouble(makePath(key)); 188 } 189 190 @Override 191 public float getFloat(String key, float defaultValue) 192 { 193 return config.getFloat(makePath(key), defaultValue); 194 } 195 196 @Override 197 public Float getFloat(String key, Float defaultValue) 198 { 199 return config.getFloat(makePath(key), defaultValue); 200 } 201 202 @Override 203 public float getFloat(String key) 204 { 205 return config.getFloat(makePath(key)); 206 } 207 208 @Override 209 public int getInt(String key, int defaultValue) 210 { 211 return config.getInt(makePath(key), defaultValue); 212 } 213 214 @Override 215 public int getInt(String key) 216 { 217 return config.getInt(makePath(key)); 218 } 219 220 @Override 221 public Integer getInteger(String key, Integer defaultValue) 222 { 223 return config.getInteger(makePath(key), defaultValue); 224 } 225 226 @Override 227 public Iterator<String> getKeys() 228 { 229 return config.getKeys(makePath()); 230 } 231 232 @Override 233 public Iterator<String> getKeys(String prefix) 234 { 235 return config.getKeys(makePath(prefix)); 236 } 237 238 @Override 239 public List<Object> getList(String key, List<Object> defaultValue) 240 { 241 return config.getList(makePath(key), defaultValue); 242 } 243 244 @Override 245 public List<Object> getList(String key) 246 { 247 return config.getList(makePath(key)); 248 } 249 250 @Override 251 public long getLong(String key, long defaultValue) 252 { 253 return config.getLong(makePath(key), defaultValue); 254 } 255 256 @Override 257 public Long getLong(String key, Long defaultValue) 258 { 259 return config.getLong(makePath(key), defaultValue); 260 } 261 262 @Override 263 public long getLong(String key) 264 { 265 return config.getLong(makePath(key)); 266 } 267 268 @Override 269 public Properties getProperties(String key) 270 { 271 return config.getProperties(makePath(key)); 272 } 273 274 @Override 275 public Object getProperty(String key) 276 { 277 return config.getProperty(makePath(key)); 278 } 279 280 @Override 281 public short getShort(String key, short defaultValue) 282 { 283 return config.getShort(makePath(key), defaultValue); 284 } 285 286 @Override 287 public Short getShort(String key, Short defaultValue) 288 { 289 return config.getShort(makePath(key), defaultValue); 290 } 291 292 @Override 293 public short getShort(String key) 294 { 295 return config.getShort(makePath(key)); 296 } 297 298 @Override 299 public String getString(String key, String defaultValue) 300 { 301 return config.getString(makePath(key), defaultValue); 302 } 303 304 @Override 305 public String getString(String key) 306 { 307 return config.getString(makePath(key)); 308 } 309 310 @Override 311 public String[] getStringArray(String key) 312 { 313 return config.getStringArray(makePath(key)); 314 } 315 316 @Override 317 public boolean isEmpty() 318 { 319 return getConfig().isEmpty(); 320 } 321 322 @Override 323 public void setProperty(String key, Object value) 324 { 325 getConfig().setProperty(key, value); 326 } 327 328 @Override 329 public Configuration subset(String prefix) 330 { 331 return getConfig().subset(prefix); 332 } 333 334 @Override 335 public Node getRoot() 336 { 337 return getConfig().getRoot(); 338 } 339 340 @Override 341 public void setRoot(Node node) 342 { 343 if (init) 344 { 345 getConfig().setRoot(node); 346 } 347 else 348 { 349 super.setRoot(node); 350 } 351 } 352 353 @Override 354 public ConfigurationNode getRootNode() 355 { 356 return getConfig().getRootNode(); 357 } 358 359 @Override 360 public void setRootNode(ConfigurationNode rootNode) 361 { 362 if (init) 363 { 364 getConfig().setRootNode(rootNode); 365 } 366 else 367 { 368 super.setRootNode(rootNode); 369 } 370 } 371 372 @Override 373 public ExpressionEngine getExpressionEngine() 374 { 375 return config.getExpressionEngine(); 376 } 377 378 @Override 379 public void setExpressionEngine(ExpressionEngine expressionEngine) 380 { 381 if (init) 382 { 383 config.setExpressionEngine(expressionEngine); 384 } 385 else 386 { 387 super.setExpressionEngine(expressionEngine); 388 } 389 } 390 391 @Override 392 public void addNodes(String key, Collection<? extends ConfigurationNode> nodes) 393 { 394 getConfig().addNodes(key, nodes); 395 } 396 397 @Override 398 public SubnodeConfiguration configurationAt(String key, boolean supportUpdates) 399 { 400 return config.configurationAt(makePath(key), supportUpdates); 401 } 402 403 @Override 404 public SubnodeConfiguration configurationAt(String key) 405 { 406 return config.configurationAt(makePath(key)); 407 } 408 409 @Override 410 public List<HierarchicalConfiguration> configurationsAt(String key) 411 { 412 return config.configurationsAt(makePath(key)); 413 } 414 415 @Override 416 public void clearTree(String key) 417 { 418 config.clearTree(makePath(key)); 419 } 420 421 @Override 422 public int getMaxIndex(String key) 423 { 424 return config.getMaxIndex(makePath(key)); 425 } 426 427 @Override 428 public Configuration interpolatedConfiguration() 429 { 430 return getConfig().interpolatedConfiguration(); 431 } 432 433 @Override 434 public void addConfigurationListener(ConfigurationListener l) 435 { 436 getConfig().addConfigurationListener(l); 437 } 438 439 @Override 440 public boolean removeConfigurationListener(ConfigurationListener l) 441 { 442 return getConfig().removeConfigurationListener(l); 443 } 444 445 @Override 446 public Collection<ConfigurationListener> getConfigurationListeners() 447 { 448 return getConfig().getConfigurationListeners(); 449 } 450 451 @Override 452 public void clearConfigurationListeners() 453 { 454 getConfig().clearConfigurationListeners(); 455 } 456 457 @Override 458 public void addErrorListener(ConfigurationErrorListener l) 459 { 460 getConfig().addErrorListener(l); 461 } 462 463 @Override 464 public boolean removeErrorListener(ConfigurationErrorListener l) 465 { 466 return getConfig().removeErrorListener(l); 467 } 468 469 @Override 470 public void clearErrorListeners() 471 { 472 getConfig().clearErrorListeners(); 473 } 474 475 public void save(Writer writer) throws ConfigurationException 476 { 477 config.save(writer); 478 } 479 480 public void load(Reader reader) throws ConfigurationException 481 { 482 config.load(reader); 483 } 484 485 @Override 486 public Collection<ConfigurationErrorListener> getErrorListeners() 487 { 488 return getConfig().getErrorListeners(); 489 } 490 491 @Override 492 protected Object resolveContainerStore(String key) 493 { 494 if (recursive.get().booleanValue()) 495 { 496 return null; 497 } 498 recursive.set(Boolean.TRUE); 499 try 500 { 501 return super.resolveContainerStore(key); 502 } 503 finally 504 { 505 recursive.set(Boolean.FALSE); 506 } 507 } 508 509 private HierarchicalConfiguration getConfig() 510 { 511 return config.configurationAt(makePath()); 512 } 513 514 private String makePath() 515 { 516 String pathPattern = trailing ? path.substring(0, path.length() - 1) : path; 517 return getSubstitutor().replace(pathPattern); 518 } 519 520 /* 521 * Resolve the root expression and then add the item being retrieved. Insert a 522 * separator character as required. 523 */ 524 private String makePath(String item) 525 { 526 String pathPattern; 527 if ((item.length() == 0 || item.startsWith("/")) && trailing) 528 { 529 pathPattern = path.substring(0, path.length() - 1); 530 } 531 else if (!item.startsWith("/") || !trailing) 532 { 533 pathPattern = path + "/"; 534 } 535 else 536 { 537 pathPattern = path; 538 } 539 return getSubstitutor().replace(pathPattern) + item; 540 } 541 }