Coverage Report - joptsimple.AbbreviationMap
 
Classes in this File Line Coverage Branch Coverage Complexity
AbbreviationMap
100%
75/75
100%
48/48
3.167
 
 1  
 /*
 2  
  Copyright 2004-2008 Paul R. Holser, Jr.  All rights reserved.
 3  
  Licensed under the Academic Free License version 3.0
 4  
  */
 5  
 
 6  
 package joptsimple;
 7  
 
 8  
 import java.util.Iterator;
 9  
 import java.util.List;
 10  
 import java.util.Map;
 11  
 import java.util.TreeMap;
 12  
 
 13  
 /**
 14  
  * <p>A map whose keys are strings; when a key/value pair is added to the map,
 15  
  * the longest unique abbreviations of that key are added as well, and associated with
 16  
  * the value.  Thus:</p>
 17  
  *
 18  
  * <pre>
 19  
  *   <code>
 20  
  *   abbreviations.put( "good", "bye" );
 21  
  *   </code>
 22  
  * </pre>
 23  
  *
 24  
  * <p>would make it such that you could retrieve the value <code>"bye"</code> from the
 25  
  * map with the keys <code>"good"</code>, <code>"goo"</code>, <code>"go"</code>, and
 26  
  * <code>"g"</code>.  A subsequent invocation of:</p>
 27  
  * <pre>
 28  
  *   <code>
 29  
  *   abbreviations.put( "go", "fish" );
 30  
  *   </code>
 31  
  * </pre>
 32  
  *
 33  
  * <p>would make it such that you could retrieve the value <code>"bye"</code> using the
 34  
  * keys <code>"good"</code> and <code>"goo"</code>, and the value <code>"fish"</code>
 35  
  * with the key <code>"go"</code>.  The key <code>"g"</code> would yield
 36  
  * <code>null</code>, since it would no longer be a unique abbreviation.</p>
 37  
  *
 38  
  * <p>The data structure is much like a "trie".</p>
 39  
  *
 40  
  * @since 1.0
 41  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 42  
  * @version $Id: AbbreviationMap.java,v 1.4 2008/05/23 15:02:43 pholser Exp $
 43  
  * @see <a href="http://www.perldoc.com/perl5.8.0/lib/Text/Abbrev.html"> Perl's
 44  
  * Text::Abbrev module</a>
 45  
  */
 46  2870
 class AbbreviationMap {
 47  
     private String key;
 48  
     private Object value;
 49  2870
     private final Map children = new TreeMap();
 50  
     private int keysBeyond;
 51  
 
 52  
     /**
 53  
      * <p>Tells whether the given key is in the map, or whether the given key is a unique
 54  
      * abbreviation of a key that is in the map.</p>
 55  
      *
 56  
      * @param aKey key to look up
 57  
      * @return {@code true} if {@code key} is present in the map
 58  
      * @throws NullPointerException if {@code key} is {@code null}
 59  
      */
 60  
     boolean contains( String aKey ) {
 61  636
         return get( aKey ) != null;
 62  
     }
 63  
 
 64  
     /**
 65  
      * <p>Answers the value associated with the given key.  The key can be a unique
 66  
      * abbreviation of a key that is in the map. </p>
 67  
      *
 68  
      * @param aKey key to look up
 69  
      * @return the value associated with <var>aKey</var>; or <code>null</code> if there
 70  
      * is no such value or <var>aKey</var> is not a unique abbreviation of a key in the
 71  
      * map
 72  
      * @throws NullPointerException if <var>aKey</var> is <code>null</code>
 73  
      */
 74  
     Object get( String aKey ) {
 75  1250
         char[] chars = charsOf( aKey );
 76  
 
 77  1248
         AbbreviationMap child = this;
 78  4100
         for ( int i = 0; i < chars.length; ++i ) {
 79  2940
             child = (AbbreviationMap) child.children.get( new Character( chars[ i ] ) );
 80  2940
             if ( child == null )
 81  88
                 return null;
 82  
         }
 83  
 
 84  1160
         return child.value;
 85  
     }
 86  
 
 87  
     /**
 88  
      * <p>Associates a given value with a given key.  If there was a previous
 89  
      * association, the old value is replaced with the new one.</p>
 90  
      *
 91  
      * @param aKey key to create in the map
 92  
      * @param newValue value to associate with the key
 93  
      * @throws NullPointerException if <var>aKey</var> or <var>newValue</var> is
 94  
      * <code>null</code>
 95  
      * @throws IllegalArgumentException if <var>aKey</var> is a zero-length string
 96  
      */
 97  
     void put( String aKey, Object newValue ) {
 98  1198
         if ( newValue == null )
 99  2
             throw new NullPointerException();
 100  1196
         if ( aKey.length() == 0 )
 101  2
             throw new IllegalArgumentException();
 102  
 
 103  1192
         char[] chars = charsOf( aKey );
 104  1192
         add( chars, newValue, 0, chars.length );
 105  1192
     }
 106  
 
 107  
     void putAll( List keys, Object newValue ) {
 108  886
         for ( Iterator iter = keys.iterator(); iter.hasNext(); )
 109  1062
             put( (String) iter.next(), newValue );
 110  886
     }
 111  
 
 112  
     private boolean add( char[] chars, Object newValue, int offset, int length ) {
 113  5136
         if ( offset == length ) {
 114  1192
             value = newValue;
 115  1192
             boolean wasAlreadyAKey = key != null;
 116  1192
             key = new String( chars );
 117  1192
             return !wasAlreadyAKey;
 118  
         }
 119  
 
 120  3944
         Character nextChar = new Character( chars[ offset ] );
 121  3944
         AbbreviationMap child = (AbbreviationMap) children.get( nextChar );
 122  3944
         if ( child == null ) {
 123  2466
             child = new AbbreviationMap();
 124  2466
             children.put( nextChar, child );
 125  
         }
 126  
 
 127  3944
         boolean newKeyAdded = child.add( chars, newValue, offset + 1, length );
 128  
 
 129  3944
         if ( newKeyAdded )
 130  2782
             ++keysBeyond;
 131  
 
 132  3944
         if ( key == null )
 133  3814
             value = keysBeyond > 1 ? null : newValue;
 134  
 
 135  3944
         return newKeyAdded;
 136  
     }
 137  
 
 138  
     /**
 139  
      * <p>If the map contains the given key, dissociates the key from its value.</p>
 140  
      *
 141  
      * @param aKey key to remove
 142  
      * @throws NullPointerException if <var>aKey</var> is <code>null</code>
 143  
      * @throws IllegalArgumentException if <var>aKey</var> is a zero-length string
 144  
      */
 145  
     void remove( String aKey ) {
 146  24
         if ( aKey.length() == 0 )
 147  2
             throw new IllegalArgumentException();
 148  
 
 149  20
         char[] keyChars = charsOf( aKey );
 150  20
         remove( keyChars, 0, keyChars.length );
 151  20
     }
 152  
 
 153  
     private boolean remove( char[] aKey, int offset, int length ) {
 154  78
         if ( offset == length )
 155  16
             return removeAtEndOfKey();
 156  
 
 157  62
         Character nextChar = new Character( aKey[ offset ] );
 158  62
         AbbreviationMap child = (AbbreviationMap) children.get( nextChar );
 159  62
         if ( child == null || !child.remove( aKey, offset + 1, length ) )
 160  14
             return false;
 161  
 
 162  48
         --keysBeyond;
 163  48
         if ( child.keysBeyond == 0 )
 164  12
             children.remove( nextChar );
 165  48
         if ( keysBeyond == 1 && key == null )
 166  14
             setValueToThatOfOnlyChild();
 167  
 
 168  48
         return true;
 169  
     }
 170  
 
 171  
     private void setValueToThatOfOnlyChild() {
 172  18
         Map.Entry entry = (Map.Entry) children.entrySet().iterator().next();
 173  18
         AbbreviationMap onlyChild = (AbbreviationMap) entry.getValue();
 174  18
         value = onlyChild.value;
 175  18
     }
 176  
 
 177  
     private boolean removeAtEndOfKey() {
 178  16
         if ( key == null )
 179  4
             return false;
 180  
 
 181  12
         key = null;
 182  12
         if ( keysBeyond == 1 )
 183  4
             setValueToThatOfOnlyChild();
 184  
         else
 185  8
             value = null;
 186  
 
 187  12
         return true;
 188  
     }
 189  
 
 190  
     Map toJavaUtilMap() {
 191  66
         Map mappings = new TreeMap();
 192  66
         addToMappings( mappings );
 193  66
         return mappings;
 194  
     }
 195  
 
 196  
     private void addToMappings( Map mappings ) {
 197  792
         if ( key != null )
 198  194
             mappings.put( key, value );
 199  
 
 200  792
         for ( Iterator iter = children.values().iterator(); iter.hasNext(); )
 201  726
             ( (AbbreviationMap) iter.next() ).addToMappings( mappings );
 202  792
     }
 203  
 
 204  
     private static char[] charsOf( String aKey ) {
 205  2462
         char[] chars = new char[ aKey.length() ];
 206  2460
         aKey.getChars( 0, aKey.length(), chars, 0 );
 207  2460
         return chars;
 208  
     }
 209  
 }