KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > configuration > ConfigurationKey


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 JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.NoSuchElementException JavaDoc;
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 155408 2005-02-26 12:56:39Z dirkv $
39  */

40 public class ConfigurationKey implements Serializable JavaDoc
41 {
42     /** Constant for a property delimiter.*/
43     public static final char PROPERTY_DELIMITER = '.';
44
45     /** Constant for an attribute start marker.*/
46     private static final String JavaDoc ATTRIBUTE_START = "[@";
47
48     /** Constant for an attribute end marker.*/
49     private static final String JavaDoc ATTRIBUTE_END = "]";
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 JavaDoc keyBuffer;
62
63     /**
64      * Creates a new, empty instance of <code>ConfigurationKey</code>.
65      */

66     public ConfigurationKey()
67     {
68         keyBuffer = new StringBuffer JavaDoc(INITIAL_SIZE);
69     }
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 JavaDoc key)
78     {
79         keyBuffer = new StringBuffer JavaDoc(key);
80         removeTrailingDelimiter();
81     }
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 JavaDoc property)
91     {
92         if (keyBuffer.length() > 0 && !hasDelimiter() && !isAttributeKey(property))
93         {
94             keyBuffer.append(PROPERTY_DELIMITER);
95         }
96
97         keyBuffer.append(property);
98         removeTrailingDelimiter();
99         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         keyBuffer.append(INDEX_START).append(index);
111         keyBuffer.append(INDEX_END);
112         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 JavaDoc attr)
122     {
123         keyBuffer.append(constructAttributeKey(attr));
124         return this;
125     }
126     
127     /**
128      * Checks if this key is an attribute key.
129      *
130      * @return a flag if this key is an attribute key
131      */

132     public boolean isAttributeKey()
133     {
134         return isAttributeKey(keyBuffer.toString());
135     }
136
137     /**
138      * Checks if the passed in key is an attribute key. Such attribute keys
139      * start and end with certain marker strings. In some cases they must be
140      * treated slightly different.
141      *
142      * @param key the key (part) to be checked
143      * @return a flag if this key is an attribute key
144      */

145     public static boolean isAttributeKey(String JavaDoc key)
146     {
147         return key != null
148         && key.startsWith(ATTRIBUTE_START)
149         && key.endsWith(ATTRIBUTE_END);
150     }
151
152     /**
153      * Decorates the given key so that it represents an attribute. Adds
154      * special start and end markers.
155      *
156      * @param key the key to be decorated
157      * @return the decorated attribute key
158      */

159     public static String JavaDoc constructAttributeKey(String JavaDoc key)
160     {
161         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
162         buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END);
163         return buf.toString();
164     }
165
166     /**
167      * Extracts the name of the attribute from the given attribute key.
168      * This method removes the attribute markers - if any - from the
169      * specified key.
170      *
171      * @param key the attribute key
172      * @return the name of the corresponding attribute
173      */

174     public static String JavaDoc attributeName(String JavaDoc key)
175     {
176         return isAttributeKey(key) ? removeAttributeMarkers(key) : key;
177     }
178
179     /**
180      * Helper method for removing attribute markers from a key.
181      *
182      * @param key the key
183      * @return the key with removed attribute markers
184      */

185     static String JavaDoc removeAttributeMarkers(String JavaDoc key)
186     {
187         return key.substring(ATTRIBUTE_START.length(), key.length() - ATTRIBUTE_END.length());
188     }
189
190     /**
191      * Helper method that checks if the actual buffer ends with a property
192      * delimiter.
193      *
194      * @return a flag if there is a trailing delimiter
195      */

196     private boolean hasDelimiter()
197     {
198         return keyBuffer.length() > 0
199         && keyBuffer.charAt(keyBuffer.length() - 1) == PROPERTY_DELIMITER;
200     }
201
202     /**
203      * Removes a trailing delimiter if there is any.
204      */

205     private void removeTrailingDelimiter()
206     {
207         while (hasDelimiter())
208         {
209             keyBuffer.deleteCharAt(keyBuffer.length() - 1);
210         }
211     }
212
213     /**
214      * Returns a string representation of this object. This is the
215      * configuration key as a plain string.
216      *
217      * @return a string for this object
218      */

219     public String JavaDoc toString()
220     {
221         return keyBuffer.toString();
222     }
223
224     /**
225      * Returns an iterator for iterating over the single components of
226      * this configuration key.
227      *
228      * @return an iterator for this key
229      */

230     public KeyIterator iterator()
231     {
232         return new KeyIterator();
233     }
234
235     /**
236      * Returns the actual length of this configuration key.
237      *
238      * @return the length of this key
239      */

240     public int length()
241     {
242         return keyBuffer.length();
243     }
244
245     /**
246      * Sets the new length of this configuration key. With this method it is
247      * possible to truncate the key, e.g. to return to a state prior calling
248      * some <code>append()</code> methods. The semantic is the same as
249      * the <code>setLength()</code> method of <code>StringBuffer</code>.
250      *
251      * @param len the new length of the key
252      */

