Line | Hits | Source |
---|---|---|
1 | /* | |
2 | Chrysalis Web Framework [http://chrysalis.sourceforge.net] | |
3 | Copyright (c) 2002, 2003, 2004, Paul Strack | |
4 | ||
5 | All rights reserved. | |
6 | ||
7 | Redistribution and use in source and binary forms, with or without | |
8 | modification, are permitted provided that the following conditions are met: | |
9 | ||
10 | 1. Redistributions of source code must retain the above copyright notice, this | |
11 | list of conditions and the following disclaimer. | |
12 | ||
13 | 2. Redistributions in binary form must reproduce the above copyright notice, | |
14 | this list of conditions and the following disclaimer in the documentation | |
15 | and/or other materials provided with the distribution. | |
16 | ||
17 | 3. Neither the name of the copyright holder nor the names of its contributors | |
18 | may be used to endorse or promote products derived from this software without | |
19 | specific prior written permission. | |
20 | ||
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | */ | |
32 | ||
33 | package org.chwf.config; | |
34 | ||
35 | import java.text.NumberFormat; | |
36 | import java.text.ParseException; | |
37 | import java.util.Collection; | |
38 | import java.util.Collections; | |
39 | import java.util.Iterator; | |
40 | import java.util.Locale; | |
41 | import java.util.Map; | |
42 | import java.util.SortedMap; | |
43 | import java.util.StringTokenizer; | |
44 | import java.util.TreeMap; | |
45 | ||
46 | import org.chwf.util.SequencedHashMap; | |
47 | ||
48 | /** | |
49 | * <p>A class for retrieving configuration data from properties and XML files. | |
50 | * See the package documentation for further information.</p> | |
51 | * | |
52 | * @author <a href="mailto:pfstrack@users.sourceforge.net">Paul Strack</a> | |
53 | */ | |
54 | class ConfigImpl implements Config, RawConfig { | |
55 | ||
56 | /** Empty string array. */ | |
57 | 1 | private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
58 | ||
59 | /** Empty string. */ | |
60 | private static final String EMPTY_STRING = ""; | |
61 | ||
62 | /** Name of configuration resource. */ | |
63 | private final String resource; | |
64 | ||
65 | /** Localized name of resource. */ | |
66 | private final String localizedResource; | |
67 | ||
68 | /** Map containing configuration data. */ | |
69 | private final Map data; | |
70 | ||
71 | /** Reference to parent configuration object. */ | |
72 | private final ConfigImpl parentConfig; | |
73 | ||
74 | /** Reference to config for parent locale. */ | |
75 | private final ConfigImpl parentLocale; | |
76 | ||
77 | /** Reference to config for parent locale. */ | |
78 | private final boolean disableChildConfig; | |
79 | ||
80 | /** Property for disabling parent configuration: "disable.parent.config". */ | |
81 | private static final String DISABLE_PARENT_CONFIG = "disable.parent.config"; | |
82 | ||
83 | /** Property for disabling child configuration: "disable.child.config". */ | |
84 | private static final String DISABLE_CHILD_CONFIG = "disable.child.config"; | |
85 | ||
86 | /** Constant for wildcard. */ | |
87 | private static final String WILDCARD = "*"; | |
88 | ||
89 | /** Character delimiting values in delimited lists. */ | |
90 | private static final String LIST_DELIMITER = ","; | |
91 | ||
92 | /** No-parameter constructor for subclasses. */ | |
93 | 1 | ConfigImpl() { |
94 | 1 | this.resource = null; |
95 | 1 | this.localizedResource = null; |
96 | 1 | this.data = null; |
97 | 1 | this.parentConfig = null; |
98 | 1 | this.parentLocale = null; |
99 | 1 | this.disableChildConfig = false; |
100 | 1 | } |
101 | ||
102 | /** | |
103 | * Class-based constructor for subclasses. | |
104 | * | |
105 | * @param contextClass The context class. | |
106 | * @param locale The locale. | |
107 | */ | |
108 | 378 | ConfigImpl(Class contextClass, Locale locale) { |
109 | 378 | this.resource = contextClass.getName(); |
110 | 378 | this.localizedResource = |
111 | this.resource + ConfigFactory.deriveLocaleName(locale); | |
112 | 378 | this.data = null; |
113 | 378 | this.parentConfig = null; |
114 | 378 | this.parentLocale = null; |
115 | 378 | this.disableChildConfig = false; |
116 | 378 | } |
117 | ||
118 | /** | |
119 | * Constructor using the resource name. | |
120 | * | |
121 | * @param resource The name of the resource. | |
122 | * @param locale The locale. | |
123 | */ | |
124 | 3544 | ConfigImpl(String resource, Locale locale) { |
125 | 3544 | this.resource = resource; |
126 | 3544 | this.localizedResource = |
127 | this.resource + ConfigFactory.deriveLocaleName(locale); | |
128 | 3544 | this.data = ConfigFactory.initData(localizedResource); |
129 | ||
130 | 3542 | boolean disableChildConfig = false; |
131 | 3542 | if (this.data.get(DISABLE_PARENT_CONFIG) == null) { |
132 | 3525 | this.parentConfig = |
133 | (ConfigImpl) ConfigFactory.deriveParentConfig(resource, locale); | |
134 | 3525 | disableChildConfig = this.parentConfig.disableChildConfig; |
135 | 3525 | if (this.parentConfig.disableChildConfig) { |
136 | 1 | data.clear(); |
137 | } | |
138 | } else { | |
139 | 17 | this.parentConfig = (ConfigImpl) ConfigFactory.TERMINAL_CONFIG; |
140 | } | |
141 | 3542 | if (this.data.get(DISABLE_CHILD_CONFIG) != null) { |
142 | 1 | disableChildConfig = true; |
143 | } | |
144 | ||
145 | 3542 | this.disableChildConfig = disableChildConfig; |
146 | 3542 | this.parentLocale = |
147 | (ConfigImpl) ConfigFactory.deriveParentLocale(resource, locale); | |
148 | 3542 | } |
149 | ||
150 | /** | |
151 | * Gets the named property value.<p> | |
152 | * | |
153 | * @param key The search key. | |
154 | * @return The property value. | |
155 | * @throws ConfigurationException If the property is not found. | |
156 | */ | |
157 | public String get(String key) throws ConfigurationException { | |
158 | 135 | String value = getRawValue(key); |
159 | 135 | if (value != null) { |
160 | 121 | return value; |
161 | } else { | |
162 | 14 | Object[] arguments = { this.localizedResource, key }; |
163 | 14 | throw new ConfigurationException( |
164 | ConfigurationException.MESSAGE_PROPERTY_NOT_FOUND, | |
165 | arguments); | |
166 | } | |
167 | } | |
168 | ||
169 | /** | |
170 | * Gets the named property value.<p> | |
171 | * | |
172 | * @param key The search key. | |
173 | * @param defaultValue A default value. | |
174 | * @return The property value or the default value if the property is | |
175 | * not found. | |
176 | */ | |
177 | public String get(String key, String defaultValue) { | |
178 | 642 | String value = getRawValue(key); |
179 | 642 | if (value != null) { |
180 | 178 | return value; |
181 | } else { | |
182 | 464 | return defaultValue; |
183 | } | |
184 | } | |
185 | ||
186 | /** | |
187 | * Gets the named property value as an int value. String values in the | |
188 | * configuration file are parsed using the system locale.<p> | |
189 | * | |
190 | * @param key The search key. | |
191 | * @return The property value as an int. | |
192 | * @throws ConfigurationException If the property is not found or is not a | |
193 | * number. | |
194 | */ | |
195 | public int getInt(String key) throws ConfigurationException { | |
196 | try { | |
197 | 6 | return NumberFormat.getNumberInstance().parse(get(key)).intValue(); |
198 | } catch (ParseException ex) { | |
199 | 2 | throw new ConfigurationException( |
200 | ConfigurationException.MESSAGE_PROPERTY_NOT_NUMBER, | |
201 | new Object[] { this.localizedResource, key }); | |
202 | } | |
203 | } | |
204 | ||
205 | /** | |
206 | * Gets the named property value as an int value. String values in the | |
207 | * configuration file are parsed using the system locale.<p> | |
208 | * | |
209 | * @param key The search key. | |
210 | * @param defaultValue A default value. | |
211 | * @return The property value as an int, or the default value if | |
212 | * the property is not found or is not a number. | |
213 | */ | |
214 | public int getInt(String key, int defaultValue) { | |
215 | try { | |
216 | 6 | return NumberFormat.getNumberInstance().parse(get(key)).intValue(); |
217 | } catch (Exception ex) { | |
218 | 4 | return defaultValue; |
219 | } | |
220 | } | |
221 | ||
222 | /** | |
223 | * Gets the named property value as a double value. String values in the | |
224 | * configuration file are parsed using the system locale.<p> | |
225 | * | |
226 | * @param key The search key. | |
227 | * @return The property value as a double. | |
228 | * @throws ConfigurationException If the property is not found or is not a | |
229 | * number. | |
230 | */ | |
231 | public double getDouble(String key) throws ConfigurationException { | |
232 | try { | |
233 | 6 | return NumberFormat.getNumberInstance().parse(get(key)).doubleValue(); |
234 | } catch (ParseException ex) { | |
235 | 2 | throw new ConfigurationException( |
236 | ConfigurationException.MESSAGE_PROPERTY_NOT_NUMBER, | |
237 | new Object[] { this.localizedResource, key }); | |
238 | } | |
239 | } | |
240 | ||
241 | /** | |
242 | * Gets the named property value as a double value. String values in the | |
243 | * configuration file are parsed using the system locale.<p> | |
244 | * | |
245 | * @param key The search key. | |
246 | * @param defaultValue A default value. | |
247 | * @return The property value as a double, or the default value if | |
248 | * the property is not found or is not a number. | |
249 | */ | |
250 | public double getDouble(String key, double defaultValue) { | |
251 | try { | |
252 | 6 | return NumberFormat.getNumberInstance().parse(get(key)).doubleValue(); |
253 | } catch (Exception ex) { | |
254 | 4 | return defaultValue; |
255 | } | |
256 | } | |
257 | ||
258 | /** | |
259 | * Gets the named property value as a boolean value.<p> | |
260 | * | |
261 | * @param key The search key. | |
262 | * @return The property value as a boolean. | |
263 | * @throws ConfigurationException If the property is not found. | |
264 | */ | |
265 | public boolean getBoolean(String key) throws ConfigurationException { | |
266 | 6 | return Boolean.valueOf(get(key)).booleanValue(); |
267 | } | |
268 | ||
269 | /** | |
270 | * Gets the named property value as a boolean value.<p> | |
271 | * | |
272 | * @param key The search key. | |
273 | * @param defaultValue A default value. | |
274 | * @return The property value as a boolean, or the default value if | |
275 | * the property is not found. | |
276 | */ | |
277 | public boolean getBoolean(String key, boolean defaultValue) { | |
278 | 108 | String value = getRawValue(key); |
279 | 108 | if (value != null) { |
280 | 3 | return Boolean.valueOf(get(key)).booleanValue(); |
281 | } else { | |
282 | 105 | return defaultValue; |
283 | } | |
284 | } | |
285 | ||
286 | /** | |
287 | * <p>Gets an array of strings for a property key. This is done in | |
288 | * one of two ways:</p> | |
289 | * | |
290 | * <ul> | |
291 | * <li>Get a list of all entries beginning with the specified key.</li> | |
292 | * <li>If there is only one entry corresponding to the key, parse | |
293 | * that entry as a comma-delimited list.</li> | |
294 | * </ul> | |
295 | * | |
296 | * <p>Note that this method never returns an empty list. If the property | |
297 | * is not found, a <code>ConfigurationException</code> is thrown. If | |
298 | * you need to allow for the possibility of empty lists, use the values | |
299 | * of the <code>getMap()</code> method instead:</p> | |
300 | * | |
301 | * <pre> | |
302 | * Collection list = config.getMap().values();</pre> | |
303 | * | |
304 | * @param key The search key. | |
305 | * @return The list of property values. | |
306 | * @throws ConfigurationException If the property is not found. | |
307 | */ | |
308 | public String[] getList(String key) throws ConfigurationException { | |
309 | 84 | String[] list = getList(key, null); |
310 | 84 | if (list == null) { |
311 | 74 | throw new ConfigurationException( |
312 | ConfigurationException.MESSAGE_PROPERTY_NOT_FOUND, | |
313 | new Object[] { this.localizedResource, key }); | |
314 | } else { | |
315 | 10 | return list; |
316 | } | |
317 | } | |
318 | ||
319 | /** | |
320 | * <p>As {@link #getList(java.lang.String)}, but with a default.</p> | |
321 | * | |
322 | * @param key The search key. | |
323 | * @param defaultList The default list. | |
324 | * @return The list of property values or the default if not found. | |
325 | */ | |
326 | public String[] getList(String key, String[] defaultList) { | |
327 | 410 | SortedMap map = getMap(key); |
328 | 410 | Collection data = map.values(); |
329 | 410 | if (data.size() == 0) { |
330 | 344 | return defaultList; |
331 | 66 | } else if ( |
332 | (data.size() == 1) | |
333 | && (map.keySet().iterator().next().equals(EMPTY_STRING))) { | |
334 | 47 | return getDelimitedList(key); |
335 | } else { | |
336 | 19 | String[] list = new String[data.size()]; |
337 | 19 | data.toArray(list); |
338 | 19 | return list; |
339 | } | |
340 | } | |
341 | ||
342 | /** | |
343 | * <p>Gets a sorted map consisting of all entries beginning with | |
344 | * the specified key. The keys in the sorted map will be the | |
345 | * property keys with the search key (and any leading ".") | |
346 | * removed. For example, suppose the following values are in | |
347 | * the property file:</p> | |
348 | * | |
349 | * <pre> | |
350 | * map.value.1=Value 1 | |
351 | * map.value.2=Value 2 | |
352 | * map.value.3=Value 3</pre> | |
353 | * | |
354 | * <p>The method call <code>config.getMap("map.value")</code> will | |
355 | * result in a map with 3 values, whose keys are "1", "2" and "3".</p> | |
356 | * | |
357 | * <p>Note that the sort order is based on <i>string</i> ordering, | |
358 | * not numeric ordering. This means if you have more than 10 values, | |
359 | * you will have to number them more carefully if the ordering is | |
360 | * important:</p> | |
361 | * | |
362 | * <pre> | |
363 | * map.value.01=Value 1 | |
364 | * map.value.02=Value 2 | |
365 | * map.value.03=Value 3 | |
366 | * map.value.04=Value 4 | |
367 | * map.value.05=Value 5 | |
368 | * map.value.06=Value 6 | |
369 | * map.value.07=Value 7 | |
370 | * map.value.08=Value 8 | |
371 | * map.value.09=Value 9 | |
372 | * map.value.10=Value 10 | |
373 | * map.value.11=Value 11</pre> | |
374 | * | |
375 | * @param key The search key, which is used as the initial part | |
376 | * of property keys. | |
377 | * @return The sorted map, or an empty map if no property keys | |
378 | * beginning with the search key. | |
379 | */ | |
380 | public SortedMap getMap(String key) { | |
381 | 1375 | Map map = getOrderedMap(key); |
382 | 1375 | if (map.isEmpty()) { |
383 | 1232 | return ConfigFactory.EMPTY_SORTED_MAP; |
384 | } | |
385 | 143 | return new TreeMap(map); |
386 | } | |
387 | ||
388 | /** | |
389 | * <p>Gets an ordered map consisting of all entries beginning with the | |
390 | * specified key. The keys in the sorted map will be the property keys | |
391 | * with the search key (and any leading ".") removed. If the configuration | |
392 | * data is in an XML file, the data retains the sequential ordering of the | |
393 | * elements in the XML file. For example, suppose the XML data were as | |
394 | * follows:</p> | |
395 | * | |
396 | * <pre> | |
397 | * <map.values> | |
398 | * <value name="C">Item 1</data> | |
399 | * <value name="B">Item 2</data> | |
400 | * <value name="A">Item 3</data> | |
401 | * <map.values></pre> | |
402 | * | |
403 | * <p>The method call <code>config.getOrderedMap("map.values")</code> will | |
404 | * result in a map with 3 values, whose keys are "C", "B" and "A" in that | |
405 | * order. Compare this to the <code>getMap()</code> method, which sorts keys | |
406 | * in alphabetical order rather than in sequential order.</p> | |
407 | * | |
408 | * <p>This method only functions correctly for configuration data in XML | |
409 | * files, because property files are not ordered. Using this method for | |
410 | * data in property files will result in keys sorted in pseudo-random | |
411 | * order.</p> | |
412 | * | |
413 | * @param key The search key, which is used as the initial part | |
414 | * of property keys. | |
415 | * @return The ordered map, or an empty map if no property keys | |
416 | * beginning with the search key. | |
417 | */ | |
418 | public Map getOrderedMap(String key) { | |
419 | 952 | Map map = getMapInConfig(key); |
420 | 952 | if (map.isEmpty()) { |
421 | 928 | map = getParentConfig().getMapWithContext(this.resource, key); |
422 | } | |
423 | 952 | if (map.isEmpty()) { |
424 | 829 | map = this.parentLocale.getMap(key); |
425 | } | |
426 | 952 | return map; |
427 | } | |
428 | ||
429 | /** | |
430 | * Get the raw configuration data. Parent configuration data is not included. | |
431 | * | |
432 | * @return The list of keys for the current Config object. | |
433 | */ | |
434 | public Map getRawData() { | |
435 | 2 | return Collections.unmodifiableMap(this.data); |
436 | } | |
437 | ||
438 | /** | |
439 | * <p>Gets a sorted map based on the context. | |
440 | * | |
441 | * @param context The search context. | |
442 | * @param key The search key, which is used as the initial part | |
443 | * of property keys. | |
444 | * @return The sorted map, or an empty map if no property keys | |
445 | * beginning with the search key. | |
446 | */ | |
447 | Map getMapWithContext(String context, String key) { | |
448 | 3886 | String baseName = ConfigFactory.stripLastName(this.resource); |
449 | 3886 | String searchContext = context.substring(baseName.length()); |
450 | 3886 | Map map = null; |
451 | ||
452 | 19027 | while (searchContext.length() > 0) { |
453 | 11350 | String prefixedKey = searchContext + ConfigFactory.KEY_DELIMITER + key; |
454 | 11350 | map = getMapInConfig(prefixedKey); |
455 | 11350 | if (!map.isEmpty()) { |
456 | 95 | return map; |
457 | } | |
458 | 11255 | searchContext = ConfigFactory.stripLastNameAndPeriod(searchContext); |
459 | } | |
460 | ||
461 | 3791 | map = getMapInConfig(key); |
462 | 3791 | if (map.isEmpty()) { |
463 | 3787 | map = getParentConfig().getMapWithContext(context, key); |
464 | } | |
465 | ||
466 | 3791 | return map; |
467 | } | |
468 | ||
469 | /** | |
470 | * Gets configuration property using the specified context name. | |
471 | * This method checks all possible prefixed and suffixed keys and recurses | |
472 | * up the configuration search path. It has package-level access so it | |
473 | * can be overridden in the TerminalConfig subclass. | |
474 | * | |
475 | * @param context The search context. | |
476 | * @param key The search key. | |
477 | * @return The value or <code>null</code> if it does not exist. | |
478 | */ | |
479 | String getWithContext(String context, String key) { | |
480 | 5699 | String baseName = ConfigFactory.stripLastName(this.resource); |
481 | 5699 | String searchContext = context.substring(baseName.length()); |
482 | 5699 | String value = null; |
483 | ||
484 | 27911 | while (searchContext.length() > 0) { |
485 | 16742 | String prefixedKey = searchContext + ConfigFactory.KEY_DELIMITER + key; |
486 | 16742 | value = (String) data.get(prefixedKey); |
487 | 16742 | if (value != null) { |
488 | 229 | return value; |
489 | } | |
490 | 16513 | searchContext = ConfigFactory.stripLastNameAndPeriod(searchContext); |
491 | } | |
492 | ||
493 | 5470 | value = (String) data.get(key); |
494 | 5470 | if (value == null) { |
495 | 5460 | value = getParentConfig().getWithContext(context, key); |
496 | } | |
497 | ||
498 | 5470 | return value; |
499 | } | |
500 | ||
501 | /** | |
502 | * Get parent Config object in search path. | |
503 | * | |
504 | * @return The parent configuration. | |
505 | */ | |
506 | private ConfigImpl getParentConfig() { | |
507 | 11511 | return this.parentConfig; |
508 | } | |
509 | ||
510 | /** | |
511 | * Get raw data value. | |
512 | * | |
513 | * @param key The search key. | |
514 | * @return The value or <code>null</code> if it does not exist. | |
515 | */ | |
516 | public String getRawValue(String key) { | |
517 | 1401 | String value = (String) data.get(key); |
518 | 1401 | if (value == null) { |
519 | 1336 | value = getParentConfig().getWithContext(this.resource, key); |
520 | } | |
521 | 1401 | if (value == null) { |
522 | 1097 | value = this.parentLocale.getRawValue(key); |
523 | } | |
524 | 1401 | return value; |
525 | } | |
526 | ||
527 | /** | |
528 | * Get raw data map in a single Config object. | |
529 | * | |
530 | * @param key The search key. | |
531 | * @return A map with values beginning with the given key. | |
532 | */ | |
533 | private Map getMapInConfig(String key) { | |
534 | 16093 | int keyLength = key.length(); |
535 | 16093 | Map map = new SequencedHashMap(); |
536 | 16093 | Iterator i = this.data.keySet().iterator(); |
537 | 71720 | while (i.hasNext()) { |
538 | 39534 | String dataKey = i.next().toString(); |
539 | 39534 | String mapKey = deriveMapKey(key, dataKey); |
540 | 39534 | if (mapKey != null) { |
541 | 299 | if ((mapKey.length() > 0) |
542 | && (mapKey.charAt(0) == ConfigFactory.KEY_DELIMITER)) { | |
543 | 211 | mapKey = mapKey.substring(1); |
544 | } | |
545 | 299 | map.put(mapKey, data.get(dataKey)); |
546 | } | |
547 | } | |
548 | 16093 | return map; |
549 | } | |
550 | ||
551 | /** | |
552 | * Derive submap key from key pattern and actual key | |
553 | * | |
554 | * @param key The key pattern (with wildcards) | |
555 | * @param dataKey The actual key | |
556 | * @return The derived submap key or null if it does not match | |
557 | */ | |
558 | private static String deriveMapKey(String key, String dataKey) { | |
559 | 39534 | StringBuffer mapKey = new StringBuffer(); |
560 | 39534 | StringTokenizer tokenizer = new StringTokenizer(key, WILDCARD); |
561 | 39534 | int lastMatchPosition = -1; |
562 | 39534 | boolean isFirstToken = true; |
563 | 79429 | while (tokenizer.hasMoreTokens()) { |
564 | 39596 | String token = tokenizer.nextToken(); |
565 | 39596 | int currentMatchPosition = dataKey.indexOf(token, lastMatchPosition); |
566 | 39596 | if (isFirstToken) { |
567 | 39534 | if (!dataKey.startsWith(token)) { |
568 | 39192 | return null; |
569 | } | |
570 | } | |
571 | 404 | if (lastMatchPosition >= currentMatchPosition) { |
572 | 43 | return null; |
573 | } else { | |
574 | 361 | if (!isFirstToken) { |
575 | 19 | String wildcard = |
576 | dataKey.substring(lastMatchPosition, currentMatchPosition); | |
577 | 19 | mapKey.append(wildcard); |
578 | } | |
579 | 361 | lastMatchPosition = currentMatchPosition + token.length(); |
580 | } | |
581 | 361 | isFirstToken = false; |
582 | } | |
583 | 299 | mapKey.append(dataKey.substring(lastMatchPosition)); |
584 | 299 | return mapKey.toString(); |
585 | } | |
586 | ||
587 | /** | |
588 | * Gets the named property value by parsing it as a comma-delimited list. | |
589 | * | |
590 | * @param key The search key. | |
591 | * @return An array containing the list of property values. | |
592 | * @throws ConfigurationException If the property is not found. | |
593 | */ | |
594 | private String[] getDelimitedList(String key) throws ConfigurationException { | |
595 | 47 | String value = get(key).trim(); |
596 | 47 | if (value.length() == 0) { |
597 | 1 | return EMPTY_STRING_ARRAY; |
598 | } | |
599 | 46 | StringTokenizer t = new StringTokenizer(value, LIST_DELIMITER); |
600 | 46 | String[] array = new String[t.countTokens()]; |
601 | 46 | int count = 0; |
602 | 147 | while (t.hasMoreTokens()) { |
603 | 55 | array[count++] = t.nextToken().trim(); |
604 | } | |
605 | 46 | return array; |
606 | } | |
607 | ||
608 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |