Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ConfigurationUtils |
|
| 4.434782608695652;4,435 | ||||
ConfigurationUtils$1 |
|
| 4.434782608695652;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 | 97 | String key = keys.next(); |
145 | 97 | target.setProperty(key, source.getProperty(key)); |
146 | 97 | } |
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 | 4276 | if (conf == null) |
215 | { | |
216 | 1 | return null; |
217 | } | |
218 | ||
219 | 4275 | if (conf instanceof HierarchicalConfiguration) |
220 | { | |
221 | HierarchicalConfiguration hc; | |
222 | 4130 | if (conf instanceof Reloadable) |
223 | { | |
224 | 4108 | Object lock = ((Reloadable) conf).getReloadLock(); |
225 | 4108 | synchronized (lock) |
226 | { | |
227 | 4108 | hc = new HierarchicalConfiguration((HierarchicalConfiguration) conf); |
228 | 4108 | } |
229 | 4108 | } |
230 | else | |
231 | { | |
232 | 22 | hc = (HierarchicalConfiguration) conf; |
233 | } | |
234 | 4130 | if (engine != null) |
235 | { | |
236 | 1 | hc.setExpressionEngine(engine); |
237 | } | |
238 | ||
239 | 4130 | return hc; |
240 | } | |
241 | else | |
242 | { | |
243 | 145 | HierarchicalConfiguration hc = new HierarchicalConfiguration(); |
244 | 145 | if (engine != null) |
245 | { | |
246 | 2 | hc.setExpressionEngine(engine); |
247 | } | |
248 | ||
249 | // Workaround for problem with copy() | |
250 | 145 | boolean delimiterParsingStatus = hc.isDelimiterParsingDisabled(); |
251 | 145 | hc.setDelimiterParsingDisabled(true); |
252 | 145 | hc.append(conf); |
253 | 145 | hc.setDelimiterParsingDisabled(delimiterParsingStatus); |
254 | 145 | 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 | 1398 | File absolute = null; |
364 | 1398 | if (fileName != null) |
365 | { | |
366 | 1398 | absolute = new File(fileName); |
367 | } | |
368 | ||
369 | 1398 | if (StringUtils.isEmpty(basePath) || (absolute != null && absolute.isAbsolute())) |
370 | { | |
371 | 72 | file = new File(fileName); |
372 | } | |
373 | else | |
374 | { | |
375 | 1326 | StringBuilder fName = new StringBuilder(); |
376 | 1326 | fName.append(basePath); |
377 | ||
378 | // My best friend. Paranoia. | |
379 | 1326 | if (!basePath.endsWith(File.separator)) |
380 | { | |
381 | 1326 | 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 | 1326 | if (fileName.startsWith("." + File.separator)) |
391 | { | |
392 | 0 | fName.append(fileName.substring(2)); |
393 | } | |
394 | else | |
395 | { | |
396 | 1326 | fName.append(fileName); |
397 | } | |
398 | ||
399 | 1326 | file = new File(fName.toString()); |
400 | } | |
401 | ||
402 | 1398 | 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 | 7103 | 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 | 7103 | if (name == null) |
453 | { | |
454 | // undefined, always return null | |
455 | 10 | return null; |
456 | } | |
457 | ||
458 | // attempt to create an URL directly | |
459 | ||
460 | 7093 | URL url = fileSystem.locateFromURL(base, name); |
461 | ||
462 | // attempt to load from an absolute path | |
463 | 7093 | if (url == null) |
464 | { | |
465 | 1453 | File file = new File(name); |
466 | 1453 | if (file.isAbsolute() && file.exists()) // already absolute? |
467 | { | |
468 | try | |
469 | { | |
470 | 328 | url = toURL(file); |
471 | 328 | 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 | 328 | } |
477 | } | |
478 | } | |
479 | ||
480 | // attempt to load from the base directory | |
481 | 7093 | if (url == null) |
482 | { | |
483 | try | |
484 | { | |
485 | 1125 | File file = constructFile(base, name); |
486 | 1125 | if (file != null && file.exists()) |
487 | { | |
488 | 901 | url = toURL(file); |
489 | } | |
490 | ||
491 | 1125 | if (url != null) |
492 | { | |
493 | 901 | 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 | 1125 | } |
500 | } | |
501 | ||
502 | // attempt to load from the user home directory | |
503 | 7093 | 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 | 7093 | if (url == null) |
527 | { | |
528 | 224 | url = locateFromClasspath(name); |
529 | } | |
530 | 7093 | 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 | 145 | 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 | 79 | url = ClassLoader.getSystemResource(resourceName); |
558 | ||
559 | 79 | 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 | * <properties fileName="/subdir/my.properties"/> | |
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 | 35407 | if (url == null || !url.getProtocol().equals(PROTOCOL_FILE)) |
693 | { | |
694 | 7 | return null; |
695 | } | |
696 | else | |
697 | { | |
698 | 35400 | String filename = url.getFile().replace('/', File.separatorChar); |
699 | 35400 | int pos = 0; |
700 | 35410 | 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 | 35400 | 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 | 1290 | 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 | } |