253     public void setLength(int len)
254     {
255         keyBuffer.setLength(len);
256     }
257
258     /**
259      * Checks if two <code>ConfigurationKey</code> objects are equal. The
260      * method can be called with strings or other objects, too.
261      *
262      * @param c the object to compare
263      * @return a flag if both objects are equal
264      */

265     public boolean equals(Object JavaDoc c)
266     {
267         if (c == null)
268         {
269             return false;
270         }
271
272         return keyBuffer.toString().equals(c.toString());
273     }
274
275     /**
276      * Returns the hash code for this object.
277      *
278      * @return the hash code
279      */

280     public int hashCode()
281     {
282         return keyBuffer.hashCode();
283     }
284
285     /**
286      * Returns a configuration key object that is initialized with the part
287      * of the key that is common to this key and the passed in key.
288      *
289      * @param other the other key
290      * @return a key object with the common key part
291      */

292     public ConfigurationKey commonKey(ConfigurationKey other)
293     {
294         if (other == null)
295         {
296             throw new IllegalArgumentException JavaDoc("Other key must no be null!");
297         }
298
299         ConfigurationKey result = new ConfigurationKey();
300         KeyIterator it1 = iterator();
301         KeyIterator it2 = other.iterator();
302
303         while (it1.hasNext() && it2.hasNext() && partsEqual(it1, it2))
304         {
305             if (it1.isAttribute())
306             {
307                 result.appendAttribute(it1.currentKey());
308             }
309             else
310             {
311                 result.append(it1.currentKey());
312                 if (it1.hasIndex)
313                 {
314                     result.appendIndex(it1.getIndex());
315                 }
316             }
317         }
318
319         return result;
320     }
321
322     /**
323      * Returns the &quot;difference key&quot; to a given key. This value
324      * is the part of the passed in key that differs from this key. There is
325      * the following relation:
326      * <code>other = key.commonKey(other) + key.differenceKey(other)</code>
327      * for an arbitrary configuration key <code>key</code>.
328      *
329      * @param other the key for which the difference is to be calculated
330      * @return the difference key
331      */

332     public ConfigurationKey differenceKey(ConfigurationKey other)
333     {
334         ConfigurationKey common = commonKey(other);
335         ConfigurationKey result = new ConfigurationKey();
336
337         if (common.length() < other.length())
338         {
339             String JavaDoc k = other.toString().substring(common.length());
340             // skip trailing delimiters
341
int i = 0;
342             while (i < k.length() && k.charAt(i) == PROPERTY_DELIMITER)
343             {
344                 i++;
345             }
346
347             if (i < k.length())
348             {
349                 result.append(k.substring(i));
350             }
351         }
352
353         return result;
354     }
355
356     /**
357      * Helper method for comparing two key parts.
358      *
359      * @param it1 the iterator with the first part
360      * @param it2 the iterator with the second part
361      * @return a flag if both parts are equal
362      */

363     private static boolean partsEqual(KeyIterator it1, KeyIterator it2)
364     {
365         return it1.nextKey().equals(it2.nextKey())
366         && it1.getIndex() == it2.getIndex()
367         && it1.isAttribute() == it2.isAttribute();
368     }
369
370     /**
371      * A specialized iterator class for tokenizing a configuration key.
372      * This class implements the normal iterator interface. In addition it
373      * provides some specific methods for configuration keys.
374      */

