Coverage report

| %line | %branch
--- | --- | ---
org.apache.commons.configuration.ConfigurationKey |
97% |
100% |

1 |  |
     /*
2 |  |
      * Copyright 2004 The Apache Software Foundation.
3 |  |
      *
4 |  |
      * Licensed under the Apache License, Version 2.0 (the "License")
5 |  |
      * you may not use this file except in compliance with the License.
6 |  |
      * You may obtain a copy of the License at
7 |  |
      *
8 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
9 |  |
      *
10 |  |
      * Unless required by applicable law or agreed to in writing, software
11 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
12 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 |  |
      * See the License for the specific language governing permissions and
14 |  |
      * limitations under the License.
15 |  |
      */
16 |  |
17 |  |
     package org.apache.commons.configuration;
18 |  |
19 |  |
     import java.io.Serializable;
20 |  |
     import java.util.Iterator;
21 |  |
     import java.util.NoSuchElementException;
22 |  |
23 |  |
     /**
24 |  |
      * <p>A simple class that supports creation of and iteration on complex
25 |  |
      * configuration keys.</p>
26 |  |
      *
27 |  |
      * <p>For key creation the class works similar to a StringBuffer: There are
28 |  |
      * several <code>appendXXXX()</code> methods with which single parts
29 |  |
      * of a key can be constructed. All these methods return a reference to the
30 |  |
      * actual object so they can be written in a chain. When using this methods
31 |  |
      * the exact syntax for keys need not be known.</p>
32 |  |
      *
33 |  |
      * <p>This class also defines a specialized iterator for configuration keys.
34 |  |
      * With such an iterator a key can be tokenized into its single parts. For
35 |  |
      * each part it can be checked whether it has an associated index.</p>
36 |  |
      *
37 |  |
      * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
38 |  |
      * @version $Id: ConfigurationKey.java,v 1.5 2004/08/14 11:23:14 epugh Exp $
39 |  |
      */
40 | 16123 |
     public class ConfigurationKey implements Serializable
