Coverage Report - jaggregate.AbstractBag
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractBag
98%
65/66
100%
8/8
0
AbstractBag$1
100%
4/4
100%
1/1
0
AbstractBag$2
100%
3/3
N/A
0
 
 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 jaggregate;
 7  
 
 8  
 import java.io.IOException;
 9  
 import java.io.ObjectInputStream;
 10  
 import java.io.ObjectOutputStream;
 11  
 
 12  
 import jaggregate.internal.Casting;
 13  
 import static jaggregate.internal.ArgumentChecks.*;
 14  
 
 15  
 /**
 16  
  * Represents an unordered, variable-sized collection whose <dfn>elements</dfn> can be
 17  
  * added or removed, but cannot be individually accessed by external <dfn>keys</dfn>.
 18  
  * <p/>
 19  
  * A bag is similar to a {@linkplain Set set} but can contain duplicate elements.
 20  
  * Elements are duplicates if they are <dfn>equivalent</dfn>.
 21  
  *
 22  
  * @param <E> a restriction on the types of elements that may be contained in the bag
 23  
  *
 24  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 25  
  * @version $Id: AbstractBag.java,v 1.9 2008/10/03 19:01:23 pholser Exp $
 26  
  */
 27  0
 public abstract class AbstractBag<E> extends AbstractExtensibleCollection<E> {
 28  
     private transient AbstractDictionary<E, Integer> storage;
 29  
     private transient int numberOfElements;
 30  
 
 31  
     /**
 32  
      * Creates an empty bag.
 33  
      */
 34  636
     protected AbstractBag() {
 35  636
         initialize( 0 );
 36  636
     }
 37  
 
 38  
     /**
 39  
      * Creates a bag containing the elements in the given collection.
 40  
      *
 41  
      * @param elements elements to add to the new bag
 42  
      * @throws NullPointerException if {@code elements} is {@code null}
 43  
      */
 44  
     protected AbstractBag( Collection<? extends E> elements ) {
 45  30
         this();
 46  30
         addAll( elements );
 47  28
     }
 48  
 
 49  
     /**
 50  
      * Creates a bag containing the elements in the given array.
 51  
      *
 52  
      * @param elements elements to add to the new bag
 53  
      * @throws NullPointerException if {@code elements} is {@code null}
 54  
      */
 55  
     protected AbstractBag( E... elements ) {
 56  335
         this();
 57  335
         addAll( elements );
 58  333
     }
 59  
 
 60  
     /**
 61  
      * Creates a bag containing the elements in the given iterable object.
 62  
      *
 63  
      * @param elements elements to add to the new bag
 64  
      * @throws NullPointerException if {@code elements} is {@code null}
 65  
      */
 66  
     protected AbstractBag( Iterable<? extends E> elements ) {
 67  4
         this();
 68  4
         addAll( elements );
 69  2
     }
 70  
 
 71  
     /**
 72  
      * {@inheritDoc}
 73  
      *
 74  
      * @return a bag of results
 75  
      */
 76  
     @Override
 77  
     public <R> AbstractBag<R> collect(
 78  
         UnaryFunctor<? super E, ? extends R> transformer ) {
 79  
 
 80  6
         return (AbstractBag<R>) super.collect( transformer );
 81  
     }
 82  
 
 83  
     /**
 84  
      * {@inheritDoc}
 85  
      */
 86  
     public void forEachDo( final UnaryFunctor<? super E, ?> operation ) {
 87  323
         ensureNotNull( operation, OPERATION );
 88  
 
 89  321
         storage.keysAndValuesDo( new BinaryFunctor<E, Integer, Void>() {
 90  1489
             public Void evaluate( E first, Integer second ) {
 91  2606
                 for ( int i = 0; i < second; ++i )
 92  1441
                     operation.evaluate( first );
 93  
 
 94  1165
                 return null;
 95  
             }
 96  
         } );
 97  318
     }
 98  
 
 99  
     /**
 100  
      * {@inheritDoc}
 101  
      */
 102  
     @Override
 103  
     public boolean includes( E target ) {
 104  63
         return storage.includesKey( target );
 105  
     }
 106  
 
 107  
     /**
 108  
      * {@inheritDoc}
 109  
      */
 110  
     @Override
 111  
     public int occurrencesOf( E target ) {
 112  49
         Integer occurrences = storage.at( target );
 113  49
         return occurrences == null ? 0 : occurrences;
 114  
     }
 115  
 
 116  
     /**
 117  
      * {@inheritDoc}
 118  
      */
 119  
     @Override
 120  
     public void rehash() {
 121  2
         storage.rehash();
 122  2
     }
 123  
 
 124  
     /**
 125  
      * {@inheritDoc}
 126  
      *
 127  
      * @return a bag of rejects
 128  
      */
 129  
     @Override
 130  
     public AbstractBag<E> reject( UnaryCondition<? super E> discriminator ) {
 131  5
         return (AbstractBag<E>) super.reject( discriminator );
 132  
     }
 133  
 
 134  
     /**
 135  
      * {@inheritDoc}
 136  
      *
 137  
      * @return a bag of selections
 138  
      */
 139  
     @Override
 140  
     public AbstractBag<E> select( UnaryCondition<? super E> discriminator ) {
 141  15
         return (AbstractBag<E>) super.select( discriminator );
 142  
     }
 143  
 
 144  
     /**
 145  
      * {@inheritDoc}
 146  
      */
 147  
     public int size() {
 148  53
         return numberOfElements;
 149  
     }
 150  
 
 151  
     /**
 152  
      * {@inheritDoc}
 153  
      */
 154  
     public void add( E newElement ) {
 155  2350
         add( newElement, 1 );
 156  2350
     }
 157  
 
 158  
     /**
 159  
      * Adds the given element to this bag a given number of times.
 160  
      *
 161  
      * @see #add(Object)
 162  
      * @param newElement the element to add
 163  
      * @param occurrences number of times to add {@code newElement}
 164  
      * @throws IllegalArgumentException if {@code occurrences} is negative
 165  
      */
 166  
     public void add( E newElement, int occurrences ) {
 167  2369
         if ( occurrences < 0 )
 168  2
             throw new IllegalArgumentException(
 169  
                 "illegal non-negative oldOccurrences: " + occurrences );
 170  
 
 171  2367
         Integer oldOccurrences = storage.at( newElement );
 172  2367
         storage.putAt( newElement,
 173  
             oldOccurrences == null ? occurrences : oldOccurrences + occurrences );
 174  
 
 175  2367
         numberOfElements += occurrences;
 176  2367
     }
 177  
 
 178  
     /**
 179  
      * {@inheritDoc}
 180  
      */
 181  
     public boolean remove( E oldElement ) {
 182  566
         Integer occurrences = storage.at( oldElement );
 183  
 
 184  566
         if ( occurrences != null ) {
 185  457
             if ( occurrences == 1 )
 186  325
                 storage.removeKey( oldElement );
 187  
             else
 188  132
                 storage.putAt( oldElement, occurrences - 1 );
 189  
 
 190  457
             --numberOfElements;
 191  457
             return true;
 192  
         }
 193  
 
 194  109
         return false;
 195  
     }
 196  
 
 197  
     /**
 198  
      * {@inheritDoc}
 199  
      * <p/>
 200  
      * An object is considered equivalent to this bag if the object is assignable to
 201  
      * this bag's class and has equivalent contents, as defined by this bag's notion of
 202  
      * equivalence.
 203  
      */
 204  
     @Override
 205  
     public boolean equals( Object that ) {
 206  725
         if ( this == that )
 207  168
             return true;
 208  
 
 209  557
         if ( !getImplementationClass().isInstance( that ) )
 210  8
             return false;
 211  
 
 212  549
         AbstractBag<?> other = (AbstractBag<?>) that;
 213  549
         return storage.equals( other.storage );
 214  
     }
 215  
 
 216  
     /**
 217  
      * Subclasses implement this method to answer the root of its true runtime class,
 218  
      * for purposes of determining {@linkplain #equals(Object) equivalence}.
 219  
      *
 220  
      * @return this bag's implementation class
 221  
      */
 222  
     protected abstract Class<? extends AbstractBag> getImplementationClass();
 223  
 
 224  
     /**
 225  
      * {@inheritDoc}
 226  
      */
 227  
     @Override
 228  
     public int hashCode() {
 229  180
         return storage.hashCode();
 230  
     }
 231  
 
 232  
     /**
 233  
      * {@inheritDoc}
 234  
      */
 235  
     @Override
 236  
     public String toString() {
 237  10
         final StringBuilder buffer = new StringBuilder( "[" );
 238  
 
 239  10
         if ( !isEmpty() ) {
 240  8
             forEachDo( new UnaryFunctor<E, Void>() {
 241  20
                 public Void evaluate( E argument ) {
 242  12
                     buffer.append( asString( argument ) ).append( ',' );
 243  12
                     return null;
 244  
                 }
 245  
             } );
 246  
 
 247  8
             buffer.deleteCharAt( buffer.length() - 1 );
 248  
         }
 249  
 
 250  10
         buffer.append( ']' );
 251  
 
 252  10
         return buffer.toString();
 253  
     }
 254  
 
 255  
     /**
 256  
      * Answers a new, empty storage for the bag.
 257  
      *
 258  
      * @return a new, empty dictionary
 259  
      */
 260  
     protected abstract AbstractDictionary<E, Integer> newEmptyStorage();
 261  
 
 262  
     /**
 263  
      * Tells how the given element will show up in this bag's string representation.
 264  
      *
 265  
      * @param anElement an element of this collection
 266  
      * @return a string representation of {@code anElement} to be used in this bag's
 267  
      * string representation
 268  
      */
 269  
     protected abstract String asString( E anElement );
 270  
 
 271  
     /**
 272  
      * Serialization helper method.
 273  
      *
 274  
      * @param output stream to write object state to
 275  
      * @throws IOException if an I/O problem occurs during serialization
 276  
      */
 277  
     protected final void serializeTo( ObjectOutputStream output ) throws IOException {
 278  2
         output.defaultWriteObject();
 279  2
         output.writeInt( numberOfElements );
 280  2
         output.writeObject( storage );
 281  2
     }
 282  
 
 283  
     /**
 284  
      * De-serialization helper method.
 285  
      *
 286  
      * @param input stream to read object state from
 287  
      * @throws IOException if an I/O problem occurs during de-serialization
 288  
      * @throws ClassNotFoundException if a class that is necessary for de-serialization
 289  
      * cannot be found
 290  
      */
 291  
     protected final void deserializeFrom( ObjectInputStream input )
 292  
         throws IOException, ClassNotFoundException {
 293  
 
 294  2
         input.defaultReadObject();
 295  
 
 296  2
         initialize( input.readInt(),
 297  
             Casting.<AbstractDictionary<E, Integer>>cast( input.readObject() ) );
 298  2
     }
 299  
 
 300  
     private void initialize( int size ) {
 301  636
         initialize( size, newEmptyStorage() );
 302  636
     }
 303  
 
 304  
     private void initialize( int size, AbstractDictionary<E, Integer> backingStore ) {
 305  638
         numberOfElements = size;
 306  638
         storage = backingStore;
 307  638
     }
 308  
 }