375     public class KeyIterator implements Iterator JavaDoc, Cloneable JavaDoc
376     {
377         /** Stores the current key name.*/
378         private String JavaDoc current;
379
380         /** Stores the start index of the actual token.*/
381         private int startIndex;
382
383         /** Stores the end index of the actual token.*/
384         private int endIndex;
385
386         /** Stores the index of the actual property if there is one.*/
387         private int indexValue;
388
389         /** Stores a flag if the actual property has an index.*/
390         private boolean hasIndex;
391
392         /** Stores a flag if the actual property is an attribute.*/
393         private boolean attribute;
394
395         /**
396          * Helper method for determining the next indices.
397          */

398         private void findNextIndices()
399         {
400             startIndex = endIndex;
401             // skip empty names
402
while (startIndex < keyBuffer.length()
403             && keyBuffer.charAt(startIndex) == PROPERTY_DELIMITER)
404             {
405                 startIndex++;
406             }
407
408             // Key ends with a delimiter?
409
if (startIndex >= keyBuffer.length())
410             {
411                 endIndex = keyBuffer.length();
412                 startIndex = endIndex - 1;
413             }
414             else
415             {
416                 String JavaDoc s = keyBuffer.toString(); // for compatibility
417
endIndex = s.indexOf(PROPERTY_DELIMITER, startIndex);
418                 if (endIndex < 0)
419                 {
420                     endIndex = s.indexOf(ATTRIBUTE_START, startIndex);
421                     if (endIndex < 0 || endIndex == startIndex)
422                     {
423                         endIndex = keyBuffer.length();
424                     }
425                 }
426             }
427         }
428
429         /**
430          * Returns the next key part of this configuration key. This is a short
431          * form of <code>nextKey(false)</code>.
432          *
433          * @return the next key part
434          */

435         public String JavaDoc nextKey()
436         {
437             return nextKey(false);
438         }
439
440         /**
441          * Returns the next key part of this configuration key. The boolean
442          * parameter indicates wheter a decorated key should be returned. This
443          * affects only attribute keys: if the parameter is <b>false</b>, the
444          * attribute markers are stripped from the key; if it is <b>true</b>,
445          * they remain.
446          *
447          * @param decorated a flag if the decorated key is to be returned
448          * @return the next key part
449          */

450         public String JavaDoc nextKey(boolean decorated)
451         {
452             if (!hasNext())
453             {
454                 throw new NoSuchElementException JavaDoc("No more key parts!");
455             }
456
457             hasIndex = false;
458             indexValue = -1;
459             findNextIndices();
460             String JavaDoc key = keyBuffer.substring(startIndex, endIndex);
461
462             attribute = checkAttribute(key);
463             if (!attribute)
464             {
465                 hasIndex = checkIndex(key);
466                 if (!hasIndex)
467                 {
468                     current = key;
469                 }
470             }
471
472             return currentKey(decorated);
473         }
474
475         /**
476          * Helper method for checking if the passed key is an attribute.
477          * If this is the case, the internal fields will be set.
478          *
479          * @param key the key to be checked
480          * @return a flag if the key is an attribute
481          */

482         private boolean checkAttribute(String JavaDoc key)
483         {
484             if (isAttributeKey(key))
485             {
486                 current = removeAttributeMarkers(key);
487                 return true;
488             }
489             else
490             {
491                 return false;
492             }
493         }
494
495         /**
496          * Helper method for checking if the passed key contains an index.
497          * If this is the case, internal fields will be set.
498          *
499          * @param key the key to be checked
500          * @return a flag if an index is defined
501          */

502         private boolean checkIndex(String JavaDoc key)
503         {
504             boolean result = false;
505
506             int idx = key.indexOf(INDEX_START);
507             if (idx > 0)
508             {
509                 int endidx = key.indexOf(INDEX_END, idx);
510
511                 if (endidx > idx + 1)
512                 {
513                     indexValue = Integer.parseInt(key.substring(idx + 1, endidx));
514                     current = key.substring(0, idx);
515                     result = true;
516                 }
517             }
518
519             return result;
520         }
521
522         /**
523          * Checks if there is a next element.
524          *
525          * @return a flag if there is a next element
526          */

527         public boolean hasNext()
528         {
529             return endIndex < keyBuffer.length();
530         }
531
532         /**
533          * Returns the next object in the iteration.
534          *
535          * @return the next object
536          */

537         public Object JavaDoc next()
538         {
539             return nextKey();
540         }
541
542         /**
543          * Removes the current object in the iteration. This method is not
544          * supported by this iterator type, so an exception is thrown.
545          */

546         public void remove()
547         {
548             throw new UnsupportedOperationException JavaDoc("Remove not supported!");
549         }
550
551         /**
552          * Returns the current key of the iteration (without skipping to the
553          * next element). This is the same key the previous <code>next()</code>
554          * call had returned. (Short form of <code>currentKey(false)</code>.
555          *
556          * @return the current key
557          */

558         public String JavaDoc currentKey()
559         {
560             return currentKey(false);
561         }
562
563         /**
564          * Returns the current key of the iteration (without skipping to the
565          * next element). The boolean parameter indicates wheter a decorated
566          * key should be returned. This affects only attribute keys: if the
567          * parameter is <b>false</b>, the attribute markers are stripped from
568          * the key; if it is <b>true</b>, they remain.
569          *
570          * @param decorated a flag if the decorated key is to be returned
571          * @return the current key
572          */

573         public String JavaDoc currentKey(boolean decorated)
574         {
575             return (decorated && isAttribute()) ? constructAttributeKey(current) : current;
576         }
577
578         /**
579          * Returns a flag if the current key is an attribute. This method can
580          * be called after <code>next()</code>.
581          *
582          * @return a flag if the current key is an attribute
583          */

584         public boolean isAttribute()
585         {
586             return attribute;
587         }
588
589         /**
590          * Returns the index value of the current key. If the current key does
591          * not have an index, return value is -1. This method can be called
592          * after <code>next()</code>.
593          *
594          * @return the index value of the current key
595          */

596         public int getIndex()
597         {
598             return indexValue;
599         }
600
601         /**
602          * Returns a flag if the current key has an associated index.
603          * This method can be called after <code>next()</code>.
604          *
605          * @return a flag if the current key has an index
606          */

607         public boolean hasIndex()
608         {
609             return hasIndex;
610         }
611
612         /**
613          * Creates a clone of this object.
614          *
615          * @return a clone of this object
616          */

617         protected Object JavaDoc clone()
618         {
619             try
620             {
621                 return super.clone();
622             }
623             catch (CloneNotSupportedException JavaDoc cex)
624             {
625                 // should not happen
626
return null;
627             }
628         }
629     }
630 }
631
Popular Tags