KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > myvietnam > mvncore > configuration > ConfigurationKey


1 package net.myvietnam.mvncore.configuration;
2
3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if
22  * any, must include the following acknowlegement:
23  * "This product includes software developed by the
24  * Apache Software Foundation (http://www.apache.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
29  * Foundation" must not be used to endorse or promote products derived
30  * from this software without prior written permission. For written
31  * permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache"
34  * nor may "Apache" appear in their names without prior written
35  * permission of the Apache Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation. For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  */

56
57 import java.io.Serializable JavaDoc;
58 import java.util.Iterator JavaDoc;
59 import java.util.NoSuchElementException JavaDoc;
60
61 /**
62  * <p>A simple class that supports creation of and iteration on complex
63  * configuration keys.</p>
64  * <p>For key creation the class works similar to a StringBuffer: There are
65  * several <code>appendXXXX()</code> methods with which single parts
66  * of a key can be constructed. All these methods return a reference to the
67  * actual object so they can be written in a chain. When using this methods
68  * the exact syntax for keys need not be known.</p>
69  * <p>This class also defines a specialized iterator for configuration keys.
70  * With such an iterator a key can be tokenized into its single parts. For
71  * each part it can be checked whether it has an associated index.</p>
72  *
73  * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
74  * @version $Id: ConfigurationKey.java,v 1.2 2004/10/10 16:51:13 minhnn Exp $
75  */

76 public class ConfigurationKey implements Serializable JavaDoc
77 {
78     /** Constant for an attribute start marker.*/
79     private static final String JavaDoc ATTRIBUTE_START = "[@";
80
81     /** Constant for an attribute end marker.*/
82     private static final String JavaDoc ATTRIBUTE_END = "]";
83
84     /** Constant for a property delimiter.*/
85     private static final char PROPERTY_DELIMITER = '.';
86
87     /** Constant for an index start marker.*/
88     private static final char INDEX_START = '(';
89
90     /** Constant for an index end marker.*/
91     private static final char INDEX_END = ')';
92
93     /** Constant for the initial StringBuffer size.*/
94     private static final int INITIAL_SIZE = 32;
95
96     /** Holds a buffer with the so far created key.*/
97     private StringBuffer JavaDoc keyBuffer;
98
99     /**
100      * Creates a new, empty instance of <code>ConfigurationKey</code>.
101      */

102     public ConfigurationKey()
103     {
104         keyBuffer = new StringBuffer JavaDoc(INITIAL_SIZE);
105     }
106
107     /**
108      * Creates a new instance of <code>ConfigurationKey</code> and
109      * initializes it with the given key.
110      * @param key the key as a string
111      */

112     public ConfigurationKey(String JavaDoc key)
113     {
114         keyBuffer = new StringBuffer JavaDoc(key);
115         removeTrailingDelimiter();
116     }
117
118     /**
119      * Appends the name of a property to this key. If necessary, a
120      * property delimiter will be added.
121      * @param property the name of the property to be added
122      * @return a reference to this object
123      */

124     public ConfigurationKey append(String JavaDoc property)
125     {
126         if(keyBuffer.length() > 0 && !hasDelimiter()
127         && !isAttributeKey(property))
128         {
129             keyBuffer.append(PROPERTY_DELIMITER);
130         } /* if */
131
132         keyBuffer.append(property);
133         removeTrailingDelimiter();
134         return this;
135     }
136
137     /**
138      * Appends an index to this configuration key.
139      * @param index the index to be appended
140      * @return a reference to this object
141      */

142     public ConfigurationKey appendIndex(int index)
143     {
144         keyBuffer.append(INDEX_START).append(index);
145         keyBuffer.append(INDEX_END);
146         return this;
147     }
148
149     /**
150      * Appends an attribute to this configuration key.
151      * @param attr the name of the attribute to be appended
152      * @return a reference to this object
153      */

154     public ConfigurationKey appendAttribute(String JavaDoc attr)
155     {
156         keyBuffer.append(constructAttributeKey(attr));
157         return this;
158     }
159
160     /**
161      * Checks if the passed in key is an attribute key. Such attribute keys
162      * start and end with certain marker strings. In some cases they must be
163      * treated slightly different.
164      * @param key the key (part) to be checked
165      * @return a flag if this key is an attribute key
166      */

167     public static boolean isAttributeKey(String JavaDoc key)
168     {
169         return key != null
170         && key.startsWith(ATTRIBUTE_START)
171         && key.endsWith(ATTRIBUTE_END);
172     }
173
174     /**
175      * Decorates the given key so that it represents an attribute. Adds
176      * special start and end markers.
177      * @param key the key to be decorated
178      * @return the decorated attribute key
179      */

180     public static String JavaDoc constructAttributeKey(String JavaDoc key)
181     {
182         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
183         buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END);
184         return buf.toString();
185     }
186
187     /**
188      * Extracts the name of the attribute from the given attribute key.
189      * This method removes the attribute markers - if any - from the
190      * specified key.
191      * @param key the attribute key
192      * @return the name of the corresponding attribute
193      */

194     public static String JavaDoc attributeName(String JavaDoc key)
195     {
196         return (isAttributeKey(key)) ?
197         removeAttributeMarkers(key) : key;
198     }
199
200     /**
201      * Helper method for removing attribute markers from a key.
202      * @param key the key
203      * @return the key with removed attribute markers
204      */

205     private static String JavaDoc removeAttributeMarkers(String JavaDoc key)
206     {
207         return key.substring(ATTRIBUTE_START.length(),
208         key.length() - ATTRIBUTE_END.length());
209     }
210
211     /**
212      * Helper method that checks if the actual buffer ends with a property
213      * delimiter.
214      * @return a flag if there is a trailing delimiter
215      */

216     private boolean hasDelimiter()
217     {
218         return keyBuffer.length() > 0
219         && keyBuffer.charAt(keyBuffer.length()-1) == PROPERTY_DELIMITER;
220     }
221
222     /**
223      * Removes a trailing delimiter if there is any.
224      */

225     private void removeTrailingDelimiter()
226     {
227         while(hasDelimiter())
228         {
229             keyBuffer.deleteCharAt(keyBuffer.length()-1);
230         } /* while */
231     }
232
233     /**
234      * Returns a string representation of this object. This is the
235      * configuration key as a plain string.
236      * @return a string for this object
237      */

238     public String JavaDoc toString()
239     {
240         return keyBuffer.toString();
241     }
242
243     /**
244      * Returns an iterator for iterating over the single components of
245      * this configuration key.
246      * @return an iterator for this key
247      */

248     public KeyIterator iterator()
249     {
250         return new KeyIterator();
251     }
252
253     /**
254      * Returns the actual length of this configuration key.
255      * @return the length of this key
256      */

257     public int length()
258     {
259         return keyBuffer.length();
260     }
261
262     /**
263      * Sets the new length of this configuration key. With this method it is
264      * possible to truncate the key, e.g. to return to a state prior calling
265      * some <code>append()</code> methods. The semantic is the same as
266      * the <code>setLength()</code> method of <code>StringBuffer</code>.
267      * @param len the new length of the key
268      */

269     public void setLength(int len)
270     {
271         keyBuffer.setLength(len);
272     }
273
274     /**
275      * Checks if two <code>ConfigurationKey</code> objects are equal. The
276      * method can be called with strings or other objects, too.
277      * @param c the object to compare
278      * @return a flag if both objects are equal
279      */

280     public boolean equals(Object JavaDoc c)
281     {
282         if(c == null)
283         {
284             return false;
285         } /* if */
286
287         return keyBuffer.toString().equals(c.toString());
288     }
289
290     /**
291      * Returns the hash code for this object.
292      * @return the hash code
293      */

294     public int hashCode()
295     {
296         return keyBuffer.hashCode();
297     }
298
299     /**
300      * Returns a configuration key object that is initialized with the part
301      * of the key that is common to this key and the passed in key.
302      * @param other the other key
303      * @return a key object with the common key part
304      */

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

345     public ConfigurationKey differenceKey(ConfigurationKey other)
346     {
347         ConfigurationKey common = commonKey(other);
348         ConfigurationKey result = new ConfigurationKey();
349
350         if(common.length() < other.length())
351         {
352             String JavaDoc k = other.toString().substring(common.length());
353             // skip trailing delimiters
354
int i = 0;
355             while(i < k.length() && k.charAt(i) == PROPERTY_DELIMITER)
356             {
357                 i++;
358             } /* while */
359
360             if(i < k.length())
361             {
362                 result.append(k.substring(i));
363             } /* if */
364         } /* if */
365
366         return result;
367     }
368
369     /**
370      * Helper method for comparing two key parts.
371      * @param it1 the iterator with the first part
372      * @param it2 the iterator with the second part
373      * @return a flag if both parts are equal
374      */

375     private static boolean partsEqual(KeyIterator it1, KeyIterator it2)
376     {
377         return it1.nextKey().equals(it2.nextKey())
378         && it1.getIndex() == it2.getIndex()
379         && it1.isAttribute() == it2.isAttribute();
380     }
381
382     /**
383      * A specialized iterator class for tokenizing a configuration key.
384      * This class implements the normal iterator interface. In addition it
385      * provides some specific methods for configuration keys.
386      *
387      * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
388      */

389     public class KeyIterator implements Iterator JavaDoc, Cloneable JavaDoc
390     {
391         /** Stores the current key name.*/
392         private String JavaDoc current;
393
394         /** Stores the start index of the actual token.*/
395         private int startIndex;
396
397         /** Stores the end index of the actual token.*/
398         private int endIndex;
399
400         /** Stores the index of the actual property if there is one.*/
401         private int indexValue;
402
403         /** Stores a flag if the actual property has an index.*/
404         private boolean hasIndex;
405
406         /** Stores a flag if the actual property is an attribute.*/
407         private boolean attribute;
408
409         /**
410          * Helper method for determining the next indices.
411          */

412         private void findNextIndices()
413         {
414             startIndex = endIndex;
415             // skip empty names
416
while (startIndex < keyBuffer.length()
417             && keyBuffer.charAt(startIndex) == PROPERTY_DELIMITER)
418             {
419                 startIndex++;
420             }
421
422             // Key ends with a delimiter?
423
if (startIndex >= keyBuffer.length())
424             {
425                 endIndex = keyBuffer.length();
426                 startIndex = endIndex - 1;
427             }
428             else
429             {
430                 String JavaDoc s = keyBuffer.toString(); // for compatibility
431
endIndex = s.indexOf(PROPERTY_DELIMITER, startIndex);
432                 if (endIndex < 0)
433                 {
434                     endIndex = s.indexOf(ATTRIBUTE_START, startIndex);
435                     if (endIndex < 0 || endIndex == startIndex)
436                     {
437                         endIndex = keyBuffer.length();
438                     }
439                 }
440             }
441         }
442
443         /**
444          * Returns the next key part of this configuration key. This is a short
445          * form of <code>nextKey(false)</code>.
446          * @return the next key part
447          */

448         public String JavaDoc nextKey()
449         {
450             return nextKey(false);
451         }
452
453         /**
454          * Returns the next key part of this configuration key. The boolean
455          * parameter indicates wheter a decorated key should be returned. This
456          * affects only attribute keys: if the parameter is <b>false</b>, the
457          * attribute markers are stripped from the key; if it is <b>true</b>,
458          * they remain.
459          * @param decorated a flag if the decorated key is to be returned
460          * @return the next key part
461          */

462         public String JavaDoc nextKey(boolean decorated)
463         {
464             if(!hasNext())
465             {
466                 throw new NoSuchElementException JavaDoc("No more key parts!");
467             } /* if */
468
469             hasIndex = false;
470             indexValue = -1;
471             findNextIndices();
472             String JavaDoc key = keyBuffer.substring(startIndex, endIndex).toString();
473
474             attribute = checkAttribute(key);
475             if(!attribute)
476             {
477                 hasIndex = checkIndex(key);
478                 if(!hasIndex)
479                 {
480                     current = key;
481                 } /* if */
482             } /* if */
483
484             return currentKey(decorated);
485         }
486
487         /**
488          * Helper method for checking if the passed key is an attribute.
489          * If this is the case, the internal fields will be set.
490          * @param key the key to be checked
491          * @return a flag if the key is an attribute
492          */

493         private boolean checkAttribute(String JavaDoc key)
494         {
495             if(isAttributeKey(key))
496             {
497                 current = removeAttributeMarkers(key);
498                 return true;
499             } /* if */
500             else
501             {
502                 return false;
503             } /* else */
504         }
505
506         /**
507          * Helper method for checking if the passed key contains an index.
508          * If this is the case, internal fields will be set.
509          * @param key the key to be checked
510          * @return a flag if an index is defined
511          */

512         private boolean checkIndex(String JavaDoc key)
513         {
514             boolean result = false;
515
516             int idx = key.indexOf(INDEX_START);
517             if(idx > 0)
518             {
519                 int endidx = key.indexOf(INDEX_END, idx);
520
521                 if(endidx > idx + 1)
522                 {
523                     indexValue = Integer.parseInt(key.substring(idx+1, endidx));
524                     current = key.substring(0, idx);
525                     result = true;
526                 } /* if */
527             } /* if */
528
529             return result;
530         }
531
532         /**
533          * Checks if there is a next element.
534          * @return a flag if there is a next element
535          */

536         public boolean hasNext()
537         {
538             return endIndex < keyBuffer.length();
539         }
540
541         /**
542          * Returns the next object in the iteration.
543          * @return the next object
544          */

545         public Object JavaDoc next()
546         {
547             return nextKey();
548         }
549
550         /**
551          * Removes the current object in the iteration. This method is not
552          * supported by this iterator type, so an exception is thrown.
553          */

554         public void remove()
555         {
556             throw new UnsupportedOperationException JavaDoc("Remove not supported!");
557         }
558
559         /**
560          * Returns the current key of the iteration (without skipping to the
561          * next element). This is the same key the previous <code>next()</code>
562          * call had returned. (Short form of <code>currentKey(false)</code>.
563          * @return the current key
564          */

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

579         public String JavaDoc currentKey(boolean decorated)
580         {
581             return (decorated && isAttribute()) ?
582             constructAttributeKey(current) : current;
583         }
584
585         /**
586          * Returns a flag if the current key is an attribute. This method can
587          * be called after <code>next()</code>.
588          * @return a flag if the current key is an attribute
589          */

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

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

611         public boolean hasIndex()
612         {
613             return hasIndex;
614         }
615
616         /**
617          * Creates a clone of this object.
618          * @return a clone of this object
619          */

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