Coverage Report - jaggregate.internal.ReadOnlySequences
 
Classes in this File Line Coverage Branch Coverage Complexity
ReadOnlySequences
100%
129/129
100%
20/20
0
ReadOnlySequences$1
100%
2/2
N/A
0
ReadOnlySequences$2
100%
5/5
100%
1/1
0
ReadOnlySequences$3
100%
3/3
N/A
0
ReadOnlySequences$4
100%
4/4
100%
1/1
0
ReadOnlySequences$5
100%
3/3
N/A
0
ReadOnlySequences$6
100%
5/5
100%
1/1
0
ReadOnlySequences$7
100%
5/5
100%
1/1
0
ReadOnlySequences$8
100%
5/5
100%
1/1
0
ReadOnlySequences$9
100%
3/3
N/A
0
ReadOnlySequences$SequencesEqualAtEachIndex
100%
10/10
100%
1/1
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.internal;
 7  
 
 8  
 import jaggregate.BinaryFunctor;
 9  
 import jaggregate.OrderedCollection;
 10  
 import jaggregate.ReadOnlySequence;
 11  
 import jaggregate.UnaryCondition;
 12  
 import jaggregate.UnaryFunctor;
 13  
 import static jaggregate.Objects.*;
 14  
 import static jaggregate.OrderedCollection.*;
 15  
 import static jaggregate.internal.ArgumentChecks.*;
 16  
 
 17  
 /**
 18  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 19  
  * @version $Id: ReadOnlySequences.java,v 1.8 2008/10/03 19:01:23 pholser Exp $
 20  
  */
 21  
 public class ReadOnlySequences {
 22  
     /**
 23  
      * Discourages instantiation.
 24  
      *
 25  
      * @throws UnsupportedOperationException always
 26  
      */
 27  1
     protected ReadOnlySequences() {
 28  1
         throw new UnsupportedOperationException();
 29  
     }
 30  
 
 31  
     /**
 32  
      * Two {@link jaggregate.ReadOnlySequence}s are considered equivalent if they have
 33  
      * the same {@linkplain jaggregate.Collection#size() size} and if the corresponding
 34  
      * elements at each index are {@linkplain Object#equals(Object) equivalent}.
 35  
      *
 36  
      * @see Object#equals(Object)
 37  
      * @param first first alleged sequence
 38  
      * @param second second alleged sequence
 39  
      * @return whether {@code first} and {@code second} are equivalent
 40  
      */
 41  
     public static boolean areSequencesEqual( Object first, Object second ) {
 42  3043
         if ( first == second )
 43  255
             return true;
 44  
 
 45  2788
         if ( !( first instanceof ReadOnlySequence<?> )
 46  
             || !( second instanceof ReadOnlySequence<?> ) )
 47  4
             return false;
 48  
 
 49  2784
         final ReadOnlySequence<?> firstSequence = (ReadOnlySequence<?>) first;
 50  2784
         final ReadOnlySequence<?> secondSequence = (ReadOnlySequence<?>) second;
 51  
 
 52  2784
         boolean sequencesMatch = false;
 53  2784
         if ( firstSequence.size() == secondSequence.size() ) {
 54  
             try {
 55  2406
                 firstSequence.doWithIndex(
 56  
                     new SequencesEqualAtEachIndex( firstSequence, secondSequence ) );
 57  
 
 58  2404
                 sequencesMatch = true;
 59  
             }
 60  2
             catch ( NonLocalReturnException ignored ) {
 61  2
                 sequencesMatch = false;
 62  2404
             }
 63  
         }
 64  
 
 65  2784
         return sequencesMatch;
 66  
     }
 67  
 
 68  
     /**
 69  
      * Computes a hash value for a given sequence.
 70  
      *
 71  
      * @see Object#hashCode()
 72  
      * @param sequence the sequence to hash
 73  
      * @return the computed hash value for {@code sequence}
 74  
      */
 75  
     public static int hashCode( ReadOnlySequence<?> sequence ) {
 76  271
         if ( sequence == null )
 77  1
             return 0;
 78  
 
 79  270
         return sequence.inject( 0, new BinaryFunctor<Integer, Object, Integer>() {
 80  1170
             public Integer evaluate( Integer first, Object second ) {
 81  900
                 return first + nullSafeHashCode( second );
 82  
             }
 83  
         } );
 84  
     }
 85  
 
 86  
     /**
 87  
      * Computes a string representation for a given sequence.
 88  
      *
 89  
      * @see Object#toString()
 90  
      * @param sequence the sequence to stringify
 91  
      * @return the stringification of {@code sequence}
 92  
      */
 93  
     public static String toString( final ReadOnlySequence<?> sequence ) {
 94  14
         if ( sequence == null )
 95  1
             return "null";
 96  
 
 97  13
         final StringBuilder buffer = new StringBuilder( "[" );
 98  
 
 99  13
         sequence.doWithIndex( new BinaryFunctor<Integer, Object, Void>() {
 100  29
             public Void evaluate( Integer first, Object second ) {
 101  16
                 buffer.append( second );
 102  
 
 103  16
                 if ( first < sequence.size() - 1 )
 104  6
                     buffer.append( ',' );
 105  
 
 106  16
                 return null;
 107  
             }
 108  
         } );
 109  
 
 110  13
         buffer.append( ']' );
 111  
 
 112  13
         return buffer.toString();
 113  
     }
 114  
 
 115  
     /**
 116  
      * @see {@link jaggregate.ReadOnlySequence#concat(jaggregate.ReadOnlySequence)}
 117  
      */
 118  
     public static <E> OrderedCollection<E> concat( ReadOnlySequence<? extends E> first,
 119  
         ReadOnlySequence<? extends E> second ) {
 120  
 
 121  55
         ensureNotNull( first, SEQUENCE );
 122  55
         ensureNotNull( second, SEQUENCE );
 123  
 
 124  46
         OrderedCollection<E> concatenation = orderedCollectionFrom( first );
 125  46
         concatenation.addAll( second );
 126  
 
 127  46
         return concatenation;
 128  
     }
 129  
 
 130  
     /**
 131  
      * @see {@link jaggregate.ReadOnlySequence#copyRange(int, int)}
 132  
      */
 133  
     public static <E> OrderedCollection<E> copyRange( ReadOnlySequence<E> sequence,
 134  
         int start, int stop ) {
 135  
 
 136  971
         ensureNotNull( sequence, SEQUENCE );
 137  
 
 138  971
         if ( start > stop )
 139  232
             return emptyOrderedCollection();
 140  
 
 141  739
         if ( stop > start ) {
 142  442
             checkIndexIsValidOn( sequence, start, STARTING_INDEX );
 143  384
             checkIndexIsValidOn( sequence, stop, ENDING_INDEX );
 144  
         }
 145  
 
 146  623
         final OrderedCollection<E> subsequence = emptyOrderedCollection();
 147  
 
 148  623
         forEachInRangeDo( sequence, start, stop, new UnaryFunctor<E, Void>() {
 149  1861
             public Void evaluate( E nextElement ) {
 150  1238
                 subsequence.add( nextElement );
 151  1238
                 return null;
 152  
             }
 153  
         } );
 154  
 
 155  598
         return subsequence;
 156  
     }
 157  
 
 158  
     /**
 159  
      * @see {@link jaggregate.ReadOnlySequence#copyWith(Object)}
 160  
      */
 161  
     public static <E> OrderedCollection<E> copyWith( ReadOnlySequence<E> sequence,
 162  
         E newElement ) {
 163  
 
 164  79
         ensureNotNull( sequence, SEQUENCE );
 165  
 
 166  79
         OrderedCollection<E> copy = orderedCollectionFrom( sequence );
 167  79
         copy.add( newElement );
 168  
 
 169  79
         return copy;
 170  
     }
 171  
 
 172  
     /**
 173  
      * @see {@link jaggregate.ReadOnlySequence#copyWithout(Object)}
 174  
      */
 175  
     public static <E> OrderedCollection<E> copyWithout( ReadOnlySequence<E> sequence,
 176  
         final E oldElement ) {
 177  
 
 178  357
         ensureNotNull( sequence, SEQUENCE );
 179  
 
 180  357
         final OrderedCollection<E> copy = emptyOrderedCollection();
 181  
 
 182  357
         sequence.forEachDo( new UnaryFunctor<E, Void>() {
 183  2073
             public Void evaluate( E nextElement ) {
 184  1716
                 if ( !areEqual( nextElement, oldElement ) )
 185  1462
                     copy.add( nextElement );
 186  1716
                 return null;
 187  
             }
 188  
         } );
 189  
 
 190  357
         return copy;
 191  
     }
 192  
 
 193  
     /**
 194  
      * @see {@link jaggregate.ReadOnlySequence#doWithIndex(jaggregate.BinaryFunctor)}
 195  
      */
 196  
     public static <E> void doWithIndex( ReadOnlySequence<E> sequence,
 197  
         BinaryFunctor<? super Integer, ? super E, ?> operation ) {
 198  
 
 199  3329
         ensureNotNull( sequence, SEQUENCE );
 200  3329
         ensureNotNull( operation, OPERATION );
 201  
 
 202  11531
         for ( int i = 0; i < sequence.size(); ++i )
 203  8742
             operation.evaluate( i, sequence.at( i ) );
 204  2789
     }
 205  
 
 206  
     /**
 207  
      * @see {@link jaggregate.ReadOnlySequence#doWithOthers(jaggregate.ReadOnlySequence,
 208  
      * jaggregate.BinaryFunctor)}
 209  
      */
 210  
     public static <E, T> void doWithOthers( ReadOnlySequence<E> first,
 211  
         final ReadOnlySequence<? extends T> second,
 212  
         final BinaryFunctor<? super E, ? super T, ?> operation ) {
 213  
 
 214  137
         ensureNotNull( first, SEQUENCE );
 215  137
         ensureNotNull( second, SEQUENCE );
 216  128
         ensureNotNull( operation, OPERATION );
 217  
 
 218  119
         if ( first.size() != second.size() ) {
 219  9
             throw new IllegalArgumentException( EXPECTED_SEQUENCE_SIZE + first.size()
 220  
                 + BUT_WAS + second.size() );
 221  
         }
 222  
 
 223  110
         first.doWithIndex(
 224  
             new BinaryFunctor<Integer, E, Void>() {
 225  474
                 public Void evaluate( Integer index, E nextElement ) {
 226  364
                     operation.evaluate( nextElement, second.at( index ) );
 227  364
                     return null;
 228  
                 }
 229  
             }
 230  
         );
 231  110
     }
 232  
 
 233  
     /**
 234  
      * @see {@link jaggregate.ReadOnlySequence#forEachInRangeDo(int, int,
 235  
      * jaggregate.UnaryFunctor)}
 236  
      */
 237  
     public static <E> void forEachInRangeDo( ReadOnlySequence<E> sequence, int start,
 238  
         int stop, UnaryFunctor<? super E, ?> operation ) {
 239  
 
 240  1646
         ensureNotNull( sequence, SEQUENCE );
 241  1646
         ensureNotNull( operation, OPERATION );
 242  1637
         ensureIndexIsGreaterThan( start, -1, STARTING_INDEX );
 243  
 
 244  1576
         if ( stop != start )
 245  995
             ensureIndexIsLessThan( stop, sequence.size(), ENDING_INDEX );
 246  
 
 247  4141
         for ( int i = start; i <= stop; ++i )
 248  2651
             operation.evaluate( sequence.at( i ) );
 249  1490
     }
 250  
 
 251  
     /**
 252  
      * @see {@link jaggregate.ReadOnlySequence#forEachInRangeDoWithIndex(int, int,
 253  
      * jaggregate.BinaryFunctor)}
 254  
      */
 255  
     public static <E> void forEachInRangeDoWithIndex( ReadOnlySequence<E> sequence,
 256  
         int start, int stop,
 257  
         BinaryFunctor<? super Integer, ? super E, ?> operation ) {
 258  
 
 259  998
         ensureNotNull( sequence, SEQUENCE );
 260  998
         ensureNotNull( operation, OPERATION );
 261  989
         ensureIndexIsGreaterThan( start, -1, STARTING_INDEX );
 262  928
         ensureIndexIsLessThan( stop, sequence.size(), ENDING_INDEX );
 263  
 
 264  2201
         for ( int i = start; i <= stop; ++i )
 265  1334
             operation.evaluate( i, sequence.at( i ) );
 266  867
     }
 267  
 
 268  
     /**
 269  
      * @see {@link
 270  
      * jaggregate.ReadOnlySequence#forEachInReverseDo(jaggregate.UnaryFunctor)}
 271  
      */
 272  
     public static <E> void forEachInReverseDo( ReadOnlySequence<E> sequence,
 273  
         UnaryFunctor<? super E, ?> operation ) {
 274  
 
 275  290
         ensureNotNull( sequence, SEQUENCE );
 276  290
         ensureNotNull( operation, OPERATION );
 277  
 
 278  747
         for ( int i = sequence.size() - 1; i >= 0; --i )
 279  609
             operation.evaluate( sequence.at( i ) );
 280  138
     }
 281  
 
 282  
     /**
 283  
      * @see {@link jaggregate.ReadOnlySequence#findFirst(jaggregate.UnaryCondition)}
 284  
      */
 285  
     public static <E> int findFirst( ReadOnlySequence<E> sequence,
 286  
         final UnaryCondition<? super E> discriminator ) {
 287  
 
 288  119
         ensureNotNull( sequence, SEQUENCE );
 289  119
         ensureNotNull( discriminator, DISCRIMINATOR );
 290  
 
 291  110
         final int[] indexOfMatch = { -1 };
 292  
 
 293  
         try {
 294  110
             sequence.doWithIndex(
 295  
                 new BinaryFunctor<Integer, E, Void>() {
 296  216
                     public Void evaluate( Integer first, E second ) {
 297  106
                         if ( discriminator.matches( second ) ) {
 298  85
                             indexOfMatch[ 0 ] = first;
 299  85
                             throw new NonLocalReturnException();
 300  
                         }
 301  
 
 302  21
                         return null;
 303  
                     }
 304  
                 }
 305  
             );
 306  
         }
 307  85
         catch ( NonLocalReturnException ignored ) {
 308  85
             return indexOfMatch[ 0 ];
 309  25
         }
 310  
 
 311  25
         return -1;
 312  
     }
 313  
 
 314  
     /**
 315  
      * @see {@link jaggregate.ReadOnlySequence#findLast(jaggregate.UnaryCondition)}
 316  
      */
 317  
     public static <E> int findLast( ReadOnlySequence<E> sequence,
 318  
         final UnaryCondition<? super E> discriminator ) {
 319  
 
 320  177
         ensureNotNull( sequence, SEQUENCE );
 321  177
         ensureNotNull( discriminator, DISCRIMINATOR );
 322  
 
 323  168
         final int[] indexOfMatch = { sequence.size() - 1 };
 324  
 
 325  
         try {
 326  168
             sequence.forEachInReverseDo( new UnaryFunctor<E, Void>() {
 327  401
                 public Void evaluate( E argument ) {
 328  233
                     if ( discriminator.matches( argument ) )
 329  143
                         throw new NonLocalReturnException();
 330  
 
 331  90
                     --indexOfMatch[ 0 ];
 332  90
                     return null;
 333  
                 }
 334  
             } );
 335  
         }
 336  143
         catch ( NonLocalReturnException ignored ) {
 337  143
             return indexOfMatch[ 0 ];
 338  25
         }
 339  
 
 340  25
         return -1;
 341  
     }
 342  
 
 343  
     /**
 344  
      * @see {@link jaggregate.ReadOnlySequence#indexOf(Object)}
 345  
      */
 346  
     public static <E> int indexOf( ReadOnlySequence<E> sequence, final E target ) {
 347  566
         ensureNotNull( sequence, SEQUENCE );
 348  
 
 349  566
         final int[] indexOfMatch = { -1 };
 350  
 
 351  
         try {
 352  566
             sequence.doWithIndex(
 353  
                 new BinaryFunctor<Integer, E, Void>() {
 354  2109
                     public Void evaluate( Integer first, E second ) {
 355  1543
                         if ( areEqual( target, second ) ) {
 356  444
                             indexOfMatch[ 0 ] = first;
 357  444
                             throw new NonLocalReturnException();
 358  
                         }
 359  
 
 360  1099
                         return null;
 361  
                     }
 362  
                 } );
 363  
 
 364  122
             return -1;
 365  
         }
 366  444
         catch ( NonLocalReturnException ignored ) {
 367  444
             return indexOfMatch[ 0 ];
 368  
         }
 369  
     }
 370  
 
 371  
     /**
 372  
      * @see {@link jaggregate.ReadOnlySequence#indexOf(jaggregate.ReadOnlySequence, int)}
 373  
      */
 374  
     public static <E> int indexOf( ReadOnlySequence<E> sequence,
 375  
         ReadOnlySequence<? extends E> target, int start ) {
 376  
 
 377  94
         ensureNotNull( sequence, SEQUENCE );
 378  94
         ensureNotNull( target, "target sequence" );
 379  85
         checkIndexIsValidOn( sequence, start, STARTING_INDEX );
 380  
 
 381  56
         if ( target.isEmpty() || target.size() + start > sequence.size() )
 382  4
             return -1;
 383  
 
 384  52
         for ( int startingIndex = start;
 385  100
             startingIndex < sequence.size() - target.size() + 1;
 386  48
             ++startingIndex ) {
 387  
 
 388  95
             if ( areEqual( sequence.at( startingIndex ), target.first() ) ) {
 389  55
                 for ( int index = 0;
 390  101
                     areEqual( sequence.at( startingIndex + index ), target.at( index ) );
 391  46
                     ++index ) {
 392  
 
 393  93
                     if ( index == target.size() - 1 )
 394  47
                         return startingIndex;
 395  
                 }
 396  
             }
 397  
         }
 398  
 
 399  5
         return -1;
 400  
     }
 401  
 
 402  
     /**
 403  
      * @see {@link jaggregate.ReadOnlySequence#reverse()}
 404  
      */
 405  
     public static <E> OrderedCollection<E> reverse( ReadOnlySequence<E> sequence ) {
 406  110
         ensureNotNull( sequence, SEQUENCE );
 407  
 
 408  110
         final OrderedCollection<E> reversed = emptyOrderedCollection();
 409  
 
 410  110
         sequence.forEachInReverseDo( new UnaryFunctor<E, Void>() {
 411  474
             public Void evaluate( E argument ) {
 412  364
                 reversed.add( argument );
 413  364
                 return null;
 414  
             }
 415  
         } );
 416  
 
 417  110
         return reversed;
 418  
     }
 419  
 
 420  
     /**
 421  
      * Raises an exception if the given index is found to be invalid on the given
 422  
      * sequence.
 423  
      *
 424  
      * @param sequence the sequence to address
 425  
      * @param index the index to check
 426  
      * @param failureMessage the message to add to the exception if {@code index} is
 427  
      * invalid on {@code sequence}
 428  
      * @throws SequenceIndexOutOfBoundsException if invalid index
 429  
      */
 430  
     public static void checkIndexIsValidOn( ReadOnlySequence<?> sequence, int index,
 431  
         String failureMessage ) {
 432  
 
 433  32158
         ensureIndexIsGreaterThan( index, -1, failureMessage );
 434  32080
         ensureIndexIsLessThan( index, sequence.size(), failureMessage );
 435  31955
     }
 436  
 
 437  
     /**
 438  
      * Raises an exception if the given index is found to be less than or equal to the
 439  
      * given boundary.
 440  
      *
 441  
      * @param index the index to check
 442  
      * @param boundary the boundary against which to check
 443  
      * @param failureMessage the message to add to the exception if {@code index} is
 444  
      * less than or equal to {@code boundary}
 445  
      * @throws SequenceIndexOutOfBoundsException if invalid index
 446  
      */
 447  
     public static void ensureIndexIsGreaterThan( int index, int boundary,
 448  
         String failureMessage ) {
 449  
 
 450  37319
         if ( index <= boundary )
 451  208
             throw new SequenceIndexOutOfBoundsException( failureMessage, index );
 452  37111
     }
 453  
 
 454  
     /**
 455  
      * Raises an exception if the given index is found to be greater than or equal to the
 456  
      * given boundary.
 457  
      *
 458  
      * @param index the index to check
 459  
      * @param boundary the boundary against which to check
 460  
      * @param failureMessage the message to add to the exception if {@code index} is
 461  
      * greater than or equal to {@code boundary}
 462  
      * @throws SequenceIndexOutOfBoundsException if invalid index
 463  
      */
 464  
     public static void ensureIndexIsLessThan( int index, int boundary,
 465  
         String failureMessage ) {
 466  
 
 467  36530
         if ( index >= boundary )
 468  255
             throw new SequenceIndexOutOfBoundsException( failureMessage, index );
 469  36275
     }
 470  
 
 471  6343
     private static class SequencesEqualAtEachIndex
 472  
         implements BinaryFunctor<Integer, Object, Void> {
 473  
 
 474  
         private final ReadOnlySequence<?> firstSequence;
 475  
         private final ReadOnlySequence<?> secondSequence;
 476  
 
 477  
         public SequencesEqualAtEachIndex( ReadOnlySequence<?> firstSequence,
 478  2406
             ReadOnlySequence<?> secondSequence ) {
 479  
 
 480  2406
             this.firstSequence = firstSequence;
 481  2406
             this.secondSequence = secondSequence;
 482  2406
         }
 483  
 
 484  
         public Void evaluate( Integer first, Object second ) {
 485  6343
             Object firstAtIndex = firstSequence.at( first );
 486  6343
             Object secondAtIndex = secondSequence.at( first );
 487  6343
             if ( !areEqual( firstAtIndex, secondAtIndex ) )
 488  2
                 throw new NonLocalReturnException();
 489  
 
 490  6341
             return null;
 491  
         }
 492  
     }
 493  
 }