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