Coverage Report - jaggregate.BinaryPredicate
 
Classes in this File Line Coverage Branch Coverage Complexity
BinaryPredicate
100%
12/12
N/A
0
BinaryPredicate$CompositeBinaryPredicate
100%
5/5
N/A
0
BinaryPredicate$ConjunctiveBinaryPredicate
100%
4/4
100%
1/1
0
BinaryPredicate$DisjunctiveBinaryPredicate
100%
4/4
100%
1/1
0
BinaryPredicate$ExclusivelyDisjunctiveBinaryPredicate
100%
4/4
N/A
0
BinaryPredicate$FirstBoundBinaryPredicate
100%
6/6
N/A
0
BinaryPredicate$InvertedBinaryPredicate
100%
6/6
100%
1/1
0
BinaryPredicate$SecondBoundBinaryPredicate
100%
6/6
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 static java.lang.String.*;
 9  
 
 10  
 import static jaggregate.internal.ArgumentChecks.*;
 11  
 
 12  
 /**
 13  
  * A predicate that accepts two arguments.
 14  
  *
 15  
  * @param <A1> a constraint on the allowable types for the predicate's first argument
 16  
  * @param <A2> a constraint on the allowable types for the predicate's second argument
 17  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 18  
  * @version $Id: BinaryPredicate.java,v 1.6 2008/10/03 19:01:23 pholser Exp $
 19  
  */
 20  
 public abstract class BinaryPredicate<A1, A2> implements BinaryCondition<A1, A2> {
 21  
     /**
 22  
      * Creates a new predicate.
 23  
      */
 24  569
     protected BinaryPredicate() {
 25  
         // For subclasses.
 26  569
     }
 27  
 
 28  
     /**
 29  
      * Answers a predicate that represents the logical <dfn>inverse</dfn> of this
 30  
      * predicate; wherever this predicate's {@link #matches(Object,Object) matches}
 31  
      * method would answer {@code true}, the inverse answers {@code false}; and vice
 32  
      * versa.
 33  
      *
 34  
      * @return the inverse of this predicate
 35  
      */
 36  
     public final BinaryPredicate<A1, A2> not() {
 37  2
         return new InvertedBinaryPredicate<A1, A2>( this );
 38  
     }
 39  
 
 40  
     /**
 41  
      * Answers a predicate that represents the logical <dfn>inverse</dfn> of the given
 42  
      * predicate; wherever the given predicate's {@link #matches(Object,Object) matches}
 43  
      * method would answer {@code true}, the inverse answers {@code false}; and vice
 44  
      * versa.
 45  
      *
 46  
      * @param <T> constraint on the first type accepted by the predicate
 47  
      * @param <U> constraint on the second type accepted by the predicate
 48  
      * @param predicate the predicate to invert
 49  
      * @return the inverse of {@code predicate}
 50  
      * @throws NullPointerException if {@code predicate} is {@code null}
 51  
      */
 52  
     public static <T, U> BinaryPredicate<T, U> not( BinaryPredicate<T, U> predicate ) {
 53  2
         return new InvertedBinaryPredicate<T, U>( predicate );
 54  
     }
 55  
 
 56  
     /**
 57  
      * Answers a predicate that represents the logical <dfn>disjunction</dfn> of this
 58  
      * predicate and another predicate.  The disjunction's {@link #matches(Object,Object)
 59  
      * matches} method answers {@code true} if either this predicate or the other
 60  
      * predicate would answer {@code true}.
 61  
      * <p/>
 62  
      * When the disjunction is evaluated, this predicate is always evaluated first.
 63  
      * The other predicate may not be evaluated; so do not depend on its evaluation for
 64  
      * side effects.
 65  
      *
 66  
      * @param other the "right-hand" operand of the disjunction
 67  
      * @return the logical disjunction of this predicate and {@code other}
 68  
      */
 69  
     public final BinaryPredicate<A1, A2> or(
 70  
         BinaryPredicate<? super A1, ? super A2> other ) {
 71  
 
 72  4
         return new DisjunctiveBinaryPredicate<A1, A2>( this, other );
 73  
     }
 74  
 
 75  
     /**
 76  
      * Answers a predicate that represents the logical <dfn>conjunction</dfn> of this
 77  
      * predicate and another predicate.  The conjunction's {@link #matches(Object,Object)
 78  
      * matches} method answers {@code true} if both this predicate and the
 79  
      * other predicate would answer {@code true}.
 80  
      * <p/>
 81  
      * When the conjunction is evaluated, this predicate is always evaluated first.
 82  
      * The other predicate may not be evaluated; so do not depend on its evaluation for
 83  
      * side effects.
 84  
      *
 85  
      * @param other the "right-hand" operand of the conjunction
 86  
      * @return the logical conjunction of this predicate and {@code other}
 87  
      */
 88  
     public final BinaryPredicate<A1, A2> and(
 89  
         BinaryPredicate<? super A1, ? super A2> other ) {
 90  
 
 91  4
         return new ConjunctiveBinaryPredicate<A1, A2>( this, other );
 92  
     }
 93  
 
 94  
     /**
 95  
      * Answers a predicate that represents the logical <dfn>exclusive disjunction</dfn>
 96  
      * of this predicate and another predicate.  The disjunction's
 97  
      * {@link #matches(Object,Object) matches} method answers {@code true} if either
 98  
      * this predicate or the other predicate, but not both, would answer {@code true}.
 99  
      * <p/>
 100  
      * When the disjunction is evaluated, both this predicate and the other predicate
 101  
      * will be evaluated.
 102  
      *
 103  
      * @param other the "right-hand" operand of the disjunction
 104  
      * @return the logical exclusive disjunction of this predicate and {@code other}
 105  
      */
 106  
     public final BinaryPredicate<A1, A2> xor(
 107  
         BinaryPredicate<? super A1, ? super A2> other ) {
 108  
 
 109  4
         return new ExclusivelyDisjunctiveBinaryPredicate<A1, A2>( this, other );
 110  
     }
 111  
 
 112  
     /**
 113  
      * Answers a predicate of one argument that matches in the same manner as this
 114  
      * predicate would if evaluated using a fixed first argument and another argument.
 115  
      *
 116  
      * @param boundArgument the fixed argument used as the first argument to this
 117  
      * predicate's {@link #matches(Object,Object) matches} method
 118  
      * @return a predicate that accepts one argument, of the same type as this
 119  
      * predicate's second argument type, that behaves as this predicate would if always
 120  
      * evaluated using {@code boundArgument} as the first argument
 121  
      */
 122  
     public final UnaryPredicate<A2> bindFirst( A1 boundArgument ) {
 123  38
         return new FirstBoundBinaryPredicate<A1, A2>( this, boundArgument );
 124  
     }
 125  
 
 126  
     /**
 127  
      * Answers a predicate of one argument that matches in the same manner as this
 128  
      * predicate would if evaluated using a fixed second argument and another argument.
 129  
      *
 130  
      * @param boundArgument the fixed argument used as the second argument to this
 131  
      * predicate's {@link #matches(Object,Object) matches} method
 132  
      * @return a predicate that accepts one argument, of the same type as this
 133  
      * predicate's first argument type, that behaves as this predicate would if always
 134  
      * evaluated using {@code boundArgument} as the second argument
 135  
      */
 136  
     public final UnaryPredicate<A1> bindSecond( A2 boundArgument ) {
 137  345
         return new SecondBoundBinaryPredicate<A1, A2>( this, boundArgument );
 138  
     }
 139  
 
 140  
     /**
 141  
      * Gives a string template that describes the kinds of objects which match this
 142  
      * predicate when it has its first argument bound via {@link #bindFirst(Object)}.
 143  
      * <p/>
 144  
      * The string should have at least one format specification as described by
 145  
      * {@link String#format(String, Object...)}; that specification should expect to
 146  
      * get filled in with this predicate's first bound argument.
 147  
      * <p/>
 148  
      * If not overridden, this method answers the same as {@link #toString()}.
 149  
      *
 150  
      * @see #describe()
 151  
      * @return a descriptive string template
 152  
      */
 153  
     protected String getFirstBoundDescription() {
 154  2
         return toString();
 155  
     }
 156  
 
 157  
     /**
 158  
      * Gives a string template that describes the kinds of objects which match this
 159  
      * predicate when it has its second argument bound via {@link #bindSecond(Object)}.
 160  
      * <p/>
 161  
      * The string should have at least one format specification as described by
 162  
      * {@link String#format(String, Object...)}; that specification should expect to
 163  
      * get filled in with this predicate's second bound argument.
 164  
      * <p/>
 165  
      * If not overridden, this method answers the same as
 166  
      * {@link #getFirstBoundDescription()}.
 167  
      *
 168  
      * @see #describe()
 169  
      * @return a descriptive string template
 170  
      */
 171  
     protected String getSecondBoundDescription() {
 172  7
         return getFirstBoundDescription();
 173  
     }
 174  
 
 175  
     /**
 176  
      * Gives a string that describes the kinds of objects which match this predicate.
 177  
      * <p/>
 178  
      * If not overridden, this method answers the same as {@link #toString()}.
 179  
      *
 180  
      * @return a description of objects that meet this predicate's criteria
 181  
      */
 182  
     public String describe() {
 183  15
         return toString();
 184  
     }
 185  
 
 186  
     private static class InvertedBinaryPredicate<A1, A2>
 187  
         extends BinaryPredicate<A1, A2> {
 188  
 
 189  
         private final BinaryPredicate<A1, A2> wrapped;
 190  
 
 191  4
         InvertedBinaryPredicate( BinaryPredicate<A1, A2> wrapped ) {
 192  4
             ensureNotNull( wrapped, DISCRIMINATOR );
 193  3
             this.wrapped = wrapped;
 194  3
         }
 195  
 
 196  
         public boolean matches( A1 first, A2 second ) {
 197  8
             return !wrapped.matches( first, second );
 198  
         }
 199  
 
 200  
         @Override
 201  
         public String describe() {
 202  1
             return format( "something other than <%1$s>", wrapped.describe() );
 203  
         }
 204  
     }
 205  
 
 206  
     private abstract static class CompositeBinaryPredicate<A1, A2>
 207  
         extends BinaryPredicate<A1, A2> {
 208  
 
 209  
         protected final BinaryPredicate<A1, A2> first;
 210  
         protected final BinaryPredicate<? super A1, ? super A2> second;
 211  
 
 212  
         protected CompositeBinaryPredicate( BinaryPredicate<A1, A2> first,
 213  12
             BinaryPredicate<? super A1, ? super A2> second ) {
 214  
 
 215  12
             ensureNotNull( second, "'right-hand' operand" );
 216  
 
 217  9
             this.first = first;
 218  9
             this.second = second;
 219  9
         }
 220  
     }
 221  
 
 222  
     private static class DisjunctiveBinaryPredicate<A1, A2>
 223  
         extends CompositeBinaryPredicate<A1, A2> {
 224  
 
 225  
         DisjunctiveBinaryPredicate( BinaryPredicate<A1, A2> first,
 226  
             BinaryPredicate<? super A1, ? super A2> second ) {
 227  
 
 228  4
             super( first, second );
 229  3
         }
 230  
 
 231  
         public boolean matches( A1 first, A2 second ) {
 232  9
             return this.first.matches( first, second )
 233  
                 || this.second.matches( first, second );
 234  
         }
 235  
 
 236  
         @Override
 237  
         public String describe() {
 238  1
             return format( "<%1$s> or <%2$s>", first.describe(), second.describe() );
 239  
         }
 240  
     }
 241  
 
 242  
     private static class ConjunctiveBinaryPredicate<A1, A2>
 243  
         extends CompositeBinaryPredicate<A1, A2> {
 244  
 
 245  
         ConjunctiveBinaryPredicate( BinaryPredicate<A1, A2> first,
 246  
             BinaryPredicate<? super A1, ? super A2> second ) {
 247  
 
 248  4
             super( first, second );
 249  3
         }
 250  
 
 251  
         public boolean matches( A1 first, A2 second ) {
 252  9
             return this.first.matches( first, second )
 253  
                 && this.second.matches( first, second );
 254  
         }
 255  
 
 256  
         @Override
 257  
         public String describe() {
 258  1
             return format( "<%1$s> and <%2$s>", first.describe(), second.describe() );
 259  
         }
 260  
     }
 261  
 
 262  
     private static class ExclusivelyDisjunctiveBinaryPredicate<A1, A2>
 263  
         extends CompositeBinaryPredicate<A1, A2> {
 264  
 
 265  
         ExclusivelyDisjunctiveBinaryPredicate( BinaryPredicate<A1, A2> first,
 266  
             BinaryPredicate<? super A1, ? super A2> second ) {
 267  
 
 268  4
             super( first, second );
 269  3
         }
 270  
 
 271  
         public boolean matches( A1 first, A2 second ) {
 272  10
             return this.first.matches( first, second )
 273  
                 ^ this.second.matches( first, second );
 274  
         }
 275  
 
 276  
         @Override
 277  
         public String describe() {
 278  1
             return format( "either <%1$s> or <%2$s> but not both", first.describe(),
 279  
                 second.describe() );
 280  
         }
 281  
     }
 282  
 
 283  
     private static class FirstBoundBinaryPredicate<A1, A2> extends UnaryPredicate<A2> {
 284  
         private final BinaryPredicate<A1, A2> wrapped;
 285  
         private final A1 boundArgument;
 286  
 
 287  38
         FirstBoundBinaryPredicate( BinaryPredicate<A1, A2> wrapped, A1 boundArgument ) {
 288  38
             this.wrapped = wrapped;
 289  38
             this.boundArgument = boundArgument;
 290  38
         }
 291  
 
 292  
         public boolean matches( A2 target ) {
 293  33
             return wrapped.matches( boundArgument, target );
 294  
         }
 295  
 
 296  
         @Override
 297  
         public String describe() {
 298  6
             return format( wrapped.getFirstBoundDescription(), boundArgument );
 299  
         }
 300  
     }
 301  
 
 302  
     private static class SecondBoundBinaryPredicate<A1, A2> extends UnaryPredicate<A1> {
 303  
         private final BinaryPredicate<A1, A2> wrapped;
 304  
         private final A2 boundArgument;
 305  
 
 306  345
         SecondBoundBinaryPredicate( BinaryPredicate<A1, A2> wrapped, A2 boundArgument ) {
 307  345
             this.wrapped = wrapped;
 308  345
             this.boundArgument = boundArgument;
 309  345
         }
 310  
 
 311  
         public boolean matches( A1 target ) {
 312  634
             return wrapped.matches( target, boundArgument );
 313  
         }
 314  
 
 315  
         @Override
 316  
         public String describe() {
 317  16
             return format( wrapped.getSecondBoundDescription(), boundArgument );
 318  
         }
 319  
     }
 320  
 }