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.