Coverage Report - org.apache.commons.configuration.ConfigurationUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
ConfigurationUtils
85%
148/174
86%
86/100
4,435
ConfigurationUtils$1
100%
2/2
N/A
4,435
 
 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.PrintStream;
 22  
 import java.io.PrintWriter;
 23  
 import java.io.StringWriter;
 24  
 import java.lang.reflect.InvocationTargetException;
 25  
 import java.lang.reflect.Method;
 26  
 import java.net.MalformedURLException;
 27  
 import java.net.URL;
 28  
 import java.util.Iterator;
 29  
 
 30  
 import org.apache.commons.configuration.event.ConfigurationErrorEvent;
 31  
 import org.apache.commons.configuration.event.ConfigurationErrorListener;
 32  
 import org.apache.commons.configuration.event.EventSource;
 33  
 import org.apache.commons.configuration.reloading.Reloadable;
 34  
 import org.apache.commons.configuration.tree.ExpressionEngine;
 35  
 import org.apache.commons.lang.StringUtils;
 36  
 import org.apache.commons.logging.Log;
 37  
 import org.apache.commons.logging.LogFactory;
 38  
 
 39  
 /**
 40  
  * Miscellaneous utility methods for configurations.
 41  
  *
 42  
  * @see ConfigurationConverter Utility methods to convert configurations.
 43  
  *
 44  
  * @author <a href="mailto:herve.quiroz@esil.univ-mrs.fr">Herve Quiroz</a>
 45  
  * @author Emmanuel Bourg
 46  
  * @version $Id: ConfigurationUtils.java 1208795 2011-11-30 21:18:17Z oheger $
 47  
  */
 48  
 public final class ConfigurationUtils
 49  
 {
 50  
     /** Constant for the file URL protocol.*/
 51  
     static final String PROTOCOL_FILE = "file";
 52  
 
 53  
     /** Constant for the resource path separator.*/
 54  
     static final String RESOURCE_PATH_SEPARATOR = "/";
 55  
 
 56  
     /** Constant for the file URL protocol */
 57  
     private static final String FILE_SCHEME = "file:";
 58  
 
 59  
     /** Constant for the name of the clone() method.*/
 60  
     private static final String METHOD_CLONE = "clone";
 61  
 
 62  
     /** Constant for parsing numbers in hex format. */
 63  
     private static final int HEX = 16;
 64  
 
 65  
     /** The logger.*/
 66  1
     private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class);
 67  
 
 68  
     /**
 69  
      * Private constructor. Prevents instances from being created.
 70  
      */
 71  
     private ConfigurationUtils()
 72  0
     {
 73  
         // to prevent instantiation...
 74  0
     }
 75  
 
 76  
     /**
 77  
      * Dump the configuration key/value mappings to some ouput stream.
 78  
      *
 79  
      * @param configuration the configuration
 80  
      * @param out the output stream to dump the configuration to
 81  
      */
 82  
     public static void dump(Configuration configuration, PrintStream out)
 83  
     {
 84  0
         dump(configuration, new PrintWriter(out));
 85  0
     }
 86  
 
 87  
     /**
 88  
      * Dump the configuration key/value mappings to some writer.
 89  
      *
 90  
      * @param configuration the configuration
 91  
      * @param out the writer to dump the configuration to
 92  
      */
 93  
     public static void dump(Configuration configuration, PrintWriter out)
 94  
     {
 95  5
         for (Iterator<String> keys = configuration.getKeys(); keys.hasNext();)
 96  
         {
 97  6
             String key = keys.next();
 98  6
             Object value = configuration.getProperty(key);
 99  6
             out.print(key);
 100  6
             out.print("=");
 101  6
             out.print(value);
 102  
 
 103  6
             if (keys.hasNext())
 104  
             {
 105  2
                 out.println();
 106  
             }
 107  6
         }
 108  
 
 109  5
         out.flush();
 110  5
     }
 111  
 
 112  
     /**
 113  
      * Get a string representation of the key/value mappings of a
 114  
      * configuration.
 115  
      *
 116  
      * @param configuration the configuration
 117  
      * @return a string representation of the configuration
 118  
      */
 119  
     public static String toString(Configuration configuration)
 120  
     {
 121  5
         StringWriter writer = new StringWriter();
 122  5
         dump(configuration, new PrintWriter(writer));
 123  5
         return writer.toString();
 124  
     }
 125  
 
 126  
     /**
 127  
      * <p>Copy all properties from the source configuration to the target
 128  
      * configuration. Properties in the target configuration are replaced with
 129  
      * the properties with the same key in the source configuration.</p>
 130  
      * <p><em>Note:</em> This method is not able to handle some specifics of
 131  
      * configurations derived from {@code AbstractConfiguration} (e.g.
 132  
      * list delimiters). For a full support of all of these features the
 133  
      * {@code copy()} method of {@code AbstractConfiguration} should
 134  
      * be used. In a future release this method might become deprecated.</p>
 135  
      *
 136  
      * @param source the source configuration
 137  
      * @param target the target configuration
 138  
      * @since 1.1
 139  
      */
 140  
     public static void copy(Configuration source, Configuration target)
 141  
     {
 142  9
         for (Iterator<String> keys = source.getKeys(); keys.hasNext();)
 143  
         {
 144  99
             String key = keys.next();
 145  99
             target.setProperty(key, source.getProperty(key));
 146  99
         }
 147  9
     }
 148  
 
 149  
     /**
 150  
      * <p>Append all properties from the source configuration to the target
 151  
      * configuration. Properties in the source configuration are appended to
 152  
      * the properties with the same key in the target configuration.</p>
 153  
      * <p><em>Note:</em> This method is not able to handle some specifics of
 154  
      * configurations derived from {@code AbstractConfiguration} (e.g.
 155  
      * list delimiters). For a full support of all of these features the
 156  
      * {@code copy()} method of {@code AbstractConfiguration} should
 157  
      * be used. In a future release this method might become deprecated.</p>
 158  
      *
 159  
      * @param source the source configuration
 160  
      * @param target the target configuration
 161  
      * @since 1.1
 162  
      */
 163  
     public static void append(Configuration source, Configuration target)
 164  
     {
 165  1
         for (Iterator<String> keys = source.getKeys(); keys.hasNext();)
 166  
         {
 167  2
             String key = keys.next();
 168  2
             target.addProperty(key, source.getProperty(key));
 169  2
         }
 170  1
     }
 171  
 
 172  
     /**
 173  
      * Converts the passed in configuration to a hierarchical one. If the
 174  
      * configuration is already hierarchical, it is directly returned. Otherwise
 175  
      * all properties are copied into a new hierarchical configuration.
 176  
      *
 177  
      * @param conf the configuration to convert
 178  
      * @return the new hierarchical configuration (the result is <b>null</b> if
 179  
      * and only if the passed in configuration is <b>null</b>)
 180  
      * @since 1.3
 181  
      */
 182  
     public static HierarchicalConfiguration convertToHierarchical(
 183  
             Configuration conf)
 184  
     {
 185  5
         return convertToHierarchical(conf, null);
 186  
     }
 187  
 
 188  
     /**
 189  
      * Converts the passed in {@code Configuration} object to a
 190  
      * hierarchical one using the specified {@code ExpressionEngine}. This
 191  
      * conversion works by adding the keys found in the configuration to a newly
 192  
      * created hierarchical configuration. When adding new keys to a
 193  
      * hierarchical configuration the keys are interpreted by its
 194  
      * {@code ExpressionEngine}. If they contain special characters (e.g.
 195  
      * brackets) that are treated in a special way by the default expression
 196  
      * engine, it may be necessary using a specific engine that can deal with
 197  
      * such characters. Otherwise <b>null</b> can be passed in for the
 198  
      * {@code ExpressionEngine}; then the default expression engine is
 199  
      * used. If the passed in configuration is already hierarchical, it is
 200  
      * directly returned. (However, the {@code ExpressionEngine} is set if
 201  
      * it is not <b>null</b>.) Otherwise all properties are copied into a new
 202  
      * hierarchical configuration.
 203  
      *
 204  
      * @param conf the configuration to convert
 205  
      * @param engine the {@code ExpressionEngine} for the hierarchical
 206  
      *        configuration or <b>null</b> for the default
 207  
      * @return the new hierarchical configuration (the result is <b>null</b> if
 208  
      *         and only if the passed in configuration is <b>null</b>)
 209  
      * @since 1.6
 210  
      */
 211  
     public static HierarchicalConfiguration convertToHierarchical(
 212  
             Configuration conf, ExpressionEngine engine)
 213  
     {
 214  7193
         if (conf == null)
 215  
         {
 216  1
             return null;
 217  
         }
 218  
 
 219  7192
         if (conf instanceof HierarchicalConfiguration)
 220  
         {
 221  
             HierarchicalConfiguration hc;
 222  7060
             if (conf instanceof Reloadable)
 223  
             {
 224  7038
                 Object lock = ((Reloadable) conf).getReloadLock();
 225  7038
                 synchronized (lock)
 226  
                 {
 227  7038
                     hc = new HierarchicalConfiguration((HierarchicalConfiguration) conf);
 228  7038
                 }
 229  7038
             }
 230  
             else
 231  
             {
 232  22
                 hc = (HierarchicalConfiguration) conf;
 233  
             }
 234  7060
             if (engine != null)
 235  
             {
 236  1
                 hc.setExpressionEngine(engine);
 237  
             }
 238  
 
 239  7060
             return hc;
 240  
         }
 241  
         else
 242  
         {
 243  132
             HierarchicalConfiguration hc = new HierarchicalConfiguration();
 244  132
             if (engine != null)
 245  
             {
 246  2
                 hc.setExpressionEngine(engine);
 247  
             }
 248  
 
 249  
             // Workaround for problem with copy()
 250  132
             boolean delimiterParsingStatus = hc.isDelimiterParsingDisabled();
 251  132
             hc.setDelimiterParsingDisabled(true);
 252  132
             hc.append(conf);
 253  132
             hc.setDelimiterParsingDisabled(delimiterParsingStatus);
 254  132
             return hc;
 255  
         }
 256  
     }
 257  
 
 258  
     /**
 259  
      * Clones the given configuration object if this is possible. If the passed
 260  
      * in configuration object implements the {@code Cloneable}
 261  
      * interface, its {@code clone()} method will be invoked. Otherwise
 262  
      * an exception will be thrown.
 263  
      *
 264  
      * @param config the configuration object to be cloned (can be <b>null</b>)
 265  
      * @return the cloned configuration (<b>null</b> if the argument was
 266  
      * <b>null</b>, too)
 267  
      * @throws ConfigurationRuntimeException if cloning is not supported for
 268  
      * this object
 269  
      * @since 1.3
 270  
      */
 271  
     public static Configuration cloneConfiguration(Configuration config)
 272  
             throws ConfigurationRuntimeException
 273  
     {
 274  12
         if (config == null)
 275  
         {
 276  1
             return null;
 277  
         }
 278  
         else
 279  
         {
 280  
             try
 281  
             {
 282  11
                 return (Configuration) clone(config);
 283  
             }
 284  2
             catch (CloneNotSupportedException cnex)
 285  
             {
 286  2
                 throw new ConfigurationRuntimeException(cnex);
 287  
             }
 288  
         }
 289  
     }
 290  
 
 291  
     /**
 292  
      * An internally used helper method for cloning objects. This implementation
 293  
      * is not very sophisticated nor efficient. Maybe it can be replaced by an
 294  
      * implementation from Commons Lang later. The method checks whether the
 295  
      * passed in object implements the {@code Cloneable} interface. If
 296  
      * this is the case, the {@code clone()} method is invoked by
 297  
      * reflection. Errors that occur during the cloning process are re-thrown as
 298  
      * runtime exceptions.
 299  
      *
 300  
      * @param obj the object to be cloned
 301  
      * @return the cloned object
 302  
      * @throws CloneNotSupportedException if the object cannot be cloned
 303  
      */
 304  
     static Object clone(Object obj) throws CloneNotSupportedException
 305  
     {
 306  24
         if (obj instanceof Cloneable)
 307  
         {
 308  
             try
 309  
             {
 310  22
                 Method m = obj.getClass().getMethod(METHOD_CLONE);
 311  22
                 return m.invoke(obj);
 312  
             }
 313  0
             catch (NoSuchMethodException nmex)
 314  
             {
 315  0
                 throw new CloneNotSupportedException(
 316  
                         "No clone() method found for class"
 317  
                                 + obj.getClass().getName());
 318  
             }
 319  0
             catch (IllegalAccessException iaex)
 320  
             {
 321  0
                 throw new ConfigurationRuntimeException(iaex);
 322  
             }
 323  0
             catch (InvocationTargetException itex)
 324  
             {
 325  0
                 throw new ConfigurationRuntimeException(itex);
 326  
             }
 327  
         }
 328  
         else
 329  
         {
 330  2
             throw new CloneNotSupportedException(obj.getClass().getName()
 331  
                     + " does not implement Cloneable");
 332  
         }
 333  
     }
 334  
 
 335  
     /**
 336  
      * Constructs a URL from a base path and a file name. The file name can
 337  
      * be absolute, relative or a full URL. If necessary the base path URL is
 338  
      * applied.
 339  
      *
 340  
      * @param basePath the base path URL (can be <b>null</b>)
 341  
      * @param file the file name
 342  
      * @return the resulting URL
 343  
      * @throws MalformedURLException if URLs are invalid
 344  
      */
 345  
     public static URL getURL(String basePath, String file) throws MalformedURLException
 346  
     {
 347  8
         return FileSystem.getDefaultFileSystem().getURL(basePath, file);
 348  
     }
 349  
 
 350  
     /**
 351  
      * Helper method for constructing a file object from a base path and a
 352  
      * file name. This method is called if the base path passed to
 353  
      * {@code getURL()} does not seem to be a valid URL.
 354  
      *
 355  
      * @param basePath the base path
 356  
      * @param fileName the file name
 357  
      * @return the resulting file
 358  
      */
 359  
     static File constructFile(String basePath, String fileName)
 360  
     {
 361  
         File file;
 362  
 
 363  1399
         File absolute = null;
 364  1399
         if (fileName != null)
 365  
         {
 366  1399
             absolute = new File(fileName);
 367  
         }
 368  
 
 369  1399
         if (StringUtils.isEmpty(basePath) || (absolute != null && absolute.isAbsolute()))
 370  
         {
 371  74
             file = new File(fileName);
 372  
         }
 373  
         else
 374  
         {
 375  1325
             StringBuilder fName = new StringBuilder();
 376  1325
             fName.append(basePath);
 377  
 
 378  
             // My best friend. Paranoia.
 379  1325
             if (!basePath.endsWith(File.separator))
 380  
             {
 381  1325
                 fName.append(File.separator);
 382  
             }
 383  
 
 384  
             //
 385  
             // We have a relative path, and we have
 386  
             // two possible forms here. If we have the
 387  
             // "./" form then just strip that off first
 388  
             // before continuing.
 389  
             //
 390  1325
             if (fileName.startsWith("." + File.separator))
 391  
             {
 392  0
                 fName.append(fileName.substring(2));
 393  
             }
 394  
             else
 395  
             {
 396  1325
                 fName.append(fileName);
 397  
             }
 398  
 
 399  1325
             file = new File(fName.toString());
 400  
         }
 401  
 
 402  1399
         return file;
 403  
     }
 404  
 
 405  
     /**
 406  
      * Return the location of the specified resource by searching the user home
 407  
      * directory, the current classpath and the system classpath.
 408  
      *
 409  
      * @param name the name of the resource
 410  
      *
 411  
      * @return the location of the resource
 412  
      */
 413  
     public static URL locate(String name)
 414  
     {
 415  0
         return locate(null, name);
 416  
     }
 417  
 
 418  
     /**
 419  
      * Return the location of the specified resource by searching the user home
 420  
      * directory, the current classpath and the system classpath.
 421  
      *
 422  
      * @param base the base path of the resource
 423  
      * @param name the name of the resource
 424  
      *
 425  
      * @return the location of the resource
 426  
      */
 427  
     public static URL locate(String base, String name)
 428  
     {
 429  23
         return locate(FileSystem.getDefaultFileSystem(), base, name);
 430  
     }
 431  
 
 432  
     /**
 433  
      * Return the location of the specified resource by searching the user home
 434  
      * directory, the current classpath and the system classpath.
 435  
      *
 436  
      * @param fileSystem the FileSystem to use.
 437  
      * @param base the base path of the resource
 438  
      * @param name the name of the resource
 439  
      *
 440  
      * @return the location of the resource
 441  
      */
 442  
     public static URL locate(FileSystem fileSystem, String base, String name)
 443  
     {
 444  6033
         if (LOG.isDebugEnabled())
 445  
         {
 446  0
             StringBuilder buf = new StringBuilder();
 447  0
             buf.append("ConfigurationUtils.locate(): base is ").append(base);
 448  0
             buf.append(", name is ").append(name);
 449  0
             LOG.debug(buf.toString());
 450  
         }
 451  
 
 452  6009
         if (name == null)
 453  
         {
 454  
             // undefined, always return null
 455  10
             return null;
 456  
         }
 457  
 
 458  
         // attempt to create an URL directly
 459  
 
 460  6024
         URL url = fileSystem.locateFromURL(base, name);
 461  
 
 462  
         // attempt to load from an absolute path
 463  6008
         if (url == null)
 464  
         {
 465  1458
             File file = new File(name);
 466  1458
             if (file.isAbsolute() && file.exists()) // already absolute?
 467  
             {
 468  
                 try
 469  
                 {
 470  332
                     url = toURL(file);
 471  332
                     LOG.debug("Loading configuration from the absolute path " + name);
 472  
                 }
 473  0
                 catch (MalformedURLException e)
 474  
                 {
 475  0
                     LOG.warn("Could not obtain URL from file", e);
 476  332
                 }
 477  
             }
 478  
         }
 479  
 
 480  
         // attempt to load from the base directory
 481  5979
         if (url == null)
 482  
         {
 483  
             try
 484  
             {
 485  1126
                 File file = constructFile(base, name);
 486  1126
                 if (file != null && file.exists())
 487  
                 {
 488  902
                     url = toURL(file);
 489  
                 }
 490  
 
 491  1126
                 if (url != null)
 492  
                 {
 493  902
                     LOG.debug("Loading configuration from the path " + file);
 494  
                 }
 495  
             }
 496  0
             catch (MalformedURLException e)
 497  
             {
 498  0
                 LOG.warn("Could not obtain URL from file", e);
 499  1126
             }
 500  
         }
 501  
 
 502  
         // attempt to load from the user home directory
 503  5985
         if (url == null)
 504  
         {
 505  
             try
 506  
             {
 507  224
                 File file = constructFile(System.getProperty("user.home"), name);
 508  224
                 if (file != null && file.exists())
 509  
                 {
 510  0
                     url = toURL(file);
 511  
                 }
 512  
 
 513  224
                 if (url != null)
 514  
                 {
 515  0
                     LOG.debug("Loading configuration from the home path " + file);
 516  
                 }
 517  
 
 518  
             }
 519  0
             catch (MalformedURLException e)
 520  
             {
 521  0
                 LOG.warn("Could not obtain URL from file", e);
 522  224
             }
 523  
         }
 524  
 
 525  
         // attempt to load from classpath
 526  5956
         if (url == null)
 527  
         {
 528  224
             url = locateFromClasspath(name);
 529  
         }
 530  6023
         return url;
 531  
     }
 532  
 
 533  
     /**
 534  
      * Tries to find a resource with the given name in the classpath.
 535  
      * @param resourceName the name of the resource
 536  
      * @return the URL to the found resource or <b>null</b> if the resource
 537  
      * cannot be found
 538  
      */
 539  
     static URL locateFromClasspath(String resourceName)
 540  
     {
 541  224
         URL url = null;
 542  
         // attempt to load from the context classpath
 543  224
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 544  224
         if (loader != null)
 545  
         {
 546  223
             url = loader.getResource(resourceName);
 547  
 
 548  223
             if (url != null)
 549  
             {
 550  147
                 LOG.debug("Loading configuration from the context classpath (" + resourceName + ")");
 551  
             }
 552  
         }
 553  
 
 554  
         // attempt to load from the system classpath
 555  224
         if (url == null)
 556  
         {
 557  77
             url = ClassLoader.getSystemResource(resourceName);
 558  
 
 559  77
             if (url != null)
 560  
             {
 561  0
                 LOG.debug("Loading configuration from the system classpath (" + resourceName + ")");
 562  
             }
 563  
         }
 564  224
         return url;
 565  
     }
 566  
 
 567  
     /**
 568  
      * Return the path without the file name, for example http://xyz.net/foo/bar.xml
 569  
      * results in http://xyz.net/foo/
 570  
      *
 571  
      * @param url the URL from which to extract the path
 572  
      * @return the path component of the passed in URL
 573  
      */
 574  
     static String getBasePath(URL url)
 575  
     {
 576  23
         if (url == null)
 577  
         {
 578  0
             return null;
 579  
         }
 580  
 
 581  23
         String s = url.toString();
 582  23
         if (s.startsWith(FILE_SCHEME) && !s.startsWith("file://"))
 583  
         {
 584  14
             s = "file://" + s.substring(FILE_SCHEME.length());
 585  
         }
 586  
 
 587  23
         if (s.endsWith("/") || StringUtils.isEmpty(url.getPath()))
 588  
         {
 589  3
             return s;
 590  
         }
 591  
         else
 592  
         {
 593  20
             return s.substring(0, s.lastIndexOf("/") + 1);
 594  
         }
 595  
     }
 596  
 
 597  
     /**
 598  
      * Extract the file name from the specified URL.
 599  
      *
 600  
      * @param url the URL from which to extract the file name
 601  
      * @return the extracted file name
 602  
      */
 603  
     static String getFileName(URL url)
 604  
     {
 605  21
         if (url == null)
 606  
         {
 607  1
             return null;
 608  
         }
 609  
 
 610  20
         String path = url.getPath();
 611  
 
 612  20
         if (path.endsWith("/") || StringUtils.isEmpty(path))
 613  
         {
 614  1
             return null;
 615  
         }
 616  
         else
 617  
         {
 618  19
             return path.substring(path.lastIndexOf("/") + 1);
 619  
         }
 620  
     }
 621  
 
 622  
     /**
 623  
      * Tries to convert the specified base path and file name into a file object.
 624  
      * This method is called e.g. by the save() methods of file based
 625  
      * configurations. The parameter strings can be relative files, absolute
 626  
      * files and URLs as well. This implementation checks first whether the passed in
 627  
      * file name is absolute. If this is the case, it is returned. Otherwise
 628  
      * further checks are performed whether the base path and file name can be
 629  
      * combined to a valid URL or a valid file name. <em>Note:</em> The test
 630  
      * if the passed in file name is absolute is performed using
 631  
      * {@code java.io.File.isAbsolute()}. If the file name starts with a
 632  
      * slash, this method will return <b>true</b> on Unix, but <b>false</b> on
 633  
      * Windows. So to ensure correct behavior for relative file names on all
 634  
      * platforms you should never let relative paths start with a slash. E.g.
 635  
      * in a configuration definition file do not use something like that:
 636  
      * <pre>
 637  
      * &lt;properties fileName="/subdir/my.properties"/&gt;
 638  
      * </pre>
 639  
      * Under Windows this path would be resolved relative to the configuration
 640  
      * definition file. Under Unix this would be treated as an absolute path
 641  
      * name.
 642  
      *
 643  
      * @param basePath the base path
 644  
      * @param fileName the file name
 645  
      * @return the file object (<b>null</b> if no file can be obtained)
 646  
      */
 647  
     public static File getFile(String basePath, String fileName)
 648  
     {
 649  
         // Check if the file name is absolute
 650  21
         File f = new File(fileName);
 651  21
         if (f.isAbsolute())
 652  
         {
 653  3
             return f;
 654  
         }
 655  
 
 656  
         // Check if URLs are involved
 657  
         URL url;
 658  
         try
 659  
         {
 660  18
             url = new URL(new URL(basePath), fileName);
 661  
         }
 662  17
         catch (MalformedURLException mex1)
 663  
         {
 664  
             try
 665  
             {
 666  17
                 url = new URL(fileName);
 667  
             }
 668  16
             catch (MalformedURLException mex2)
 669  
             {
 670  16
                 url = null;
 671  1
             }
 672  1
         }
 673  
 
 674  18
         if (url != null)
 675  
         {
 676  2
             return fileFromURL(url);
 677  
         }
 678  
 
 679  16
         return constructFile(basePath, fileName);
 680  
     }
 681  
 
 682  
     /**
 683  
      * Tries to convert the specified URL to a file object. If this fails,
 684  
      * <b>null</b> is returned. Note: This code has been copied from the
 685  
      * {@code FileUtils} class from <em>Commons IO</em>.
 686  
      *
 687  
      * @param url the URL
 688  
      * @return the resulting file object
 689  
      */
 690  
     public static File fileFromURL(URL url)
 691  
     {
 692  33101
         if (url == null || !url.getProtocol().equals(PROTOCOL_FILE))
 693  
         {
 694  7
             return null;
 695  
         }
 696  
         else
 697  
         {
 698  33423
             String filename = url.getFile().replace('/', File.separatorChar);
 699  33236
             int pos = 0;
 700  33420
             while ((pos = filename.indexOf('%', pos)) >= 0)
 701  
             {
 702  10
                 if (pos + 2 < filename.length())
 703  
                 {
 704  10
                     String hexStr = filename.substring(pos + 1, pos + 3);
 705  10
                     char ch = (char) Integer.parseInt(hexStr, HEX);
 706  10
                     filename = filename.substring(0, pos) + ch
 707  
                             + filename.substring(pos + 3);
 708  10
                 }
 709  
             }
 710  33101
             return new File(filename);
 711  
         }
 712  
     }
 713  
 
 714  
     /**
 715  
      * Convert the specified file into an URL. This method is equivalent
 716  
      * to file.toURI().toURL(). It was used to work around a bug in the JDK
 717  
      * preventing the transformation of a file into an URL if the file name
 718  
      * contains a '#' character. See the issue CONFIGURATION-300 for
 719  
      * more details. Now that we switched to JDK 1.4 we can directly use
 720  
      * file.toURI().toURL().
 721  
      *
 722  
      * @param file the file to be converted into an URL
 723  
      */
 724  
     static URL toURL(File file) throws MalformedURLException
 725  
     {
 726  1295
         return file.toURI().toURL();
 727  
     }
 728  
 
 729  
     /**
 730  
      * Enables runtime exceptions for the specified configuration object. This
 731  
      * method can be used for configuration implementations that may face errors
 732  
      * on normal property access, e.g. {@code DatabaseConfiguration} or
 733  
      * {@code JNDIConfiguration}. Per default such errors are simply
 734  
      * logged and then ignored. This implementation will register a special
 735  
      * {@link ConfigurationErrorListener} that throws a runtime
 736  
      * exception (namely a {@code ConfigurationRuntimeException}) on
 737  
      * each received error event.
 738  
      *
 739  
      * @param src the configuration, for which runtime exceptions are to be
 740  
      * enabled; this configuration must be derived from
 741  
      * {@link EventSource}
 742  
      */
 743  
     public static void enableRuntimeExceptions(Configuration src)
 744  
     {
 745  3
         if (!(src instanceof EventSource))
 746  
         {
 747  2
             throw new IllegalArgumentException(
 748  
                     "Configuration must be derived from EventSource!");
 749  
         }
 750  1
         ((EventSource) src).addErrorListener(new ConfigurationErrorListener()
 751  1
         {
 752  
             public void configurationError(ConfigurationErrorEvent event)
 753  
             {
 754  
                 // Throw a runtime exception
 755  1
                 throw new ConfigurationRuntimeException(event.getCause());
 756  
             }
 757  
         });
 758  1
     }
 759  
 }