41 |  |
     {
42 |  |
         /** Constant for an attribute start marker.*/
43 |  |
         private static final String ATTRIBUTE_START = "[@";
44 |  |
45 |  |
         /** Constant for an attribute end marker.*/
46 |  |
         private static final String ATTRIBUTE_END = "]";
47 |  |
48 |  |
         /** Constant for a property delimiter.*/
49 |  |
         private static final char PROPERTY_DELIMITER = '.';
50 |  |
51 |  |
         /** Constant for an index start marker.*/
52 |  |
         private static final char INDEX_START = '(';
53 |  |
54 |  |
         /** Constant for an index end marker.*/
55 |  |
         private static final char INDEX_END = ')';
56 |  |
57 |  |
         /** Constant for the initial StringBuffer size.*/
58 |  |
         private static final int INITIAL_SIZE = 32;
59 |  |
60 |  |
         /** Holds a buffer with the so far created key.*/
61 |  |
         private StringBuffer keyBuffer;
62 |  |
63 |  |
         /**
64 |  |
          * Creates a new, empty instance of <code>ConfigurationKey</code>.
65 |  |
          */
66 |  |
         public ConfigurationKey()
67 | 384 |
         {
68 | 384 |
             keyBuffer = new StringBuffer(INITIAL_SIZE);
69 | 384 |
         }
70 |  |
71 |  |
         /**
72 |  |
          * Creates a new instance of <code>ConfigurationKey</code> and
73 |  |
          * initializes it with the given key.
74 |  |
          *
75 |  |
          * @param key the key as a string
76 |  |
          */
77 |  |
         public ConfigurationKey(String key)
78 | 260 |
         {
79 | 260 |
             keyBuffer = new StringBuffer(key);
80 | 260 |
             removeTrailingDelimiter();
81 | 260 |
         }
82 |  |
83 |  |
         /**
84 |  |
          * Appends the name of a property to this key. If necessary, a
85 |  |
          * property delimiter will be added.
86 |  |
          *
87 |  |
          * @param property the name of the property to be added
88 |  |
          * @return a reference to this object
89 |  |
          */
90 |  |
         public ConfigurationKey append(String property)
91 |  |
         {
92 | 459 |
             if (keyBuffer.length() > 0 && !hasDelimiter() && !isAttributeKey(property))
93 |  |
             {
94 | 146 |
                 keyBuffer.append(PROPERTY_DELIMITER);
95 |  |
             }
96 |  |
97 | 459 |
             keyBuffer.append(property);
98 | 459 |
             removeTrailingDelimiter();
99 | 459 |
             return this;
100 |  |
         }
101 |  |
102 |  |
         /**
103 |  |
          * Appends an index to this configuration key.
104 |  |
          *
105 |  |
          * @param index the index to be appended
106 |  |
          * @return a reference to this object
107 |  |
          */
108 |  |
         public ConfigurationKey appendIndex(int index)
109 |  |
         {
110 | 20 |
             keyBuffer.append(INDEX_START).append(index);
111 | 20 |
             keyBuffer.append(INDEX_END);
112 | 20 |
             return this;
113 |  |
         }
114 |  |
115 |  |
         /**
116 |  |
          * Appends an attribute to this configuration key.
117 |  |
          *
118 |  |
          * @param attr the name of the attribute to be appended
119 |  |
          * @return a reference to this object
120 |  |
          */
121 |  |
         public ConfigurationKey appendAttribute(String attr)
122 |  |
         {
123 | 5 |
             keyBuffer.append(constructAttributeKey(attr));
124 | 5 |
             return this;
125 |  |
         }
126 |  |
127 |  |
         /**
128 |  |
          * Checks if the passed in key is an attribute key. Such attribute keys
129 |  |
          * start and end with certain marker strings. In some cases they must be
130 |  |
          * treated slightly different.
131 |  |
          *
132 |  |
          * @param key the key (part) to be checked
133 |  |
          * @return a flag if this key is an attribute key
134 |  |
          */
135 |  |
         public static boolean isAttributeKey(String key)
136 |  |
         {
137 | 2077 |
             return key != null
138 |  |
             && key.startsWith(ATTRIBUTE_START)
139 |  |
             && key.endsWith(ATTRIBUTE_END);
140 |  |
         }
141 |  |
142 |  |
         /**
143 |  |
          * Decorates the given key so that it represents an attribute. Adds
144 |  |
          * special start and end markers.
145 |  |
          *
146 |  |
          * @param key the key to be decorated
147 |  |
          * @return the decorated attribute key
148 |  |
          */
149 |  |
         public static String constructAttributeKey(String key)
150 |  |
         {
151 | 44 |
             StringBuffer buf = new StringBuffer();
152 | 44 |
             buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END);
153 | 44 |
             return buf.toString();
154 |  |
         }
155 |  |
156 |  |
         /**
157 |  |
          * Extracts the name of the attribute from the given attribute key.
158 |  |
          * This method removes the attribute markers - if any - from the
159 |  |
          * specified key.
160 |  |
          *
161 |  |
          * @param key the attribute key
162 |  |
          * @return the name of the corresponding attribute
163 |  |
          */
164 |  |
         public static String attributeName(String key)
165 |  |
         {
166 | 4 |
             return isAttributeKey(key) ? removeAttributeMarkers(key) : key;
167 |  |
         }
168 |  |
169 |  |
         /**
170 |  |
          * Helper method for removing attribute markers from a key.
171 |  |
          *
172 |  |
          * @param key the key
173 |  |
          * @return the key with removed attribute markers
174 |  |
          */
175 |  |
         private static String removeAttributeMarkers(String key)
176 |  |
         {
177 | 31 |
             return key.substring(ATTRIBUTE_START.length(), key.length() - ATTRIBUTE_END.length());
178 |  |
         }
179 |  |
180 |  |
         /**
181 |  |
          * Helper method that checks if the actual buffer ends with a property
182 |  |
          * delimiter.
183 |  |
          *
184 |  |
          * @return a flag if there is a trailing delimiter
185 |  |
          */
186 |  |
         private boolean hasDelimiter()
187 |  |
         {
188 | 869 |
             return keyBuffer.length() > 0
189 |  |
             && keyBuffer.charAt(keyBuffer.length() - 1) == PROPERTY_DELIMITER;
190 |  |
         }
