Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ConfigurationUtils |
|
| 4.722222222222222;4,722 |
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.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 | 28 | 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 | 28 | private static Log log = LogFactory.getLog(ConfigurationUtils.class); |
60 | ||
61 | /** | |
62 | * Private constructor. Prevents instances from being created. | |
63 | */ | |
64 | private ConfigurationUtils() | |
65 | 0 | { |
66 | // to prevent instanciation... | |
67 | 0 | } |
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 | 0 | dump(configuration, new PrintWriter(out)); |
78 | 0 | } |
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 | 5 | Iterator keys = configuration.getKeys(); |
89 | 16 | while (keys.hasNext()) |
90 | { | |
91 | 6 | String key = (String) keys.next(); |
92 | 6 | Object value = configuration.getProperty(key); |
93 | 6 | out.print(key); |
94 | 6 | out.print("="); |
95 | 6 | out.print(value); |
96 | ||
97 | 6 | if (keys.hasNext()) |
98 | { | |
99 | 2 | out.println(); |
100 | } | |
101 | } | |
102 | ||
103 | 5 | out.flush(); |
104 | 5 | } |
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 | 5 | StringWriter writer = new StringWriter(); |
116 | 5 | dump(configuration, new PrintWriter(writer)); |
117 | 5 | 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 | 43 | Iterator keys = source.getKeys(); |
137 | 1182 | while (keys.hasNext()) |
138 | { | |
139 | 1096 | String key = (String) keys.next(); |
140 | 1096 | target.setProperty(key, source.getProperty(key)); |
141 | } | |
142 | 43 | } |
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 | 1 | Iterator keys = source.getKeys(); |
156 | 4 | while (keys.hasNext()) |
157 | { | |
158 | 2 | String key = (String) keys.next(); |
159 | 2 | target.addProperty(key, source.getProperty(key)); |
160 | } | |
161 | 1 | } |
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 | 73 | if (conf == null) |
177 | { | |
178 | 1 | return null; |
179 | } | |
180 | ||
181 | 72 | if (conf instanceof HierarchicalConfiguration) |
182 | { | |
183 | 38 | return (HierarchicalConfiguration) conf; |
184 | } | |
185 | else | |
186 | { | |
187 | 34 | HierarchicalConfiguration hc = new HierarchicalConfiguration(); |
188 | 34 | ConfigurationUtils.copy(conf, hc); |
189 | 34 | 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 | 11 | if (config == null) |
210 | { | |
211 | 1 | return null; |
212 | } | |
213 | else | |
214 | { | |
215 | try | |
216 | { | |
217 | 10 | return (Configuration) clone(config); |
218 | } | |
219 | catch (CloneNotSupportedException cnex) | |
220 | { | |
221 | 2 | 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 | 21 | if (obj instanceof Cloneable) |
242 | { | |
243 | try | |
244 | { | |
245 | 19 | Method m = obj.getClass().getMethod(METHOD_CLONE, null); |
246 | 19 | return m.invoke(obj, null); |
247 | } | |
248 | catch (NoSuchMethodException nmex) | |
249 | { | |
250 | 0 | throw new CloneNotSupportedException( |
251 | "No clone() method found for class" | |
252 | + obj.getClass().getName()); | |
253 | } | |
254 | catch (IllegalAccessException iaex) | |
255 | { | |
256 | 0 | throw new ConfigurationRuntimeException(iaex); |
257 | } | |
258 | catch (InvocationTargetException itex) | |
259 | { | |
260 | 0 | throw new ConfigurationRuntimeException(itex); |
261 | } | |
262 | } | |
263 | else | |
264 | { | |
265 | 2 | 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 | 8 | File f = new File(file); |
283 | 8 | if (f.isAbsolute()) // already absolute? |
284 | { | |
285 | 2 | return f.toURL(); |
286 | } | |
287 | ||
288 | try | |
289 | { | |
290 | 6 | if (basePath == null) |
291 | { | |
292 | 2 | return new URL(file); |
293 | } | |
294 | else | |
295 | { | |
296 | 4 | URL base = new URL(basePath); |
297 | 3 | return new URL(base, file); |
298 | } | |
299 | } | |
300 | catch (MalformedURLException uex) | |
301 | { | |
302 | 2 | 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 | 404 | File file = null; |
318 | ||
319 | 404 | File absolute = null; |
320 | 404 | if (fileName != null) |
321 | { | |
322 | 404 | absolute = new File(fileName); |
323 | } | |
324 | ||
325 | 404 | if (StringUtils.isEmpty(basePath) || (absolute != null && absolute.isAbsolute())) |
326 | { | |
327 | 49 | file = new File(fileName); |
328 | } | |
329 | else | |
330 | { | |
331 | 355 | StringBuffer fName = new StringBuffer(); |
332 | 355 | fName.append(basePath); |
333 | ||
334 | // My best friend. Paranoia. | |
335 | 355 | if (!basePath.endsWith(File.separator)) |
336 | { | |
337 | 355 | fName.append(File.separator); |
338 | } | |
339 | ||
340 | // | |
341 | // We have a relative path, and we have | |
342 | // two possible forms here. If we have the | |
343 | // "./" form then just strip that off first | |
344 | // before continuing. | |
345 | // | |
346 | 355 | if (fileName.startsWith("." + File.separator)) |
347 | { | |
348 | 0 | fName.append(fileName.substring(2)); |
349 | } | |
350 | else | |
351 | { | |
352 | 355 | fName.append(fileName); |
353 | } | |
354 | ||
355 | 355 | file = new File(fName.toString()); |
356 | } | |
357 | ||
358 | 404 | 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 | 1 | 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 | 933 | if (name == null) |
386 | { | |
387 | // undefined, always return null | |
388 | 4 | return null; |
389 | } | |
390 | ||
391 | 929 | URL url = null; |
392 | ||
393 | // attempt to create an URL directly | |
394 | try | |
395 | { | |
396 | 929 | if (base == null) |
397 | { | |
398 | 270 | url = new URL(name); |
399 | } | |
400 | else | |
401 | { | |
402 | 659 | URL baseURL = new URL(base); |
403 | 381 | url = new URL(baseURL, name); |
404 | ||
405 | // check if the file exists | |
406 | 380 | InputStream in = null; |
407 | try | |
408 | { | |
409 | 380 | in = url.openStream(); |
410 | 364 | } |
411 | finally | |
412 | { | |
413 | 16 | if (in != null) |
414 | { | |
415 | 364 | in.close(); |
416 | } | |
417 | } | |
418 | } | |
419 | ||
420 | 364 | log.debug("Configuration loaded from the URL " + url); |
421 | 364 | } |
422 | catch (IOException e) | |
423 | { | |
424 | 565 | url = null; |
425 | } | |
426 | ||
427 | // attempt to load from an absolute path | |
428 | 929 | if (url == null) |
429 | { | |
430 | 565 | File file = new File(name); |
431 | 565 | if (file.isAbsolute() && file.exists()) // already absolute? |
432 | { | |
433 | try | |
434 | { | |
435 | 231 | url = file.toURL(); |
436 | 231 | log.debug("Configuration loaded from the absolute path " + name); |
437 | 231 | } |
438 | catch (MalformedURLException e) | |
439 | { | |
440 | 0 | e.printStackTrace(); |
441 | } | |
442 | } | |
443 | } | |
444 | ||
445 | // attempt to load from the base directory | |
446 | 929 | if (url == null) |
447 | { | |
448 | try | |
449 | { | |
450 | 334 | File file = constructFile(base, name); |
451 | 334 | if (file != null && file.exists()) |
452 | { | |
453 | 290 | url = file.toURL(); |
454 | } | |
455 | ||
456 | 334 | if (url != null) |
457 | { | |
458 | 290 | log.debug("Configuration loaded from the base path " + name); |
459 | } | |
460 | 334 | } |
461 | catch (IOException e) | |
462 | { | |
463 | 0 | e.printStackTrace(); |
464 | } | |
465 | } | |
466 | ||
467 | // attempt to load from the user home directory | |
468 | 929 | if (url == null) |
469 | { | |
470 | try | |
471 | { | |
472 | 44 | File file = constructFile(System.getProperty("user.home"), name); |
473 | 44 | if (file != null && file.exists()) |
474 | { | |
475 | 1 | url = file.toURL(); |
476 | } | |
477 | ||
478 | 44 | if (url != null) |
479 | { | |
480 | 1 | log.debug("Configuration loaded from the home path " + name); |
481 | } | |
482 | ||
483 | 44 | } |
484 | catch (IOException e) | |
485 | { | |
486 | 0 | e.printStackTrace(); |
487 | } | |
488 | } | |
489 | ||
490 | // attempt to load from classpath | |
491 | 929 | if (url == null) |
492 | { | |
493 | 43 | url = locateFromClasspath(name); |
494 | } | |
495 | 929 | 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 | 43 | URL url = null; |
507 | // attempt to load from the context classpath | |
508 | 43 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
509 | 43 | if (loader != null) |
510 | { | |
511 | 41 | url = loader.getResource(resourceName); |
512 | ||
513 | 41 | if (url != null) |
514 | { | |
515 | 18 | log.debug("Configuration loaded from the context classpath (" + resourceName + ")"); |
516 | } | |
517 | } | |
518 | ||
519 | // attempt to load from the system classpath | |
520 | 43 | if (url == null) |
521 | { | |
522 | 25 | url = ClassLoader.getSystemResource(resourceName); |
523 | ||
524 | 25 | if (url != null) |
525 | { | |
526 | 1 | log.debug("Configuration loaded from the system classpath (" + resourceName + ")"); |
527 | } | |
528 | } | |
529 | 43 | 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 | 19 | if (url == null) |
542 | { | |
543 | 0 | return null; |
544 | } | |
545 | ||
546 | 19 | String s = url.toString(); |
547 | ||
548 | 19 | if (s.endsWith("/") || StringUtils.isEmpty(url.getPath())) |
549 | { | |
550 | 3 | return s; |
551 | } | |
552 | else | |
553 | { | |
554 | 16 | 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 | 17 | if (url == null) |
567 | { | |
568 | 1 | return null; |
569 | } | |
570 | ||
571 | 16 | String path = url.getPath(); |
572 | ||
573 | 16 | if (path.endsWith("/") || StringUtils.isEmpty(path)) |
574 | { | |
575 | 1 | return null; |
576 | } | |
577 | else | |
578 | { | |
579 | 15 | 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 | // Check if URLs are involved | |
596 | URL url; | |
597 | try | |
598 | { | |
599 | 29 | url = new URL(new URL(basePath), fileName); |
600 | 3 | } |
601 | catch (MalformedURLException mex1) | |
602 | { | |
603 | 26 | try |
604 | { | |
605 | 26 | url = new URL(fileName); |
606 | 2 | } |
607 | catch (MalformedURLException mex2) | |
608 | { | |
609 | 24 | url = null; |
610 | } | |
611 | } | |
612 | ||
613 | 29 | if (url != null) |
614 | { | |
615 | 5 | return fileFromURL(url); |
616 | } | |
617 | ||
618 | 24 | 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 | 1128 | if (PROTOCOL_FILE.equals(url.getProtocol())) |
631 | { | |
632 | 1126 | return new File(URLDecoder.decode(url.getPath())); |
633 | } | |
634 | else | |
635 | { | |
636 | 2 | return null; |
637 | } | |
638 | } | |
639 | } |