Coverage Report - org.apache.commons.configuration.AbstractFileConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractFileConfiguration
91%
202/221
88%
36/41
2,34
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.configuration;
 19  
 
 20  
 import java.io.File;
 21  
 import java.io.FileOutputStream;
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.io.InputStreamReader;
 25  
 import java.io.OutputStream;
 26  
 import java.io.OutputStreamWriter;
 27  
 import java.io.Reader;
 28  
 import java.io.UnsupportedEncodingException;
 29  
 import java.io.Writer;
 30  
 import java.net.MalformedURLException;
 31  
 import java.net.URL;
 32  
 import java.util.Iterator;
 33  
 
 34  
 import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
 35  
 import org.apache.commons.configuration.reloading.ReloadingStrategy;
 36  
 import org.apache.commons.lang.StringUtils;
 37  
 
 38  
 /**
 39  
  * <p>Partial implementation of the <code>FileConfiguration</code> interface.
 40  
  * Developpers of file based configuration may want to extend this class,
 41  
  * the two methods left to implement are <code>{@link FileConfiguration#load(Reader)}</code>
 42  
  * and <code>{@link FileConfiguration#save(Writer)}.</p>
 43  
  * <p>This base class already implements a couple of ways to specify the location
 44  
  * of the file this configuration is based on. The following possibilities
 45  
  * exist:
 46  
  * <ul><li>URLs: With the method <code>setURL()</code> a full URL to the
 47  
  * configuration source can be specified. This is the most flexible way. Note
 48  
  * that the <code>save()</code> methods support only <em>file:</em> URLs.</li>
 49  
  * <li>Files: The <code>setFile()</code> method allows to specify the
 50  
  * configuration source as a file. This can be either a relative or an
 51  
  * absolute file. In the former case the file is resolved based on the current
 52  
  * directory.</li>
 53  
  * <li>As file paths in string form: With the <code>setPath()</code> method a
 54  
  * full path to a configuration file can be provided as a string.</li>
 55  
  * <li>Separated as base path and file name: This is the native form in which
 56  
  * the location is stored. The base path is a string defining either a local
 57  
  * directory or a URL. It can be set using the <code>setBasePath()</code>
 58  
  * method. The file name, non surprisingly, defines the name of the configuration
 59  
  * file.</li></ul></p>
 60  
  * <p>Note that the <code>load()</code> methods do not wipe out the configuration's
 61  
  * content before the new configuration file is loaded. Thus it is very easy to
 62  
  * construct a union configuration by simply loading multiple configuration
 63  
  * files, e.g.</p>
 64  
  * <p><pre>
 65  
  * config.load(configFile1);
 66  
  * config.load(configFile2);
 67  
  * </pre></p>
 68  
  * <p>After executing this code fragment, the resulting configuration will
 69  
  * contain both the properties of configFile1 and configFile2. On the other
 70  
  * hand, if the current configuration file is to be reloaded, <code>clear()</code>
 71  
  * should be called first. Otherwise the properties are doubled. This behavior
 72  
  * is analogous to the behavior of the <code>load(InputStream)</code> method
 73  
  * in <code>java.util.Properties</code>.</p>
 74  
  *
 75  
  * @author Emmanuel Bourg
 76  
  * @version $Revision: 439648 $, $Date: 2006-09-02 22:42:10 +0200 (Sa, 02 Sep 2006) $
 77  
  * @since 1.0-rc2
 78  
  */
 79  
 public abstract class AbstractFileConfiguration extends BaseConfiguration implements FileConfiguration
 80  
 {
 81  
     /** Constant for the configuration reload event.*/
 82  
     public static final int EVENT_RELOAD = 20;
 83  
 
 84  
     /** Stores the file name.*/
 85  
     protected String fileName;
 86  
 
 87  
     /** Stores the base path.*/
 88  
     protected String basePath;
 89  
 
 90  
     /** The auto save flag.*/
 91  
     protected boolean autoSave;
 92  
 
 93  
     /** Holds a reference to the reloading strategy.*/
 94  
     protected ReloadingStrategy strategy;
 95  
 
 96  
     /** A lock object for protecting reload operations.*/
 97  794
     private Object reloadLock = new Object();
 98  
 
 99  
     /** Stores the encoding of the configuration file.*/
 100  
     private String encoding;
 101  
 
 102  
     /** Stores the URL from which the configuration file was loaded.*/
 103  
     private URL sourceURL;
 104  
 
 105  
     /** A counter that prohibits reloading.*/
 106  
     private int noReload;
 107  
 
 108  
     /**
 109  
      * Default constructor
 110  
      *
 111  
      * @since 1.1
 112  
      */
 113  
     public AbstractFileConfiguration()
 114  794
     {
 115  794
         initReloadingStrategy();
 116  794
     }
 117  
 
 118  
     /**
 119  
      * Creates and loads the configuration from the specified file. The passed
 120  
      * in string must be a valid file name, either absolute or relativ.
 121  
      *
 122  
      * @param fileName The name of the file to load.
 123  
      *
 124  
      * @throws ConfigurationException Error while loading the file
 125  
      * @since 1.1
 126  
      */
 127  
     public AbstractFileConfiguration(String fileName) throws ConfigurationException
 128  
     {
 129  220
         this();
 130  
 
 131  
         // store the file name
 132  220
         setFileName(fileName);
 133  
 
 134  
         // load the file
 135  220
         load();
 136  218
     }
 137  
 
 138  
     /**
 139  
      * Creates and loads the configuration from the specified file.
 140  
      *
 141  
      * @param file The file to load.
 142  
      * @throws ConfigurationException Error while loading the file
 143  
      * @since 1.1
 144  
      */
 145  
     public AbstractFileConfiguration(File file) throws ConfigurationException
 146  
     {
 147  9
         this();
 148  
 
 149  
         // set the file and update the url, the base path and the file name
 150  9
         setFile(file);
 151  
 
 152  
         // load the file
 153  9
         if (file.exists())
 154  
         {
 155  8
             load();
 156  
         }
 157  8
     }
 158  
 
 159  
     /**
 160  
      * Creates and loads the configuration from the specified URL.
 161  
      *
 162  
      * @param url The location of the file to load.
 163  
      * @throws ConfigurationException Error while loading the file
 164  
      * @since 1.1
 165  
      */
 166  
     public AbstractFileConfiguration(URL url) throws ConfigurationException
 167  
     {
 168  1
         this();
 169  
 
 170  
         // set the URL and update the base path and the file name
 171  1
         setURL(url);
 172  
 
 173  
         // load the file
 174  1
         load();
 175  1
     }
 176  
 
 177  
     /**
 178  
      * Load the configuration from the underlying location.
 179  
      *
 180  
      * @throws ConfigurationException if loading of the configuration fails
 181  
      */
 182  
     public void load() throws ConfigurationException
 183  
     {
 184  724
         if (sourceURL != null)
 185  
         {
 186  192
             load(sourceURL);
 187  
         }
 188  
         else
 189  
         {
 190  532
             load(getFileName());
 191  
         }
 192  703
     }
 193  
 
 194  
     /**
 195  
      * Locate the specified file and load the configuration. This does not
 196  
      * change the source of the configuration (i.e. the internally maintained file name).
 197  
      * Use one of the setter methods for this purpose.
 198  
      *
 199  
      * @param fileName the name of the file to be loaded
 200  
      * @throws ConfigurationException if an error occurs
 201  
      */
 202  
     public void load(String fileName) throws ConfigurationException
 203  
     {
 204  
         try
 205  
         {
 206  537
             URL url = ConfigurationUtils.locate(basePath, fileName);
 207  
 
 208  537
             if (url == null)
 209  
             {
 210  18
                 throw new ConfigurationException("Cannot locate configuration source " + fileName);
 211  
             }
 212  519
             load(url);
 213  515
         }
 214  
         catch (ConfigurationException e)
 215  
         {
 216  22
             throw e;
 217  
         }
 218  
         catch (Exception e)
 219  
         {
 220  0
             throw new ConfigurationException(e.getMessage(), e);
 221  
         }
 222  515
     }
 223  
 
 224  
     /**
 225  
      * Load the configuration from the specified file. This does not change
 226  
      * the source of the configuration (i.e. the internally maintained file
 227  
      * name). Use one of the setter methods for this purpose.
 228  
      *
 229  
      * @param file the file to load
 230  
      * @throws ConfigurationException if an error occurs
 231  
      */
 232  
     public void load(File file) throws ConfigurationException
 233  
     {
 234  
         try
 235  
         {
 236  12
             load(file.toURL());
 237  9
         }
 238  
         catch (ConfigurationException e)
 239  
         {
 240  3
             throw e;
 241  
         }
 242  
         catch (Exception e)
 243  
         {
 244  0
             throw new ConfigurationException(e.getMessage(), e);
 245  
         }
 246  9
     }
 247  
 
 248  
     /**
 249  
      * Load the configuration from the specified URL. This does not change the
 250  
      * source of the configuration (i.e. the internally maintained file name).
 251  
      * Use on of the setter methods for this purpose.
 252  
      *
 253  
      * @param url the URL of the file to be loaded
 254  
      * @throws ConfigurationException if an error occurs
 255  
      */
 256  
     public void load(URL url) throws ConfigurationException
 257  
     {
 258  1079
         if (sourceURL == null)
 259  
         {
 260  531
             if (StringUtils.isEmpty(getBasePath()))
 261  
             {
 262  
                 // ensure that we have a valid base path
 263  280
                 setBasePath(url.toString());
 264  
             }
 265  531
             sourceURL = url;
 266  
         }
 267  
 
 268  
         // throw an exception if the target URL is a directory
 269  1079
         File file = ConfigurationUtils.fileFromURL(url);
 270  1079
         if (file != null && file.isDirectory())
 271  
         {
 272  4
             throw new ConfigurationException("Cannot load a configuration from a directory");
 273  
         }
 274  
 
 275  1075
         InputStream in = null;
 276  
 
 277  
         try
 278  
         {
 279  1075
             in = url.openStream();
 280  1075
             load(in);
 281  1072
         }
 282  
         catch (ConfigurationException e)
 283  
         {
 284  3
             throw e;
 285  
         }
 286  
         catch (Exception e)
 287  
         {
 288  0
             throw new ConfigurationException(e.getMessage(), e);
 289  
         }
 290  
         finally
 291  
         {
 292  
             // close the input stream
 293  3
             try
 294  
             {
 295  1075
                 if (in != null)
 296  
                 {
 297  1075
                     in.close();
 298  
                 }
 299  1075
             }
 300  
             catch (IOException e)
 301  
             {
 302  0
                 e.printStackTrace();
 303  1075
             }
 304  
         }
 305  1072
     }
 306  
 
 307  
     /**
 308  
      * Load the configuration from the specified stream, using the encoding
 309  
      * returned by {@link #getEncoding()}.
 310  
      *
 311  
      * @param in the input stream
 312  
      *
 313  
      * @throws ConfigurationException if an error occurs during the load operation
 314  
      */
 315  
     public void load(InputStream in) throws ConfigurationException
 316  
     {
 317  852
         load(in, getEncoding());
 318  851
     }
 319  
 
 320  
     /**
 321  
      * Load the configuration from the specified stream, using the specified
 322  
      * encoding. If the encoding is null the default encoding is used.
 323  
      *
 324  
      * @param in the input stream
 325  
      * @param encoding the encoding used. <code>null</code> to use the default encoding
 326  
      *
 327  
      * @throws ConfigurationException if an error occurs during the load operation
 328  
      */
 329  
     public void load(InputStream in, String encoding) throws ConfigurationException
 330  
     {
 331  853
         Reader reader = null;
 332  
 
 333  853
         if (encoding != null)
 334  
         {
 335  
             try
 336  
             {
 337  451
                 reader = new InputStreamReader(in, encoding);
 338  451
             }
 339  
             catch (UnsupportedEncodingException e)
 340  
             {
 341  0
                 throw new ConfigurationException(
 342  
                         "The requested encoding is not supported, try the default encoding.", e);
 343  
             }
 344  
         }
 345  
 
 346  853
         if (reader == null)
 347  
         {
 348  402
             reader = new InputStreamReader(in);
 349  
         }
 350  
 
 351  853
         load(reader);
 352  852
     }
 353  
 
 354  
     /**
 355  
      * Save the configuration. Before this method can be called a valid file
 356  
      * name must have been set.
 357  
      *
 358  
      * @throws ConfigurationException if an error occurs or no file name has
 359  
      * been set yet
 360  
      */
 361  
     public void save() throws ConfigurationException
 362  
     {
 363  27
         if (getFileName() == null)
 364  
         {
 365  4
             throw new ConfigurationException("No file name has been set!");
 366  
         }
 367  
 
 368  23
         if (sourceURL != null)
 369  
         {
 370  15
             save(sourceURL);
 371  
         }
 372  
         else
 373  
         {
 374  8
             save(fileName);
 375  
         }
 376  23
         strategy.init();
 377  23
     }
 378  
 
 379  
     /**
 380  
      * Save the configuration to the specified file. This doesn't change the
 381  
      * source of the configuration, use setFileName() if you need it.
 382  
      *
 383  
      * @param fileName the file name
 384  
      *
 385  
      * @throws ConfigurationException if an error occurs during the save operation
 386  
      */
 387  
     public void save(String fileName) throws ConfigurationException
 388  
     {
 389  
         try
 390  
         {
 391  15
             File file = ConfigurationUtils.getFile(basePath, fileName);
 392  15
             if (file == null)
 393  
             {
 394  1
                 throw new ConfigurationException("Invalid file name for save: " + fileName);
 395  
             }
 396  14
             save(file);
 397  14
         }
 398  
         catch (ConfigurationException e)
 399  
         {
 400  1
             throw e;
 401  
         }
 402  
         catch (Exception e)
 403  
         {
 404  0
             throw new ConfigurationException(e.getMessage(), e);
 405  
         }
 406  14
     }
 407  
 
 408  
     /**
 409  
      * Save the configuration to the specified URL if it's a file URL.
 410  
      * This doesn't change the source of the configuration, use setURL()
 411  
      * if you need it.
 412  
      *
 413  
      * @param url the URL
 414  
      *
 415  
      * @throws ConfigurationException if an error occurs during the save operation
 416  
      */
 417  
     public void save(URL url) throws ConfigurationException
 418  
     {
 419  17
         File file = ConfigurationUtils.fileFromURL(url);
 420  17
         if (file != null)
 421  
         {
 422  16
             save(file);
 423  
         }
 424  
         else
 425  
         {
 426  1
             throw new ConfigurationException("Could not save to URL " + url);
 427  
         }
 428  16
     }
 429  
 
 430  
     /**
 431  
      * Save the configuration to the specified file. The file is created
 432  
      * automatically if it doesn't exist. This doesn't change the source
 433  
      * of the configuration, use {@link #setFile} if you need it.
 434  
      *
 435  
      * @param file the target file
 436  
      *
 437  
      * @throws ConfigurationException if an error occurs during the save operation
 438  
      */
 439  
     public void save(File file) throws ConfigurationException
 440  
     {
 441  39
         OutputStream out = null;
 442  
 
 443  
         try
 444  
         {
 445  
             // create the file if necessary
 446  39
             createPath(file);
 447  39
             out = new FileOutputStream(file);
 448  39
             save(out);
 449  38
         }
 450  
         catch (IOException e)
 451  
         {
 452  0
             throw new ConfigurationException(e.getMessage(), e);
 453  
         }
 454  
         finally
 455  
         {
 456  
             // close the output stream
 457  1
             try
 458  
             {
 459  39
                 if (out != null)
 460  
                 {
 461  39
                     out.close();
 462  
                 }
 463  39
             }
 464  
             catch (IOException e)
 465  
             {
 466  0
                 e.printStackTrace();
 467  39
             }
 468  
         }
 469  38
     }
 470  
 
 471  
     /**
 472  
      * Save the configuration to the specified stream, using the encoding
 473  
      * returned by {@link #getEncoding()}.
 474  
      *
 475  
      * @param out the output stream
 476  
      *
 477  
      * @throws ConfigurationException if an error occurs during the save operation
 478  
      */
 479  
     public void save(OutputStream out) throws ConfigurationException
 480  
     {
 481  40
         save(out, getEncoding());
 482  39
     }
 483  
 
 484  
     /**
 485  
      * Save the configuration to the specified stream, using the specified
 486  
      * encoding. If the encoding is null the default encoding is used.
 487  
      *
 488  
      * @param out the output stream
 489  
      * @param encoding the encoding to use
 490  
      * @throws ConfigurationException if an error occurs during the save operation
 491  
      */
 492  
     public void save(OutputStream out, String encoding) throws ConfigurationException
 493  
     {
 494  41
         Writer writer = null;
 495  
 
 496  41
         if (encoding != null)
 497  
         {
 498  
             try
 499  
             {
 500  25
                 writer = new OutputStreamWriter(out, encoding);
 501  25
             }
 502  
             catch (UnsupportedEncodingException e)
 503  
             {
 504  0
                 throw new ConfigurationException(
 505  
                         "The requested encoding is not supported, try the default encoding.", e);
 506  
             }
 507  
         }
 508  
 
 509  41
         if (writer == null)
 510  
         {
 511  16
             writer = new OutputStreamWriter(out);
 512  
         }
 513  
 
 514  41
         save(writer);
 515  40
     }
 516  
 
 517  
     /**
 518  
      * Return the name of the file.
 519  
      *
 520  
      * @return the file name
 521  
      */
 522  
     public String getFileName()
 523  
     {
 524  687
         return fileName;
 525  
     }
 526  
 
 527  
     /**
 528  
      * Set the name of the file. The passed in file name can contain a
 529  
      * relative path.
 530  
      * It must be used when referring files with relative paths from classpath.
 531  
      * Use <code>{@link AbstractFileConfiguration#setPath(String)
 532  
      * setPath()}</code> to set a full qualified file name.
 533  
      *
 534  
      * @param fileName the name of the file
 535  
      */
 536  
     public void setFileName(String fileName)
 537  
     {
 538  615
         sourceURL = null;
 539  615
         this.fileName = fileName;
 540  615
     }
 541  
 
 542  
     /**
 543  
      * Return the base path.
 544  
      *
 545  
      * @return the base path
 546  
      */
 547  
     public String getBasePath()
 548  
     {
 549  969
         return basePath;
 550  
     }
 551  
 
 552  
     /**
 553  
      * Set the base path. Relative configurations are loaded from this path. The
 554  
      * base path can be either a path to a directory or a URL.
 555  
      *
 556  
      * @param basePath the base path.
 557  
      */
 558  
     public void setBasePath(String basePath)
 559  
     {
 560  577
         sourceURL = null;
 561  577
         this.basePath = basePath;
 562  577
     }
 563  
 
 564  
     /**
 565  
      * Return the file where the configuration is stored. If the base path is a
 566  
      * URL with a protocol different than &quot;file&quot;, or the configuration
 567  
      * file is within a compressed archive, the return value
 568  
      * will not point to a valid file object.
 569  
      *
 570  
      * @return the file where the configuration is stored; this can be <b>null</b>
 571  
      */
 572  
     public File getFile()
 573  
     {
 574  11
         if (getFileName() == null)
 575  
         {
 576  1
             return null;
 577  
         }
 578  
         else
 579  
         {
 580  10
             if (sourceURL != null)
 581  
             {
 582  1
                 return ConfigurationUtils.fileFromURL(sourceURL);
 583  
             }
 584  
             else
 585  
             {
 586  9
                 return ConfigurationUtils.getFile(getBasePath(), getFileName());
 587  
             }
 588  
         }
 589  
     }
 590  
 
 591  
     /**
 592  
      * Set the file where the configuration is stored. The passed in file is
 593  
      * made absolute if it is not yet. Then the file's path component becomes
 594  
      * the base path and its name component becomes the file name.
 595  
      *
 596  
      * @param file the file where the configuration is stored
 597  
      */
 598  
     public void setFile(File file)
 599  
     {
 600  165
         sourceURL = null;
 601  165
         setFileName(file.getName());
 602  165
         setBasePath((file.getParentFile() != null) ? file.getParentFile()
 603  
                 .getAbsolutePath() : null);
 604  165
     }
 605  
 
 606  
     /**
 607  
      * Returns the full path to the file this configuration is based on. The
 608  
      * return value is a valid File path only if this configuration is based on
 609  
      * a file on the local disk.
 610  
      * If the configuration was loaded from a packed archive the returned value
 611  
      * is the string form of the URL from which the configuration was loaded.
 612  
      *
 613  
      * @return the full path to the configuration file
 614  
      */
 615  
     public String getPath()
 616  
     {
 617  2
         String path = null;
 618  2
         File file = getFile();
 619  
         // if resource was loaded from jar file may be null
 620  2
         if (file != null)
 621  
         {
 622  2
             path = file.getAbsolutePath();
 623  
         }
 624  
 
 625  
         // try to see if file was loaded from a jar
 626  2
         if (path == null)
 627  
         {
 628  0
             if (sourceURL != null)
 629  
             {
 630  0
                 path = sourceURL.getPath();
 631  
             }
 632  
             else
 633  
             {
 634  
                 try {
 635  0
                     path = ConfigurationUtils.getURL(getBasePath(),
 636  
                             getFileName()).getPath();
 637  0
                 } catch (MalformedURLException e) {
 638  
                     // simply ignore it and return null
 639  
                 }
 640  
             }
 641  
         }
 642  
 
 643  2
         return path;
 644  
     }
 645  
 
 646  
     /**
 647  
      * Sets the location of this configuration as a full or relative path name.
 648  
      * The passed in path should represent a valid file name on the file system.
 649  
      * It must not be used to specify relative paths for files that exist
 650  
      * in classpath, either plain file system or compressed archive,
 651  
      * because this method expands any relative path to an absolute one which
 652  
      * may end in an invalid absolute path for classpath references.
 653  
      *
 654  
      * @param path the full path name of the configuration file
 655  
      */
 656  
     public void setPath(String path)
 657  
     {
 658  1
         setFile(new File(path));
 659  1
     }
 660  
 
 661  
     /**
 662  
      * Return the URL where the configuration is stored.
 663  
      *
 664  
      * @return the configuration's location as URL
 665  
      */
 666  
     public URL getURL()
 667  
     {
 668  630
         return (sourceURL != null) ? sourceURL
 669  
                 : ConfigurationUtils.locate(getBasePath(), getFileName());
 670  
     }
 671  
 
 672  
     /**
 673  
      * Set the location of this configuration as a URL. For loading this can be
 674  
      * an arbitrary URL with a supported protocol. If the configuration is to
 675  
      * be saved, too, a URL with the &quot;file&quot; protocol should be
 676  
      * provided.
 677  
      *
 678  
      * @param url the location of this configuration as URL
 679  
      */
 680  
     public void setURL(URL url)
 681  
     {
 682  14
         setBasePath(ConfigurationUtils.getBasePath(url));
 683  14
         setFileName(ConfigurationUtils.getFileName(url));
 684  14
         sourceURL = url;
 685  14
     }
 686  
 
 687  
     public void setAutoSave(boolean autoSave)
 688  
     {
 689  1639
         this.autoSave = autoSave;
 690  1639
     }
 691  
 
 692  
     public boolean isAutoSave()
 693  
     {
 694  818
         return autoSave;
 695  
     }
 696  
 
 697  
     /**
 698  
      * Save the configuration if the automatic persistence is enabled
 699  
      * and if a file is specified.
 700  
      */
 701  
     protected void possiblySave()
 702  
     {
 703  19801
         if (autoSave && fileName != null)
 704  
         {
 705  
             try
 706  
             {
 707  14
                 save();
 708  14
             }
 709  
             catch (ConfigurationException e)
 710  
             {
 711  0
                 throw new ConfigurationRuntimeException("Failed to auto-save", e);
 712  
             }
 713  
         }
 714  19801
     }
 715  
 
 716  
     /**
 717  
      * Adds a new property to this configuration. This implementation checks if
 718  
      * the auto save mode is enabled and saves the configuration if necessary.
 719  
      *
 720  
      * @param key the key of the new property
 721  
      * @param value the value
 722  
      */
 723  
     public void addProperty(String key, Object value)
 724  
     {
 725  19462
         super.addProperty(key, value);
 726  19462
         possiblySave();
 727  19462
     }
 728  
 
 729  
     /**
 730  
      * Sets a new value for the specified property. This implementation checks
 731  
      * if the auto save mode is enabled and saves the configuration if
 732  
      * necessary.
 733  
      *
 734  
      * @param key the key of the affected property
 735  
      * @param value the value
 736  
      */
 737  
     public void setProperty(String key, Object value)
 738  
     {
 739  17
         super.setProperty(key, value);
 740  17
         possiblySave();
 741  17
     }
 742  
 
 743  
     public void clearProperty(String key)
 744  
     {
 745  37
         super.clearProperty(key);
 746  37
         possiblySave();
 747  37
     }
 748  
 
 749  
     public ReloadingStrategy getReloadingStrategy()
 750  
     {
 751  7
         return strategy;
 752  
     }
 753  
 
 754  
     public void setReloadingStrategy(ReloadingStrategy strategy)
 755  
     {
 756  814
         this.strategy = strategy;
 757  814
         strategy.setConfiguration(this);
 758  814
         strategy.init();
 759  814
     }
 760  
 
 761  
     public void reload()
 762  
     {
 763  23655
         synchronized (reloadLock)
 764  
         {
 765  23655
             if (noReload == 0)
 766  
             {
 767  
                 try
 768  
                 {
 769  14027
                     enterNoReload(); // avoid reentrant calls
 770  
 
 771  14027
                     if (strategy.reloadingRequired())
 772  
                     {
 773  171
                         fireEvent(EVENT_RELOAD, null, getURL(), true);
 774  171
                         setDetailEvents(false);
 775  
                         try
 776  
                         {
 777  171
                             clear();
 778  171
                             load();
 779  171
                         }
 780  
                         finally
 781  
                         {
 782  0
                             setDetailEvents(true);
 783  
                         }
 784  171
                         fireEvent(EVENT_RELOAD, null, getURL(), false);
 785  
 
 786  
                         // notify the strategy
 787  171
                         strategy.reloadingPerformed();
 788  
                     }
 789  14027
                 }
 790  
                 catch (Exception e)
 791  
                 {
 792  0
                     e.printStackTrace();
 793  
                     // todo rollback the changes if the file can't be reloaded
 794  0
                 }
 795  
                 finally
 796  
                 {
 797  0
                     exitNoReload();
 798  
                 }
 799  
             }
 800  23655
         }
 801  23655
     }
 802  
 
 803  
     /**
 804  
      * Enters the &quot;No reloading mode&quot;. As long as this mode is active
 805  
      * no reloading will be performed. This is necessary for some
 806  
      * implementations of <code>save()</code> in derived classes, which may
 807  
      * cause a reload while accessing the properties to save. This may cause the
 808  
      * whole configuration to be erased. To avoid this, this method can be
 809  
      * called first. After a call to this method there always must be a
 810  
      * corresponding call of <code>{@link #exitNoReload()}</code> later! (If
 811  
      * necessary, <code>finally</code> blocks must be used to ensure this.
 812  
      */
 813  
     protected void enterNoReload()
 814  
     {
 815  53764
         synchronized (reloadLock)
 816  
         {
 817  53764
             noReload++;
 818  53764
         }
 819  53764
     }
 820  
 
 821  
     /**
 822  
      * Leaves the &quot;No reloading mode&quot;.
 823  
      *
 824  
      * @see #enterNoReload()
 825  
      */
 826  
     protected void exitNoReload()
 827  
     {
 828  53764
         synchronized (reloadLock)
 829  
         {
 830  53764
             if (noReload > 0) // paranoia check
 831  
             {
 832  53764
                 noReload--;
 833  
             }
 834  53764
         }
 835  53764
     }
 836  
 
 837  
     /**
 838  
      * Sends an event to all registered listeners. This implementation ensures
 839  
      * that no reloads are performed while the listeners are invoked. So
 840  
      * infinite loops can be avoided that can be caused by event listeners
 841  
      * accessing the configuration's properties when they are invoked.
 842  
      *
 843  
      * @param type the event type
 844  
      * @param propName the name of the property
 845  
      * @param propValue the value of the property
 846  
      * @param before the before update flag
 847  
      */
 848  
     protected void fireEvent(int type, String propName, Object propValue,
 849  
             boolean before)
 850  
     {
 851  39712
         enterNoReload();
 852  
         try
 853  
         {
 854  39712
             super.fireEvent(type, propName, propValue, before);
 855  39712
         }
 856  
         finally
 857  
         {
 858  0
             exitNoReload();
 859  
         }
 860  39712
     }
 861  
 
 862  
     public Object getProperty(String key)
 863  
     {
 864  21707
         reload();
 865  21707
         return super.getProperty(key);
 866  
     }
 867  
 
 868  
     public boolean isEmpty()
 869  
     {
 870  9
         reload();
 871  9
         return super.isEmpty();
 872  
     }
 873  
 
 874  
     public boolean containsKey(String key)
 875  
     {
 876  599
         reload();
 877  599
         return super.containsKey(key);
 878  
     }
 879  
 
 880  
     public Iterator getKeys()
 881  
     {
 882  81
         reload();
 883  81
         return super.getKeys();
 884  
     }
 885  
 
 886  
     /**
 887  
      * Create the path to the specified file.
 888  
      *
 889  
      * @param file the target file
 890  
      */
 891  
     private void createPath(File file)
 892  
     {
 893  39
         if (file != null)
 894  
         {
 895  
             // create the path to the file if the file doesn't exist
 896  39
             if (!file.exists())
 897  
             {
 898  22
                 File parent = file.getParentFile();
 899  22
                 if (parent != null && !parent.exists())
 900  
                 {
 901  3
                     parent.mkdirs();
 902  
                 }
 903  
             }
 904  
         }
 905  39
     }
 906  
 
 907  
     public String getEncoding()
 908  
     {
 909  918
         return encoding;
 910  
     }
 911  
 
 912  
     public void setEncoding(String encoding)
 913  
     {
 914  392
         this.encoding = encoding;
 915  392
     }
 916  
 
 917  
     /**
 918  
      * Creates a copy of this configuration. The new configuration object will
 919  
      * contain the same properties as the original, but it will lose any
 920  
      * connection to a source file (if one exists); this includes setting the
 921  
      * source URL, base path, and file name to <b>null</b>. This is done to
 922  
      * avoid race conditions if both the original and the copy are modified and
 923  
      * then saved.
 924  
      *
 925  
      * @return the copy
 926  
      * @since 1.3
 927  
      */
 928  
     public Object clone()
 929  
     {
 930  4
         AbstractFileConfiguration copy = (AbstractFileConfiguration) super
 931  
                 .clone();
 932  4
         copy.setBasePath(null);
 933  4
         copy.setFileName(null);
 934  4
         copy.initReloadingStrategy();
 935  4
         return copy;
 936  
     }
 937  
 
 938  
     /**
 939  
      * Helper method for initializing the reloading strategy.
 940  
      */
 941  
     private void initReloadingStrategy()
 942  
     {
 943  798
         setReloadingStrategy(new InvariantReloadingStrategy());
 944  798
     }
 945  
 }