191 |  |
192 |  |
         /**
193 |  |
          * Removes a trailing delimiter if there is any.
194 |  |
          */
195 |  |
         private void removeTrailingDelimiter()
196 |  |
         {
197 | 721 |
             while (hasDelimiter())
198 |  |
             {
199 | 2 |
                 keyBuffer.deleteCharAt(keyBuffer.length() - 1);
200 |  |
             }
201 | 719 |
         }
202 |  |
203 |  |
         /**
204 |  |
          * Returns a string representation of this object. This is the
205 |  |
          * configuration key as a plain string.
206 |  |
          *
207 |  |
          * @return a string for this object
208 |  |
          */
209 |  |
         public String toString()
210 |  |
         {
211 | 248 |
             return keyBuffer.toString();
212 |  |
         }
213 |  |
214 |  |
         /**
215 |  |
          * Returns an iterator for iterating over the single components of
216 |  |
          * this configuration key.
217 |  |
          *
218 |  |
          * @return an iterator for this key
219 |  |
          */
220 |  |
         public KeyIterator iterator()
221 |  |
         {
222 | 756 |
             return new KeyIterator();
223 |  |
         }
224 |  |
225 |  |
         /**
226 |  |
          * Returns the actual length of this configuration key.
227 |  |
          *
228 |  |
          * @return the length of this key
229 |  |
          */
230 |  |
         public int length()
231 |  |
         {
232 | 471 |
             return keyBuffer.length();
233 |  |
         }
234 |  |
235 |  |
         /**
236 |  |
          * Sets the new length of this configuration key. With this method it is
237 |  |
          * possible to truncate the key, e.g. to return to a state prior calling
238 |  |
          * some <code>append()</code> methods. The semantic is the same as
239 |  |
          * the <code>setLength()</code> method of <code>StringBuffer</code>.
240 |  |
          *
241 |  |
          * @param len the new length of the key
242 |  |
          */
243 |  |
         public void setLength(int len)
244 |  |
         {
245 | 37 |
             keyBuffer.setLength(len);
246 | 37 |
         }
247 |  |
248 |  |
         /**
249 |  |
          * Checks if two <code>ConfigurationKey</code> objects are equal. The
250 |  |
          * method can be called with strings or other objects, too.
251 |  |
          *
252 |  |
          * @param c the object to compare
253 |  |
          * @return a flag if both objects are equal
254 |  |
          */
255 |  |
         public boolean equals(Object c)
256 |  |
         {
257 | 11 |
             if (c == null)
258 |  |
             {
259 | 1 |
                 return false;
260 |  |
             }
261 |  |
262 | 10 |
             return keyBuffer.toString().equals(c.toString());
263 |  |
         }
264 |  |
265 |  |
         /**
266 |  |
          * Returns the hash code for this object.
267 |  |
          *
268 |  |
          * @return the hash code
269 |  |
          */
270 |  |
         public int hashCode()
271 |  |
         {
272 | 0 |
             return keyBuffer.hashCode();
273 |  |
         }
274 |  |
275 |  |
         /**
276 |  |
          * Returns a configuration key object that is initialized with the part
277 |  |
          * of the key that is common to this key and the passed in key.
278 |  |
          *
279 |  |
          * @param other the other key
280 |  |
          * @return a key object with the common key part
281 |  |
          */
282 |  |
         public ConfigurationKey commonKey(ConfigurationKey other)
283 |  |
         {
284 | 219 |
             if (other == null)
285 |  |
             {
286 | 0 |
                 throw new IllegalArgumentException("Other key must no be null!");
287 |  |
             }
288 |  |
289 | 219 |
             ConfigurationKey result = new ConfigurationKey();
290 | 219 |
             KeyIterator it1 = iterator();
291 | 219 |
             KeyIterator it2 = other.iterator();
292 |  |
293 | 435 |
             while (it1.hasNext() && it2.hasNext() && partsEqual(it1, it2))
294 |  |
             {
295 | 216 |
                 if (it1.isAttribute())
296 |  |
                 {
297 | 2 |
                     result.appendAttribute(it1.currentKey());
298 |  |
                 }
299 |  |
                 else
300 |  |
                 {
301 | 214 |
                     result.append(it1.currentKey());
302 | 214 |
                     if (it1.hasIndex)
303 |  |
                     {
304 | 7 |
                         result.appendIndex(it1.getIndex());
305 |  |
                     }
306 |  |
                 }
307 |  |
             }
308 |  |
309 | 219 |
             return result;
310 |  |
         }
