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.io.File; 021 import java.io.InputStream; 022 import java.io.OutputStream; 023 import java.io.Reader; 024 import java.io.Writer; 025 import java.net.URL; 026 import java.util.Collection; 027 import java.util.Iterator; 028 import java.util.List; 029 030 import org.apache.commons.configuration.event.ConfigurationEvent; 031 import org.apache.commons.configuration.event.ConfigurationListener; 032 import org.apache.commons.configuration.reloading.ReloadingStrategy; 033 034 /** 035 * <p>Base class for implementing file based hierarchical configurations.</p> 036 * <p>This class serves an analogous purpose as the 037 * <code>{@link AbstractFileConfiguration}</code> class for non hierarchical 038 * configurations. It behaves in exactly the same way, so please refer to the 039 * documentation of <code>AbstractFileConfiguration</code> for further details.</p> 040 * 041 * @since 1.2 042 * 043 * @author Emmanuel Bourg 044 * @version $Revision: 712405 $, $Date: 2008-11-08 17:37:48 +0100 (Sa, 08 Nov 2008) $ 045 */ 046 public abstract class AbstractHierarchicalFileConfiguration 047 extends HierarchicalConfiguration 048 implements FileConfiguration, ConfigurationListener 049 { 050 /** Stores the delegate used for implementing functionality related to the 051 * <code>FileConfiguration</code> interface. 052 */ 053 private FileConfigurationDelegate delegate; 054 055 /** 056 * Creates a new instance of 057 * <code>AbstractHierarchicalFileConfiguration</code>. 058 */ 059 protected AbstractHierarchicalFileConfiguration() 060 { 061 initialize(); 062 } 063 064 /** 065 * Creates a new instance of 066 * <code>AbstractHierarchicalFileConfiguration</code> and copies the 067 * content of the specified configuration into this object. 068 * 069 * @param c the configuration to copy 070 * @since 1.4 071 */ 072 protected AbstractHierarchicalFileConfiguration(HierarchicalConfiguration c) 073 { 074 super(c); 075 initialize(); 076 } 077 078 /** 079 * Creates and loads the configuration from the specified file. 080 * 081 * @param fileName The name of the plist file to load. 082 * @throws ConfigurationException Error while loading the file 083 */ 084 public AbstractHierarchicalFileConfiguration(String fileName) throws ConfigurationException 085 { 086 this(); 087 // store the file name 088 delegate.setFileName(fileName); 089 090 // load the file 091 load(); 092 } 093 094 /** 095 * Creates and loads the configuration from the specified file. 096 * 097 * @param file The configuration file to load. 098 * @throws ConfigurationException Error while loading the file 099 */ 100 public AbstractHierarchicalFileConfiguration(File file) throws ConfigurationException 101 { 102 this(); 103 // set the file and update the url, the base path and the file name 104 setFile(file); 105 106 // load the file 107 if (file.exists()) 108 { 109 load(); 110 } 111 } 112 113 /** 114 * Creates and loads the configuration from the specified URL. 115 * 116 * @param url The location of the configuration file to load. 117 * @throws ConfigurationException Error while loading the file 118 */ 119 public AbstractHierarchicalFileConfiguration(URL url) throws ConfigurationException 120 { 121 this(); 122 // set the URL and update the base path and the file name 123 setURL(url); 124 125 // load the file 126 load(); 127 } 128 129 /** 130 * Initializes this instance, mainly the internally used delegate object. 131 */ 132 private void initialize() 133 { 134 delegate = createDelegate(); 135 initDelegate(delegate); 136 } 137 138 protected void addPropertyDirect(String key, Object obj) 139 { 140 super.addPropertyDirect(key, obj); 141 delegate.possiblySave(); 142 } 143 144 public void clearProperty(String key) 145 { 146 super.clearProperty(key); 147 delegate.possiblySave(); 148 } 149 150 public void clearTree(String key) 151 { 152 super.clearTree(key); 153 delegate.possiblySave(); 154 } 155 156 public void setProperty(String key, Object value) 157 { 158 super.setProperty(key, value); 159 delegate.possiblySave(); 160 } 161 162 public void load() throws ConfigurationException 163 { 164 delegate.load(); 165 } 166 167 public void load(String fileName) throws ConfigurationException 168 { 169 delegate.load(fileName); 170 } 171 172 public void load(File file) throws ConfigurationException 173 { 174 delegate.load(file); 175 } 176 177 public void load(URL url) throws ConfigurationException 178 { 179 delegate.load(url); 180 } 181 182 public void load(InputStream in) throws ConfigurationException 183 { 184 delegate.load(in); 185 } 186 187 public void load(InputStream in, String encoding) throws ConfigurationException 188 { 189 delegate.load(in, encoding); 190 } 191 192 public void save() throws ConfigurationException 193 { 194 delegate.save(); 195 } 196 197 public void save(String fileName) throws ConfigurationException 198 { 199 delegate.save(fileName); 200 } 201 202 public void save(File file) throws ConfigurationException 203 { 204 delegate.save(file); 205 } 206 207 public void save(URL url) throws ConfigurationException 208 { 209 delegate.save(url); 210 } 211 212 public void save(OutputStream out) throws ConfigurationException 213 { 214 delegate.save(out); 215 } 216 217 public void save(OutputStream out, String encoding) throws ConfigurationException 218 { 219 delegate.save(out, encoding); 220 } 221 222 public String getFileName() 223 { 224 return delegate.getFileName(); 225 } 226 227 public void setFileName(String fileName) 228 { 229 delegate.setFileName(fileName); 230 } 231 232 public String getBasePath() 233 { 234 return delegate.getBasePath(); 235 } 236 237 public void setBasePath(String basePath) 238 { 239 delegate.setBasePath(basePath); 240 } 241 242 public File getFile() 243 { 244 return delegate.getFile(); 245 } 246 247 public void setFile(File file) 248 { 249 delegate.setFile(file); 250 } 251 252 public URL getURL() 253 { 254 return delegate.getURL(); 255 } 256 257 public void setURL(URL url) 258 { 259 delegate.setURL(url); 260 } 261 262 public void setAutoSave(boolean autoSave) 263 { 264 delegate.setAutoSave(autoSave); 265 } 266 267 public boolean isAutoSave() 268 { 269 return delegate.isAutoSave(); 270 } 271 272 public ReloadingStrategy getReloadingStrategy() 273 { 274 return delegate.getReloadingStrategy(); 275 } 276 277 public void setReloadingStrategy(ReloadingStrategy strategy) 278 { 279 delegate.setReloadingStrategy(strategy); 280 } 281 282 public void reload() 283 { 284 setDetailEvents(false); 285 try 286 { 287 delegate.reload(); 288 } 289 finally 290 { 291 setDetailEvents(true); 292 } 293 } 294 295 public String getEncoding() 296 { 297 return delegate.getEncoding(); 298 } 299 300 public void setEncoding(String encoding) 301 { 302 delegate.setEncoding(encoding); 303 } 304 305 public boolean containsKey(String key) 306 { 307 reload(); 308 return super.containsKey(key); 309 } 310 311 public Iterator getKeys() 312 { 313 reload(); 314 return super.getKeys(); 315 } 316 317 public Iterator getKeys(String prefix) 318 { 319 reload(); 320 return super.getKeys(prefix); 321 } 322 323 public Object getProperty(String key) 324 { 325 reload(); 326 return super.getProperty(key); 327 } 328 329 public boolean isEmpty() 330 { 331 reload(); 332 return super.isEmpty(); 333 } 334 335 /** 336 * Directly adds sub nodes to this configuration. This implementation checks 337 * whether auto save is necessary after executing the operation. 338 * 339 * @param key the key where the nodes are to be added 340 * @param nodes a collection with the nodes to be added 341 * @since 1.5 342 */ 343 public void addNodes(String key, Collection nodes) 344 { 345 super.addNodes(key, nodes); 346 delegate.possiblySave(); 347 } 348 349 /** 350 * Fetches a list of nodes, which are selected by the specified key. This 351 * implementation will perform a reload if necessary. 352 * 353 * @param key the key 354 * @return a list with the selected nodes 355 */ 356 protected List fetchNodeList(String key) 357 { 358 reload(); 359 return super.fetchNodeList(key); 360 } 361 362 /** 363 * Reacts on changes of an associated subnode configuration. If the auto 364 * save mechanism is active, the configuration must be saved. 365 * 366 * @param event the event describing the change 367 * @since 1.5 368 */ 369 protected void subnodeConfigurationChanged(ConfigurationEvent event) 370 { 371 delegate.possiblySave(); 372 super.subnodeConfigurationChanged(event); 373 } 374 375 /** 376 * Creates the file configuration delegate, i.e. the object that implements 377 * functionality required by the <code>FileConfiguration</code> interface. 378 * This base implementation will return an instance of the 379 * <code>FileConfigurationDelegate</code> class. Derived classes may 380 * override it to create a different delegate object. 381 * 382 * @return the file configuration delegate 383 */ 384 protected FileConfigurationDelegate createDelegate() 385 { 386 return new FileConfigurationDelegate(); 387 } 388 389 /** 390 * Helper method for initializing the file configuration delegate. 391 * 392 * @param del the delegate 393 */ 394 private void initDelegate(FileConfigurationDelegate del) 395 { 396 del.addConfigurationListener(this); 397 } 398 399 /** 400 * Reacts on configuration change events triggered by the delegate. These 401 * events are passed to the registered configuration listeners. 402 * 403 * @param event the triggered event 404 * @since 1.3 405 */ 406 public void configurationChanged(ConfigurationEvent event) 407 { 408 // deliver reload events to registered listeners 409 setDetailEvents(true); 410 try 411 { 412 fireEvent(event.getType(), event.getPropertyName(), event 413 .getPropertyValue(), event.isBeforeUpdate()); 414 } 415 finally 416 { 417 setDetailEvents(false); 418 } 419 } 420 421 /** 422 * Returns the file configuration delegate. 423 * 424 * @return the delegate 425 */ 426 protected FileConfigurationDelegate getDelegate() 427 { 428 return delegate; 429 } 430 431 /** 432 * Allows to set the file configuration delegate. 433 * @param delegate the new delegate 434 */ 435 protected void setDelegate(FileConfigurationDelegate delegate) 436 { 437 this.delegate = delegate; 438 } 439 440 /** 441 * A special implementation of the <code>FileConfiguration</code> interface that is 442 * used internally to implement the <code>FileConfiguration</code> methods 443 * for hierarchical configurations. 444 */ 445 protected class FileConfigurationDelegate extends AbstractFileConfiguration 446 { 447 public void load(Reader in) throws ConfigurationException 448 { 449 AbstractHierarchicalFileConfiguration.this.load(in); 450 } 451 452 public void save(Writer out) throws ConfigurationException 453 { 454 AbstractHierarchicalFileConfiguration.this.save(out); 455 } 456 457 public void clear() 458 { 459 AbstractHierarchicalFileConfiguration.this.clear(); 460 } 461 } 462 }