Coverage report

  %line %branch
org.apache.commons.configuration.ConfigurationKey
97% 
99% 

 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 155408 2005-02-26 12:56:39Z dirkv $
 39  
  */
 40  739562
 public class ConfigurationKey implements Serializable
 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 ATTRIBUTE_START = "[@";
 47  
 
 48  
     /** Constant for an attribute end marker.*/
 49  
     private static final String 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 keyBuffer;
 62  
 
 63  
     /**
 64  
      * Creates a new, empty instance of <code>ConfigurationKey</code>.
 65  
      */
 66  
     public ConfigurationKey()
 67  14766
     {
 68  14766
         keyBuffer = new StringBuffer(INITIAL_SIZE);
 69  14766
     }
 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  14431
     {
 79  14431
         keyBuffer = new StringBuffer(key);
 80  14431
         removeTrailingDelimiter();
 81  14431
     }
 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  18331
         if (keyBuffer.length() > 0 && !hasDelimiter() && !isAttributeKey(property))
 93  
         {
 94  6003
             keyBuffer.append(PROPERTY_DELIMITER);
 95  
         }
 96  
 
 97  18331
         keyBuffer.append(property);
 98  18331
         removeTrailingDelimiter();
 99  18331
         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  460
         keyBuffer.append(INDEX_START).append(index);
 111  460
         keyBuffer.append(INDEX_END);
 112  460
         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  115
         keyBuffer.append(constructAttributeKey(attr));
 124  115
         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  989
         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 key)
 146  
     {
 147  97126
         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 constructAttributeKey(String key)
 160  
     {
 161  25692
         StringBuffer buf = new StringBuffer();
 162  25692
         buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END);
 163  25692
         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 attributeName(String key)
 175  
     {
 176  92
         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 removeAttributeMarkers(String key)
 186  
     {
 187  5958
         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  39202
         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  32808
         while (hasDelimiter())
 208  
         {
 209  46
             keyBuffer.deleteCharAt(keyBuffer.length() - 1);
 210  
         }
 211  32762
     }
 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 toString()
 220  
     {
 221  13018
         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  33774
         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  20355
         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  3611
         keyBuffer.setLength(len);
 256  3611
     }
 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 c)
 266  
     {
 267  253
         if (c == null)
 268  
         {
 269  23
             return false;
 270  
         }
 271  
 
 272  230
         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  0
         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  8418
         if (other == null)
 295  
         {
 296  0
             throw new IllegalArgumentException("Other key must no be null!");
 297  
         }
 298  
 
 299  8418
         ConfigurationKey result = new ConfigurationKey();
 300  8418
         KeyIterator it1 = iterator();
 301  8418
         KeyIterator it2 = other.iterator();
 302  
 
 303  14559
         while (it1.hasNext() && it2.hasNext() && partsEqual(it1, it2))
 304  
         {
 305  6141
             if (it1.isAttribute())
 306  
             {
 307  46
                 result.appendAttribute(it1.currentKey());
 308  
             }
 309  
             else
 310  
             {
 311  6095
                 result.append(it1.currentKey());
 312  6095
                 if (it1.hasIndex)
 313  
                 {
 314  161
                     result.appendIndex(it1.getIndex());
 315  
                 }
 316  
             }
 317  
         }
 318  
 
 319  8418
         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  5635
         ConfigurationKey common = commonKey(other);
 335  5635
         ConfigurationKey result = new ConfigurationKey();
 336  
 
 337  5635
         if (common.length() < other.length())
 338  
         {
 339  5359
             String k = other.toString().substring(common.length());
 340  
             // skip trailing delimiters
 341  5359
             int i = 0;
 342  8165
             while (i < k.length() && k.charAt(i) == PROPERTY_DELIMITER)
 343  
             {
 344  2806
                 i++;
 345  
             }
 346  
 
 347  5359
             if (i < k.length())
 348  
             {
 349  5359
                 result.append(k.substring(i));
 350  
             }
 351  
         }
 352  
 
 353  5635
         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  13547
         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, Cloneable
 376  
     {
 377  
         /** Stores the current key name.*/
 378  
         private String 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 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 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 nextKey(boolean decorated)
 451  
         {
 452  
             if (!hasNext())
 453  
             {
 454  
                 throw new NoSuchElementException("No more key parts!");
 455  
             }
 456  
 
 457  
             hasIndex = false;
 458  
             indexValue = -1;
 459  
             findNextIndices();
 460  
             String 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 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 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 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("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 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 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 clone()
 618  
         {
 619  
             try
 620  
             {
 621  
                 return super.clone();
 622  
             }
 623  
             catch (CloneNotSupportedException cex)
 624  
             {
 625  
                 // should not happen
 626  
                 return null;
 627  
             }
 628  
         }
 629  
     }
 630  
 }

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