311 |  |
312 |  |
         /**
313 |  |
          * Returns the &quot;difference key&quot; to a given key. This value
314 |  |
          * is the part of the passed in key that differs from this key. There is
315 |  |
          * the following relation:
316 |  |
          * <code>other = key.commonKey(other) + key.differenceKey(other)</code>
317 |  |
          * for an arbitrary configuration key <code>key</code>.
318 |  |
          *
319 |  |
          * @param other the key for which the difference is to be calculated
320 |  |
          * @return the difference key
321 |  |
          */
322 |  |
         public ConfigurationKey differenceKey(ConfigurationKey other)
323 |  |
         {
324 | 146 |
             ConfigurationKey common = commonKey(other);
325 | 146 |
             ConfigurationKey result = new ConfigurationKey();
326 |  |
327 | 146 |
             if (common.length() < other.length())
328 |  |
             {
329 | 137 |
                 String k = other.toString().substring(common.length());
330 |  |
                 // skip trailing delimiters
331 | 137 |
                 int i = 0;
332 | 229 |
                 while (i < k.length() && k.charAt(i) == PROPERTY_DELIMITER)
333 |  |
                 {
334 | 92 |
                     i++;
335 |  |
                 }
336 |  |
337 | 137 |
                 if (i < k.length())
338 |  |
                 {
339 | 137 |
                     result.append(k.substring(i));
340 |  |
                 }
341 |  |
             }
342 |  |
343 | 146 |
             return result;
344 |  |
         }
345 |  |
346 |  |
         /**
347 |  |
          * Helper method for comparing two key parts.
348 |  |
          *
349 |  |
          * @param it1 the iterator with the first part
350 |  |
          * @param it2 the iterator with the second part
351 |  |
          * @return a flag if both parts are equal
352 |  |
          */
353 |  |
         private static boolean partsEqual(KeyIterator it1, KeyIterator it2)
354 |  |
         {
355 | 400 |
             return it1.nextKey().equals(it2.nextKey())
356 |  |
             && it1.getIndex() == it2.getIndex()
357 |  |
             && it1.isAttribute() == it2.isAttribute();
358 |  |
         }
359 |  |
360 |  |
         /**
361 |  |
          * A specialized iterator class for tokenizing a configuration key.
362 |  |
          * This class implements the normal iterator interface. In addition it
363 |  |
          * provides some specific methods for configuration keys.
364 |  |
          */
365 |  |
         public class KeyIterator implements Iterator, Cloneable
