1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.lang.StringUtils;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 /***
38 * Miscellaneous utility methods for configurations.
39 *
40 * @see ConfigurationConverter Utility methods to convert configurations.
41 *
42 * @author <a href="mailto:herve.quiroz@esil.univ-mrs.fr">Herve Quiroz</a>
43 * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
44 * @author Emmanuel Bourg
45 * @version $Revision: 439648 $, $Date: 2006-09-02 22:42:10 +0200 (Sa, 02 Sep 2006) $
46 */
47 public final class ConfigurationUtils
48 {
49 /*** Constant for the file URL protocol.*/
50 static final String PROTOCOL_FILE = "file";
51
52 /*** Constant for the resource path separator.*/
53 static final String RESOURCE_PATH_SEPARATOR = "/";
54
55 /*** Constant for the name of the clone() method.*/
56 private static final String METHOD_CLONE = "clone";
57
58 /*** The logger.*/
59 private static Log log = LogFactory.getLog(ConfigurationUtils.class);
60
61 /***
62 * Private constructor. Prevents instances from being created.
63 */
64 private ConfigurationUtils()
65 {
66
67 }
68
69 /***
70 * Dump the configuration key/value mappings to some ouput stream.
71 *
72 * @param configuration the configuration
73 * @param out the output stream to dump the configuration to
74 */
75 public static void dump(Configuration configuration, PrintStream out)
76 {
77 dump(configuration, new PrintWriter(out));
78 }
79
80 /***
81 * Dump the configuration key/value mappings to some writer.
82 *
83 * @param configuration the configuration
84 * @param out the writer to dump the configuration to
85 */
86 public static void dump(Configuration configuration, PrintWriter out)
87 {
88 Iterator keys = configuration.getKeys();
89 while (keys.hasNext())
90 {
91 String key = (String) keys.next();
92 Object value = configuration.getProperty(key);
93 out.print(key);
94 out.print("=");
95 out.print(value);
96
97 if (keys.hasNext())
98 {
99 out.println();
100 }
101 }
102
103 out.flush();
104 }
105
106 /***
107 * Get a string representation of the key/value mappings of a
108 * configuration.
109 *
110 * @param configuration the configuration
111 * @return a string representation of the configuration
112 */
113 public static String toString(Configuration configuration)
114 {
115 StringWriter writer = new StringWriter();
116 dump(configuration, new PrintWriter(writer));
117 return writer.toString();
118 }
119
120 /***
121 * Copy all properties from the source configuration to the target
122 * configuration. Properties in the target configuration are replaced with
123 * the properties with the same key in the source configuration.
124 * <em>Note:</em> This method won't work well on hierarchical configurations
125 * because it is not able to copy information about the properties'
126 * structure. So when dealing with hierarchical configuration objects their
127 * <code>{@link HierarchicalConfiguration#clone() clone()}</code> methods
128 * should be used.
129 *
130 * @param source the source configuration
131 * @param target the target configuration
132 * @since 1.1
133 */
134 public static void copy(Configuration source, Configuration target)
135 {
136 Iterator keys = source.getKeys();
137 while (keys.hasNext())
138 {
139 String key = (String) keys.next();
140 target.setProperty(key, source.getProperty(key));
141 }
142 }
143
144 /***
145 * Append all properties from the source configuration to the target
146 * configuration. Properties in the source configuration are appended to
147 * the properties with the same key in the target configuration.
148 *
149 * @param source the source configuration
150 * @param target the target configuration
151 * @since 1.1
152 */
153 public static void append(Configuration source, Configuration target)
154 {
155 Iterator keys = source.getKeys();
156 while (keys.hasNext())
157 {
158 String key = (String) keys.next();
159 target.addProperty(key, source.getProperty(key));
160 }
161 }
162
163 /***
164 * Converts the passed in configuration to a hierarchical one. If the
165 * configuration is already hierarchical, it is directly returned. Otherwise
166 * all properties are copied into a new hierarchical configuration.
167 *
168 * @param conf the configuration to convert
169 * @return the new hierarchical configuration (the result is <b>null</b> if
170 * and only if the passed in configuration is <b>null</b>)
171 * @since 1.3
172 */
173 public static HierarchicalConfiguration convertToHierarchical(
174 Configuration conf)
175 {
176 if (conf == null)
177 {
178 return null;
179 }
180
181 if (conf instanceof HierarchicalConfiguration)
182 {
183 return (HierarchicalConfiguration) conf;
184 }
185 else
186 {
187 HierarchicalConfiguration hc = new HierarchicalConfiguration();
188 ConfigurationUtils.copy(conf, hc);
189 return hc;
190 }
191 }
192
193 /***
194 * Clones the given configuration object if this is possible. If the passed
195 * in configuration object implements the <code>Cloneable</code>
196 * interface, its <code>clone()</code> method will be invoked. Otherwise
197 * an exception will be thrown.
198 *
199 * @param config the configuration object to be cloned (can be <b>null</b>)
200 * @return the cloned configuration (<b>null</b> if the argument was
201 * <b>null</b>, too)
202 * @throws ConfigurationRuntimeException if cloning is not supported for
203 * this object
204 * @since 1.3
205 */
206 public static Configuration cloneConfiguration(Configuration config)
207 throws ConfigurationRuntimeException
208 {
209 if (config == null)
210 {
211 return null;
212 }
213 else
214 {
215 try
216 {
217 return (Configuration) clone(config);
218 }
219 catch (CloneNotSupportedException cnex)
220 {
221 throw new ConfigurationRuntimeException(cnex);
222 }
223 }
224 }
225
226 /***
227 * An internally used helper method for cloning objects. This implementation
228 * is not very sophisticated nor efficient. Maybe it can be replaced by an
229 * implementation from Commons Lang later. The method checks whether the
230 * passed in object implements the <code>Cloneable</code> interface. If
231 * this is the case, the <code>clone()</code> method is invoked by
232 * reflection. Errors that occur during the cloning process are re-thrown as
233 * runtime exceptions.
234 *
235 * @param obj the object to be cloned
236 * @return the cloned object
237 * @throws CloneNotSupportedException if the object cannot be cloned
238 */
239 static Object clone(Object obj) throws CloneNotSupportedException
240 {
241 if (obj instanceof Cloneable)
242 {
243 try
244 {
245 Method m = obj.getClass().getMethod(METHOD_CLONE, null);
246 return m.invoke(obj, null);
247 }
248 catch (NoSuchMethodException nmex)
249 {
250 throw new CloneNotSupportedException(
251 "No clone() method found for class"
252 + obj.getClass().getName());
253 }
254 catch (IllegalAccessException iaex)
255 {
256 throw new ConfigurationRuntimeException(iaex);
257 }
258 catch (InvocationTargetException itex)
259 {
260 throw new ConfigurationRuntimeException(itex);
261 }
262 }
263 else
264 {
265 throw new CloneNotSupportedException(obj.getClass().getName()
266 + " does not implement Cloneable");
267 }
268 }
269
270 /***
271 * Constructs a URL from a base path and a file name. The file name can
272 * be absolute, relative or a full URL. If necessary the base path URL is
273 * applied.
274 *
275 * @param basePath the base path URL (can be <b>null</b>)
276 * @param file the file name
277 * @return the resulting URL
278 * @throws MalformedURLException if URLs are invalid
279 */
280 public static URL getURL(String basePath, String file) throws MalformedURLException
281 {
282 File f = new File(file);
283 if (f.isAbsolute())
284 {
285 return f.toURL();
286 }
287
288 try
289 {
290 if (basePath == null)
291 {
292 return new URL(file);
293 }
294 else
295 {
296 URL base = new URL(basePath);
297 return new URL(base, file);
298 }
299 }
300 catch (MalformedURLException uex)
301 {
302 return constructFile(basePath, file).toURL();
303 }
304 }
305
306 /***
307 * Helper method for constructing a file object from a base path and a
308 * file name. This method is called if the base path passed to
309 * <code>getURL()</code> does not seem to be a valid URL.
310 *
311 * @param basePath the base path
312 * @param fileName the file name
313 * @return the resulting file
314 */
315 static File constructFile(String basePath, String fileName)
316 {
317 File file = null;
318
319 File absolute = null;
320 if (fileName != null)
321 {
322 absolute = new File(fileName);
323 }
324
325 if (StringUtils.isEmpty(basePath) || (absolute != null && absolute.isAbsolute()))
326 {
327 file = new File(fileName);
328 }
329 else
330 {
331 StringBuffer fName = new StringBuffer();
332 fName.append(basePath);
333
334
335 if (!basePath.endsWith(File.separator))
336 {
337 fName.append(File.separator);
338 }
339
340
341
342
343
344
345
346 if (fileName.startsWith("." + File.separator))
347 {
348 fName.append(fileName.substring(2));
349 }
350 else
351 {
352 fName.append(fileName);
353 }
354
355 file = new File(fName.toString());
356 }
357
358 return file;
359 }
360
361 /***
362 * Return the location of the specified resource by searching the user home
363 * directory, the current classpath and the system classpath.
364 *
365 * @param name the name of the resource
366 *
367 * @return the location of the resource
368 */
369 public static URL locate(String name)
370 {
371 return locate(null, name);
372 }
373
374 /***
375 * Return the location of the specified resource by searching the user home
376 * directory, the current classpath and the system classpath.
377 *
378 * @param base the base path of the resource
379 * @param name the name of the resource
380 *
381 * @return the location of the resource
382 */
383 public static URL locate(String base, String name)
384 {
385 if (name == null)
386 {
387
388 return null;
389 }
390
391 URL url = null;
392
393
394 try
395 {
396 if (base == null)
397 {
398 url = new URL(name);
399 }
400 else
401 {
402 URL baseURL = new URL(base);
403 url = new URL(baseURL, name);
404
405
406 InputStream in = null;
407 try
408 {
409 in = url.openStream();
410 }
411 finally
412 {
413 if (in != null)
414 {
415 in.close();
416 }
417 }
418 }
419
420 log.debug("Configuration loaded from the URL " + url);
421 }
422 catch (IOException e)
423 {
424 url = null;
425 }
426
427
428 if (url == null)
429 {
430 File file = new File(name);
431 if (file.isAbsolute() && file.exists())
432 {
433 try
434 {
435 url = file.toURL();
436 log.debug("Configuration loaded from the absolute path " + name);
437 }
438 catch (MalformedURLException e)
439 {
440 e.printStackTrace();
441 }
442 }
443 }
444
445
446 if (url == null)
447 {
448 try
449 {
450 File file = constructFile(base, name);
451 if (file != null && file.exists())
452 {
453 url = file.toURL();
454 }
455
456 if (url != null)
457 {
458 log.debug("Configuration loaded from the base path " + name);
459 }
460 }
461 catch (IOException e)
462 {
463 e.printStackTrace();
464 }
465 }
466
467
468 if (url == null)
469 {
470 try
471 {
472 File file = constructFile(System.getProperty("user.home"), name);
473 if (file != null && file.exists())
474 {
475 url = file.toURL();
476 }
477
478 if (url != null)
479 {
480 log.debug("Configuration loaded from the home path " + name);
481 }
482
483 }
484 catch (IOException e)
485 {
486 e.printStackTrace();
487 }
488 }
489
490
491 if (url == null)
492 {
493 url = locateFromClasspath(name);
494 }
495 return url;
496 }
497
498 /***
499 * Tries to find a resource with the given name in the classpath.
500 * @param resourceName the name of the resource
501 * @return the URL to the found resource or <b>null</b> if the resource
502 * cannot be found
503 */
504 static URL locateFromClasspath(String resourceName)
505 {
506 URL url = null;
507
508 ClassLoader loader = Thread.currentThread().getContextClassLoader();
509 if (loader != null)
510 {
511 url = loader.getResource(resourceName);
512
513 if (url != null)
514 {
515 log.debug("Configuration loaded from the context classpath (" + resourceName + ")");
516 }
517 }
518
519
520 if (url == null)
521 {
522 url = ClassLoader.getSystemResource(resourceName);
523
524 if (url != null)
525 {
526 log.debug("Configuration loaded from the system classpath (" + resourceName + ")");
527 }
528 }
529 return url;
530 }
531
532 /***
533 * Return the path without the file name, for example http://xyz.net/foo/bar.xml
534 * results in http://xyz.net/foo/
535 *
536 * @param url the URL from which to extract the path
537 * @return the path component of the passed in URL
538 */
539 static String getBasePath(URL url)
540 {
541 if (url == null)
542 {
543 return null;
544 }
545
546 String s = url.toString();
547
548 if (s.endsWith("/") || StringUtils.isEmpty(url.getPath()))
549 {
550 return s;
551 }
552 else
553 {
554 return s.substring(0, s.lastIndexOf("/") + 1);
555 }
556 }
557
558 /***
559 * Extract the file name from the specified URL.
560 *
561 * @param url the URL from which to extract the file name
562 * @return the extracted file name
563 */
564 static String getFileName(URL url)
565 {
566 if (url == null)
567 {
568 return null;
569 }
570
571 String path = url.getPath();
572
573 if (path.endsWith("/") || StringUtils.isEmpty(path))
574 {
575 return null;
576 }
577 else
578 {
579 return path.substring(path.lastIndexOf("/") + 1);
580 }
581 }
582
583 /***
584 * Tries to convert the specified base path and file name into a file object.
585 * This method is called e.g. by the save() methods of file based
586 * configurations. The parameter strings can be relative files, absolute
587 * files and URLs as well.
588 *
589 * @param basePath the base path
590 * @param fileName the file name
591 * @return the file object (<b>null</b> if no file can be obtained)
592 */
593 public static File getFile(String basePath, String fileName)
594 {
595
596 URL url;
597 try
598 {
599 url = new URL(new URL(basePath), fileName);
600 }
601 catch (MalformedURLException mex1)
602 {
603 try
604 {
605 url = new URL(fileName);
606 }
607 catch (MalformedURLException mex2)
608 {
609 url = null;
610 }
611 }
612
613 if (url != null)
614 {
615 return fileFromURL(url);
616 }
617
618 return constructFile(basePath, fileName);
619 }
620
621 /***
622 * Tries to convert the specified URL to a file object. If this fails,
623 * <b>null</b> is returned.
624 *
625 * @param url the URL
626 * @return the resulting file object
627 */
628 public static File fileFromURL(URL url)
629 {
630 if (PROTOCOL_FILE.equals(url.getProtocol()))
631 {
632 return new File(URLDecoder.decode(url.getPath()));
633 }
634 else
635 {
636 return null;
637 }
638 }
639 }