Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$CompositeBinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$ConjunctiveBinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$DisjunctiveBinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$ExclusivelyDisjunctiveBinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$FirstBoundBinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$InvertedBinaryPredicate |
|
| 0.0;0 | ||||
BinaryPredicate$SecondBoundBinaryPredicate |
|
| 0.0;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 | } |