366 |  |
         {
367 |  |
             /** Stores the current key name.*/
368 |  |
             private String current;
369 |  |
370 |  |
             /** Stores the start index of the actual token.*/
371 |  |
             private int startIndex;
372 |  |
373 |  |
             /** Stores the end index of the actual token.*/
374 |  |
             private int endIndex;
375 |  |
376 |  |
             /** Stores the index of the actual property if there is one.*/
377 |  |
             private int indexValue;
378 |  |
379 |  |
             /** Stores a flag if the actual property has an index.*/
380 |  |
             private boolean hasIndex;
381 |  |
382 |  |
             /** Stores a flag if the actual property is an attribute.*/
383 |  |
             private boolean attribute;
384 |  |
385 |  |
             /**
386 |  |
              * Helper method for determining the next indices.
387 |  |
              */
388 |  |
             private void findNextIndices()
389 |  |
             {
390 |  |
                 startIndex = endIndex;
391 |  |
                 // skip empty names
392 |  |
                 while (startIndex < keyBuffer.length()
393 |  |
                 && keyBuffer.charAt(startIndex) == PROPERTY_DELIMITER)
394 |  |
                 {
395 |  |
                     startIndex++;
396 |  |
                 }
397 |  |
398 |  |
                 // Key ends with a delimiter?
399 |  |
                 if (startIndex >= keyBuffer.length())
400 |  |
                 {
401 |  |
                     endIndex = keyBuffer.length();
402 |  |
                     startIndex = endIndex - 1;
403 |  |
                 }
404 |  |
                 else
405 |  |
                 {
406 |  |
                     String s = keyBuffer.toString();    // for compatibility
407 |  |
                     endIndex = s.indexOf(PROPERTY_DELIMITER, startIndex);
408 |  |
                     if (endIndex < 0)
409 |  |
                     {
410 |  |
                         endIndex = s.indexOf(ATTRIBUTE_START, startIndex);
411 |  |
                         if (endIndex < 0 || endIndex == startIndex)
412 |  |
                         {
413 |  |
                             endIndex = keyBuffer.length();
414 |  |
                         }
415 |  |
                     }
416 |  |
                 }
417 |  |
             }
418 |  |
419 |  |
             /**
420 |  |
              * Returns the next key part of this configuration key. This is a short
421 |  |
              * form of <code>nextKey(false)</code>.
422 |  |
              *
423 |  |
              * @return the next key part
424 |  |
              */
425 |  |
             public String nextKey()
426 |  |
             {
427 |  |
                 return nextKey(false);
428 |  |
             }
429 |  |
430 |  |
             /**
431 |  |
              * Returns the next key part of this configuration key. The boolean
432 |  |
              * parameter indicates wheter a decorated key should be returned. This
433 |  |
              * affects only attribute keys: if the parameter is <b>false</b>, the
434 |  |
              * attribute markers are stripped from the key; if it is <b>true</b>,
435 |  |
              * they remain.
436 |  |
              *
437 |  |
              * @param decorated a flag if the decorated key is to be returned
438 |  |
              * @return the next key part
439 |  |
              */
440 |  |
             public String nextKey(boolean decorated)
441 |  |
             {
442 |  |
                 if (!hasNext())
443 |  |
                 {
444 |  |
                     throw new NoSuchElementException("No more key parts!");
445 |  |
                 }
446 |  |
447 |  |
                 hasIndex = false;
448 |  |
                 indexValue = -1;
449 |  |
                 findNextIndices();
450 |  |
                 String key = keyBuffer.substring(startIndex, endIndex);
451 |  |
452 |  |
                 attribute = checkAttribute(key);
453 |  |
                 if (!attribute)
454 |  |
                 {
455 |  |
                     hasIndex = checkIndex(key);
456 |  |
                     if (!hasIndex)
457 |  |
                     {
458 |  |
                         current = key;
459 |  |
                     }
460 |  |
                 }
461 |  |
462 |  |
                 return currentKey(decorated);
463 |  |
             }
464 |  |
465 |  |
             /**
466 |  |
              * Helper method for checking if the passed key is an attribute.
467 |  |
              * If this is the case, the internal fields will be set.
468 |  |
              *
469 |  |
              * @param key the key to be checked
470 |  |
              * @return a flag if the key is an attribute
471 |  |
              */
472 |  |
             private boolean checkAttribute(String key)
473 |  |
             {
474 |  |
                 if (isAttributeKey(key))
475 |  |
                 {
476 |  |
                     current = removeAttributeMarkers(key);
477 |  |
                     return true;
478 |  |
                 }
479 |  |
                 else
480 |  |
                 {
481 |  |
                     return false;
482 |  |
                 }
483 |  |
             }
484 |  |
485 |  |
             /**
486 |  |
              * Helper method for checking if the passed key contains an index.
487 |  |
              * If this is the case, internal fields will be set.
488 |  |
              *
489 |  |
              * @param key the key to be checked
490 |  |
              * @return a flag if an index is defined
491 |  |
              */
492 |  |
             private boolean checkIndex(String key)
493 |  |
             {
494 |  |
                 boolean result = false;
495 |  |
496 |  |
                 int idx = key.indexOf(INDEX_START);
497 |  |
                 if (idx > 0)
498 |  |
                 {
499 |  |
                     int endidx = key.indexOf(INDEX_END, idx);
500 |  |
501 |  |
                     if (endidx > idx + 1)
502 |  |
                     {
503 |  |
                         indexValue = Integer.parseInt(key.substring(idx + 1, endidx));
504 |  |
                         current = key.substring(0, idx);
505 |  |
                         result = true;
506 |  |
                     }
507 |  |
                 }
508 |  |
509 |  |
                 return result;
510 |  |
             }
511 |  |
512 |  |
             /**
513 |  |
              * Checks if there is a next element.
514 |  |
              *
515 |  |
              * @return a flag if there is a next element
516 |  |
              */
517 |  |
             public boolean hasNext()
518 |  |
             {
519 |  |
                 return endIndex < keyBuffer.length();
520 |  |
             }
521 |  |
522 |  |
             /**
523 |  |
              * Returns the next object in the iteration.
524 |  |
              *
525 |  |
              * @return the next object
526 |  |
              */
527 |  |
             public Object next()
528 |  |
             {
529 |  |
                 return nextKey();
530 |  |
             }
531 |  |
532 |  |
             /**
533 |  |
              * Removes the current object in the iteration. This method is not
534 |  |
              * supported by this iterator type, so an exception is thrown.
535 |  |
              */
536 |  |
             public void remove()
537 |  |
             {
538 |  |
                 throw new UnsupportedOperationException("Remove not supported!");
539 |  |
             }
540 |  |
541 |  |
             /**
542 |  |
              * Returns the current key of the iteration (without skipping to the
543 |  |
              * next element). This is the same key the previous <code>next()</code>
544 |  |
              * call had returned. (Short form of <code>currentKey(false)</code>.
545 |  |
              *
546 |  |
              * @return the current key
547 |  |
              */
548 |  |
             public String currentKey()
549 |  |
             {
550 |  |
                 return currentKey(false);
551 |  |
             }
552 |  |
553 |  |
             /**
554 |  |
              * Returns the current key of the iteration (without skipping to the
555 |  |
              * next element). The boolean parameter indicates wheter a decorated
556 |  |
              * key should be returned. This affects only attribute keys: if the
557 |  |
              * parameter is <b>false</b>, the attribute markers are stripped from
558 |  |
              * the key; if it is <b>true</b>, they remain.
559 |  |
              *
560 |  |
              * @param decorated a flag if the decorated key is to be returned
561 |  |
              * @return the current key
562 |  |
              */
563 |  |
             public String currentKey(boolean decorated)
564 |  |
             {
565 |  |
                 return (decorated && isAttribute()) ? constructAttributeKey(current) : current;
566 |  |
             }
567 |  |
568 |  |
             /**
569 |  |
              * Returns a flag if the current key is an attribute. This method can
570 |  |
              * be called after <code>next()</code>.
571 |  |
              *
572 |  |
              * @return a flag if the current key is an attribute
573 |  |
              */
574 |  |
             public boolean isAttribute()
575 |  |
             {
576 |  |
                 return attribute;
577 |  |
             }
578 |  |
579 |  |
             /**
580 |  |
              * Returns the index value of the current key. If the current key does
581 |  |
              * not have an index, return value is -1. This method can be called
582 |  |
              * after <code>next()</code>.
583 |  |
              *
584 |  |
              * @return the index value of the current key
585 |  |
              */
586 |  |
             public int getIndex()
587 |  |
             {
588 |  |
                 return indexValue;
589 |  |
             }
590 |  |
591 |  |
             /**
592 |  |
              * Returns a flag if the current key has an associated index.
593 |  |
              * This method can be called after <code>next()</code>.
594 |  |
              *
595 |  |
              * @return a flag if the current key has an index
596 |  |
              */
597 |  |
             public boolean hasIndex()
598 |  |
             {
599 |  |
                 return hasIndex;
600 |  |
             }
601 |  |
602 |  |
             /**
603 |  |
              * Creates a clone of this object.
604 |  |
              *
605 |  |
              * @return a clone of this object
606 |  |
              */
607 |  |
             protected Object clone()
608 |  |
             {
609 |  |
                 try
610 |  |
                 {
611 |  |
                     return super.clone();
612 |  |
                 }
613 |  |
                 catch (CloneNotSupportedException cex)
614 |  |
                 {
615 |  |
                     // should not happen
616 |  |
                     return null;
617 |  |
                 }
618 |  |
             }
619 |  |
         }
620 |  |
     }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.