1   /*
2    * Copyright 2006-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sourceforge.domian.specification.example;
17  
18  
19  import java.util.Date;
20  
21  import org.junit.Assert;
22  import org.junit.Before;
23  import org.junit.Test;
24  
25  import net.sourceforge.domian.specification.CompositeSpecification;
26  import net.sourceforge.domian.specification.Specification;
27  import net.sourceforge.domian.specification.SpecificationFactory;
28  import net.sourceforge.domian.test.domain.Customer;
29  import net.sourceforge.domian.test.domain.Order;
30  import net.sourceforge.domian.test.domain.OrderLine;
31  
32  import junit.framework.TestCase;
33  
34  import static net.sourceforge.domian.specification.SpecificationFactory.a;
35  import static net.sourceforge.domian.specification.SpecificationFactory.after;
36  import static net.sourceforge.domian.specification.SpecificationFactory.afterOrAtTheSameTimeAs;
37  import static net.sourceforge.domian.specification.SpecificationFactory.all;
38  import static net.sourceforge.domian.specification.SpecificationFactory.allObjects;
39  import static net.sourceforge.domian.specification.SpecificationFactory.atLeast;
40  import static net.sourceforge.domian.specification.SpecificationFactory.atMost;
41  import static net.sourceforge.domian.specification.SpecificationFactory.atTheSameTimeAs;
42  import static net.sourceforge.domian.specification.SpecificationFactory.before;
43  import static net.sourceforge.domian.specification.SpecificationFactory.both;
44  import static net.sourceforge.domian.specification.SpecificationFactory.createDefaultNumberSpecification;
45  import static net.sourceforge.domian.specification.SpecificationFactory.createSpecificationFor;
46  import static net.sourceforge.domian.specification.SpecificationFactory.equalTo;
47  import static net.sourceforge.domian.specification.SpecificationFactory.greaterThan;
48  import static net.sourceforge.domian.specification.SpecificationFactory.haveSizeOf;
49  import static net.sourceforge.domian.specification.SpecificationFactory.include;
50  import static net.sourceforge.domian.specification.SpecificationFactory.includeAPercentageOf;
51  import static net.sourceforge.domian.specification.SpecificationFactory.is;
52  import static net.sourceforge.domian.specification.SpecificationFactory.isAfter;
53  import static net.sourceforge.domian.specification.SpecificationFactory.isAfterOrAtTheSameTimeAs;
54  import static net.sourceforge.domian.specification.SpecificationFactory.isBefore;
55  import static net.sourceforge.domian.specification.SpecificationFactory.isDate;
56  import static net.sourceforge.domian.specification.SpecificationFactory.isEnum;
57  import static net.sourceforge.domian.specification.SpecificationFactory.isFalse;
58  import static net.sourceforge.domian.specification.SpecificationFactory.isFrom;
59  import static net.sourceforge.domian.specification.SpecificationFactory.isTrue;
60  import static net.sourceforge.domian.specification.SpecificationFactory.matches;
61  import static net.sourceforge.domian.specification.SpecificationFactory.matchesWildcardExpression;
62  import static net.sourceforge.domian.specification.SpecificationFactory.moreThan;
63  import static net.sourceforge.domian.specification.SpecificationFactory.not;
64  import static net.sourceforge.domian.specification.SpecificationFactory.specify;
65  import static net.sourceforge.domian.test.domain.Customer.Gender.FEMALE;
66  import static net.sourceforge.domian.test.domain.Customer.Gender.MALE;
67  import static net.sourceforge.domian.test.domain.Testdata.femaleCustomer;
68  import static net.sourceforge.domian.test.domain.Testdata.maleCustomer;
69  import static net.sourceforge.domian.test.domain.Testdata.thisDate;
70  import static net.sourceforge.domian.test.domain.Testdata.thisMonth;
71  import static net.sourceforge.domian.test.domain.Testdata.thisYear;
72  import static net.sourceforge.domian.test.domain.Testdata.today;
73  import static net.sourceforge.domian.util.DateUtils.getTime;
74  import static org.junit.Assert.assertFalse;
75  import static org.junit.Assert.assertTrue;
76  
77  
78  public class IntroductionExamplesTest {
79  
80      OrderLine cancelledOrderLine = new OrderLine(8001L).paymentReceived(false).cancelled(true);
81      OrderLine paymentReceivedOrderLine = new OrderLine(8002L).paymentReceived(true).cancelled(false);
82      OrderLine paymentNotReceivedOrderLine = new OrderLine(8003L).paymentReceived(false).cancelled(false);
83      OrderLine toBeRefundedOrderLine = new OrderLine(8004L).paymentReceived(true).cancelled(true);
84  
85      Order orderFromThisYear_1 = new Order(1L);
86      Order orderFromThisYear_2 = new Order(2L);
87      Order orderFromThisYear_3 = new Order(3L);
88      Order orderFromThisYear_4 = new Order(4L);
89      Order orderFromOneYearAgo_1 = new Order(101L);
90      Order orderFromOneYearAgo_2 = new Order(102L);
91      Order orderFromOneYearAgo_3 = new Order(103L);
92      Order orderFromOneYearAgo_4 = new Order(104L);
93      Order orderFromTwoYearsAgo_1 = new Order(201L);
94      Order orderFromTwoYearsAgo_2 = new Order(202L);
95      Order orderFromThreeYearsAgo_1 = new Order(301L);
96      Order orderFromThreeYearsAgo_2 = new Order(302L);
97  
98      Customer aCustomerWithNoOrders = new Customer(8001L, getTime(1999, 9, 1));
99      Customer aCustomerWithOneOrder = new Customer(8002L, getTime(1999, 9, 1));
100     Customer aCustomerWithTwoOrders = new Customer(8003L, getTime(1999, 9, 1));
101     Customer aCustomerWithThreeOrders = new Customer(8004L, getTime(1999, 9, 1));
102     Customer aCustomerWithFourOrders = new Customer(8005L, getTime(1999, 9, 1));
103     Customer aCustomerWithFiveOrders = new Customer(8006L, getTime(1999, 9, 1));
104     Customer aCustomerWithSixOrders = new Customer(8007L, getTime(1999, 9, 1));
105     Customer aCustomerWithEightOrders = new Customer(8008L, getTime(1999, 9, 1));
106 
107 
108     @Before
109     public void beforeEachMethod() {
110         // TODO: hmm, won't work around new year... at jan 3 everything should be green again ;-)
111         orderFromThisYear_1.setOrderDate(getTime(thisYear, thisMonth, thisDate));
112         orderFromThisYear_1.addOrderLine(paymentReceivedOrderLine);
113         orderFromThisYear_2.setOrderDate(getTime(thisYear, thisMonth, thisDate - 1));
114         orderFromThisYear_2.addOrderLine(paymentReceivedOrderLine);
115         orderFromThisYear_3.setOrderDate(getTime(thisYear, thisMonth, thisDate - 2));
116         orderFromThisYear_3.addOrderLine(paymentReceivedOrderLine);
117         orderFromThisYear_3.addOrderLine(paymentReceivedOrderLine);
118         orderFromThisYear_3.addOrderLine(cancelledOrderLine);
119         orderFromThisYear_4.setOrderDate(getTime(thisYear, thisMonth - 3, thisDate - 3));
120         orderFromThisYear_4.addOrderLine(paymentNotReceivedOrderLine);
121 
122         orderFromOneYearAgo_1.setOrderDate(getTime(thisYear - 1, thisMonth, thisDate));
123         orderFromOneYearAgo_1.addOrderLine(paymentReceivedOrderLine);
124         orderFromOneYearAgo_2.setOrderDate(getTime(thisYear - 1, thisMonth, thisDate - 1));
125         orderFromOneYearAgo_2.addOrderLine(paymentReceivedOrderLine);
126         orderFromOneYearAgo_3.setOrderDate(getTime(thisYear - 1, thisMonth, thisDate - 2));
127         orderFromOneYearAgo_3.addOrderLine(paymentReceivedOrderLine);
128         orderFromOneYearAgo_4.setOrderDate(getTime(thisYear - 1, thisMonth, thisDate - 3));
129         orderFromOneYearAgo_4.addOrderLine(paymentReceivedOrderLine);
130 
131         orderFromTwoYearsAgo_1.setOrderDate(getTime(thisYear - 2, thisMonth, thisDate));
132         orderFromTwoYearsAgo_1.addOrderLine(paymentReceivedOrderLine);
133         orderFromTwoYearsAgo_2.setOrderDate(getTime(thisYear - 2, thisMonth, thisDate - 1));
134         orderFromTwoYearsAgo_2.addOrderLine(paymentReceivedOrderLine);
135 
136         orderFromThreeYearsAgo_1.setOrderDate(getTime(thisYear - 3, thisMonth, thisDate));
137         orderFromThreeYearsAgo_1.addOrderLine(paymentReceivedOrderLine);
138         orderFromThreeYearsAgo_2.setOrderDate(getTime(thisYear - 3, thisMonth, thisDate - 1));
139         orderFromThreeYearsAgo_2.addOrderLine(paymentReceivedOrderLine);
140 
141         aCustomerWithOneOrder.addOrder(orderFromThisYear_4);
142 
143         aCustomerWithTwoOrders.addOrder(orderFromThisYear_1);
144         aCustomerWithTwoOrders.addOrder(orderFromThisYear_2);
145 
146         aCustomerWithThreeOrders.addOrder(orderFromThisYear_1);
147         aCustomerWithThreeOrders.addOrder(orderFromThisYear_3);
148         aCustomerWithThreeOrders.addOrder(orderFromOneYearAgo_1);
149 
150         aCustomerWithFourOrders.addOrder(orderFromThisYear_1);
151         aCustomerWithFourOrders.addOrder(orderFromThisYear_2);
152         aCustomerWithFourOrders.addOrder(orderFromOneYearAgo_1);
153         aCustomerWithFourOrders.addOrder(orderFromOneYearAgo_2);
154 
155         aCustomerWithFiveOrders.addOrder(orderFromThisYear_1);
156         aCustomerWithFiveOrders.addOrder(orderFromThisYear_2);
157         aCustomerWithFiveOrders.addOrder(orderFromOneYearAgo_1);
158         aCustomerWithFiveOrders.addOrder(orderFromOneYearAgo_2);
159         aCustomerWithFiveOrders.addOrder(orderFromTwoYearsAgo_1);
160 
161         aCustomerWithSixOrders.addOrder(orderFromThisYear_1);
162         aCustomerWithSixOrders.addOrder(orderFromThisYear_2);
163         aCustomerWithSixOrders.addOrder(orderFromOneYearAgo_1);
164         aCustomerWithSixOrders.addOrder(orderFromOneYearAgo_2);
165         aCustomerWithSixOrders.addOrder(orderFromTwoYearsAgo_1);
166         aCustomerWithSixOrders.addOrder(orderFromTwoYearsAgo_2);
167 
168         aCustomerWithEightOrders.addOrder(orderFromThisYear_1);
169         aCustomerWithEightOrders.addOrder(orderFromThisYear_2);
170         aCustomerWithEightOrders.addOrder(orderFromThisYear_3);
171         aCustomerWithEightOrders.addOrder(orderFromThisYear_4);
172         aCustomerWithEightOrders.addOrder(orderFromOneYearAgo_1);
173         aCustomerWithEightOrders.addOrder(orderFromOneYearAgo_2);
174         aCustomerWithEightOrders.addOrder(orderFromTwoYearsAgo_1);
175         aCustomerWithEightOrders.addOrder(orderFromTwoYearsAgo_2);
176     }
177 
178 
179     static Date createOneYearAgoDate() {
180         return getTime(thisYear - 1, thisMonth, thisDate);
181     }
182 
183 
184     static Date createThirtyYearsAheadDate() {
185         return getTime(thisYear + 30, thisMonth, thisDate);
186     }
187 
188 
189     static Date createThirtyYearsAgoDate() {
190         return getTime(thisYear - 30, thisMonth, thisDate);
191     }
192 
193 
194     class SubclassedCustomer extends Customer {}
195 
196 
197     @Test
198     public void testAllKindOffObjectsWithoutImports() {
199         net.sourceforge.domian.specification.Specification spec = net.sourceforge.domian.specification.SpecificationFactory.allObjects();
200 
201         assertFalse(spec.isSatisfiedBy(null));
202         assertTrue(spec.isSatisfiedBy(""));
203         assertTrue(spec.isSatisfiedBy(new Float("3.14")));
204         assertTrue(spec.isSatisfiedBy(new ShippingContainer(18, false)));
205     }
206 
207 
208     @Test
209     public void testAllKindOffObjects() {
210         Specification spec = allObjects();
211 
212         assertFalse(spec.isSatisfiedBy(null));
213         assertTrue(spec.isSatisfiedBy(""));
214         assertTrue(spec.isSatisfiedBy(new Float("3.14")));
215         assertTrue(spec.isSatisfiedBy(new ShippingContainer(18, false)));
216     }
217 
218 
219     @Test
220     public void testTypeParameterizeddObjectsWithoutImports() {
221         Customer aCustomer = maleCustomer;
222         Customer anotherCustomer = femaleCustomer;
223 
224         net.sourceforge.domian.specification.Specification<Customer> spec = createSpecificationFor(Customer.class);
225 
226         assertFalse(spec.isSatisfiedBy(null));
227         assertTrue(spec.isSatisfiedBy(aCustomer));
228         assertTrue(spec.isSatisfiedBy(anotherCustomer));
229         //assertFalse(spec.isSatisfiedBy(new Date()));    // Should not compile - OK
230     }
231 
232 
233     @Test
234     public void testTypedParameterizedObjects() {
235         Customer aCustomer = maleCustomer;
236         Customer anotherCustomer = femaleCustomer;
237         SubclassedCustomer subclassedCustomer = new SubclassedCustomer();
238 
239         Specification<Customer> spec = createSpecificationFor(Customer.class);
240 
241         assertFalse(spec.isSatisfiedBy(null));
242         assertTrue(spec.isSatisfiedBy(aCustomer));
243         assertTrue(spec.isSatisfiedBy(anotherCustomer));
244         assertTrue(spec.isSatisfiedBy(subclassedCustomer));
245         //assertFalse(spec.isSatisfiedBy(new Date()));    // Should not compile - OK
246     }
247 
248 
249     @Test
250     public void testRawSpecifications() {
251         Customer aCustomer = maleCustomer;
252         Customer anotherCustomer = femaleCustomer;
253         SubclassedCustomer subclassedCustomer = new SubclassedCustomer();
254 
255         Specification spec = createSpecificationFor(Customer.class);
256 
257         assertFalse(spec.isSatisfiedBy(null));
258         assertTrue(spec.isSatisfiedBy(aCustomer));
259         assertTrue(spec.isSatisfiedBy(anotherCustomer));
260         assertTrue(spec.isSatisfiedBy(subclassedCustomer));
261         assertFalse(spec.isSatisfiedBy(new Date()));
262     }
263 
264 
265     @Test
266     public void testEquality() {
267         Specification<Long> long42 = equalTo(42L);
268         Specification<Date> oneYearAgo = atTheSameTimeAs(createOneYearAgoDate());
269     }
270 
271 
272     @Test
273     public void testRegularExpressionMatcherStringSpecification() {
274         Specification<String> shorterThanThree = matches(".{0," + (2) + "}");
275         assertFalse(shorterThanThree.isSatisfiedBy(null));
276         assertTrue(shorterThanThree.isSatisfiedBy(""));
277         assertTrue(shorterThanThree.isSatisfiedBy("1"));
278         assertTrue(shorterThanThree.isSatisfiedBy("12"));
279         assertFalse(shorterThanThree.isSatisfiedBy("123"));
280         assertFalse(shorterThanThree.isSatisfiedBy("1 2 3 4 5 6 7 9"));
281     }
282 
283 
284     @Test
285     public void testWildcardExpressionMatcherStringSpecification() {
286         Specification<String> startsAndEndsWithA_AtLeastSevenCharsLong = matchesWildcardExpression("A?????*a");
287         assertFalse(startsAndEndsWithA_AtLeastSevenCharsLong.isSatisfiedBy(null));
288         assertFalse(startsAndEndsWithA_AtLeastSevenCharsLong.isSatisfiedBy("Aloha"));
289         assertTrue(startsAndEndsWithA_AtLeastSevenCharsLong.isSatisfiedBy("Arizona"));
290         assertTrue(startsAndEndsWithA_AtLeastSevenCharsLong.isSatisfiedBy("Australia"));
291         assertFalse(startsAndEndsWithA_AtLeastSevenCharsLong.isSatisfiedBy("australia"));
292     }
293 
294 
295     @Test
296     public void testComparison() {
297         Specification<Integer> greaterThan10 = greaterThan(10);
298         Specification<Date> lessThanOneYearAgo = after(createOneYearAgoDate());
299     }
300 
301 
302     @Test
303     public void testDefaultValue() {
304         Specification<Number> defaultNumber = createDefaultNumberSpecification();
305         assertTrue(defaultNumber.isSatisfiedBy(0));
306         assertTrue(defaultNumber.isSatisfiedBy(0L));
307         assertTrue(defaultNumber.isSatisfiedBy(0.0));
308         assertTrue(defaultNumber.isSatisfiedBy(0.0d));
309         assertFalse(defaultNumber.isSatisfiedBy(-1));
310         assertFalse(defaultNumber.isSatisfiedBy(1234567890123L));
311         assertFalse(defaultNumber.isSatisfiedBy(0.1));
312         assertFalse(defaultNumber.isSatisfiedBy(0.164537d));
313 
314         Specification<Number> notDefaultNumber = not(defaultNumber);
315         assertFalse(notDefaultNumber.isSatisfiedBy(0));
316         assertFalse(notDefaultNumber.isSatisfiedBy(0L));
317         assertFalse(notDefaultNumber.isSatisfiedBy(0.0));
318         assertFalse(notDefaultNumber.isSatisfiedBy(0.0d));
319         assertTrue(notDefaultNumber.isSatisfiedBy(-1));
320         assertTrue(notDefaultNumber.isSatisfiedBy(1234567890123L));
321         assertTrue(notDefaultNumber.isSatisfiedBy(0.1));
322         assertTrue(notDefaultNumber.isSatisfiedBy(0.164537d));
323     }
324 
325 
326     @Test
327     public void testDateStringSpecification() {
328         Specification<String> spec = isDate("ddMMyyyy");
329         assertFalse(spec.isSatisfiedBy("01-01-2007")); // Not correct date format
330         assertFalse(spec.isSatisfiedBy("32032007"));   // Illegal date
331         assertFalse(spec.isSatisfiedBy("01132007"));   // Illegal month
332         assertFalse(spec.isSatisfiedBy("29022007"));   // Not a leap year
333         assertTrue(spec.isSatisfiedBy("01012007"));
334         assertTrue(spec.isSatisfiedBy("01012007"));
335 
336         Specification<Date> dateSpec = before("2008-06-07");
337         assertFalse(dateSpec.isSatisfiedBy(getTime(2008, 6, 7)));
338         assertTrue(dateSpec.isSatisfiedBy(getTime(2008, 6, 6)));
339     }
340 
341 
342     enum Colour {
343         BLACK, WHITE
344     }
345 
346 
347     @Test
348     public void testEnumNameStringSpecification() {
349         Specification<String> spec = isEnum(Colour.class);
350         assertFalse(spec.isSatisfiedBy(null));
351         assertFalse(spec.isSatisfiedBy("01012007"));
352         assertFalse(spec.isSatisfiedBy("SHADY"));
353         assertTrue(spec.isSatisfiedBy("WHITE"));
354     }
355 
356 
357     @Test
358     public void testSimpleCompositeSpecification() {
359         Specification<Date> lessThanThirtyYearsAgo = isAfter(createThirtyYearsAgoDate());
360         Specification<Date> lessThanThirtyYearsAhead = isBefore(createThirtyYearsAheadDate());
361 
362         Specification<Date> plusMinusThirtyYears = lessThanThirtyYearsAgo.and(lessThanThirtyYearsAhead);
363 
364         assertTrue(plusMinusThirtyYears.isSatisfiedBy(new Date()));
365         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(1990, 7, 30)));
366         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(2037, 7, 29)));
367         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(1977, 7, 30)));
368         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(2050, 7, 30)));
369 
370         plusMinusThirtyYears = is(lessThanThirtyYearsAgo).and(lessThanThirtyYearsAhead);
371 
372         assertTrue(plusMinusThirtyYears.isSatisfiedBy(new Date()));
373         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(1990, 7, 30)));
374         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(2037, 7, 29)));
375         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(1977, 7, 30)));
376         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(2050, 7, 30)));
377 
378         plusMinusThirtyYears = both(lessThanThirtyYearsAgo, lessThanThirtyYearsAhead);
379 
380         assertTrue(plusMinusThirtyYears.isSatisfiedBy(new Date()));
381         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(1990, 7, 30)));
382         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(2037, 7, 29)));
383         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(1977, 7, 30)));
384         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(2050, 7, 30)));
385     }
386 
387 
388     @Test
389     public void testCompositeSpecifications() {
390         Date thirtyYearsAgo = createThirtyYearsAgoDate();
391         Date thirtyYearsAhead = createThirtyYearsAheadDate();
392 
393         Specification<Date> lessThanThirtyYearsAgo = isAfter(thirtyYearsAgo);
394         Specification<Date> lessThanThirtyYearsAhead = isBefore(thirtyYearsAhead);
395 
396         Specification<Date> plusMinusThirtyYears = is(lessThanThirtyYearsAgo).and(lessThanThirtyYearsAhead);
397         assertFalse(plusMinusThirtyYears.isSatisfiedBy(null));
398         assertTrue(plusMinusThirtyYears.isSatisfiedBy(new Date()));
399         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(1990, 7, 30)));
400         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(2037, 7, 29)));
401         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(1977, 7, 30)));
402         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(2050, 7, 30)));
403 
404         plusMinusThirtyYears = both(lessThanThirtyYearsAgo, lessThanThirtyYearsAhead);
405         assertFalse(plusMinusThirtyYears.isSatisfiedBy(null));
406         assertTrue(plusMinusThirtyYears.isSatisfiedBy(new Date()));
407         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(1990, 7, 30)));
408         assertTrue(plusMinusThirtyYears.isSatisfiedBy(getTime(2037, 7, 29)));
409         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(1977, 7, 30)));
410         assertFalse(plusMinusThirtyYears.isSatisfiedBy(getTime(2050, 7, 30)));
411     }
412 
413 
414     @Test
415     public void testNotAssosiativeSpecificationBuilding() {
416         Date oneYearAgo = getTime(thisYear - 1, thisMonth, thisDate);
417         Date oneYearAndOneMonthAgo = getTime(thisYear - 1, thisMonth - 1, thisDate);
418         Date tenYearsAgo = getTime(thisYear - 10, thisMonth, thisDate);
419         Date sixteenYearsAgo = getTime(thisYear - 16, thisMonth, thisDate);
420 
421         Customer aChildMaleCustomer = new Customer().gender(FEMALE).birthDate(tenYearsAgo).membershipDate(today);
422         Customer aTeenageMaleCustomer = new Customer().gender(MALE).birthDate(sixteenYearsAgo).membershipDate(today);
423         Customer aMaleCustomer = new Customer().gender(MALE).birthDate(getTime(1975, 10, 10)).membershipDate(today);
424         Customer aChildFemaleCustomer = new Customer().gender(FEMALE).birthDate(tenYearsAgo).membershipDate(today);
425         Customer aTeenageFemaleCustomer = new Customer().gender(FEMALE).birthDate(sixteenYearsAgo).membershipDate(today);
426         Customer aFreshFemaleCustomer = new Customer().gender(FEMALE).birthDate(getTime(1975, 10, 10)).membershipDate(today);
427         Customer aVeteranFemaleCustomer = new Customer().gender(FEMALE).birthDate(getTime(1975, 10, 10)).membershipDate(oneYearAndOneMonthAgo);
428         Customer aVeteranChildFemaleCustomer = new Customer().gender(FEMALE).birthDate(tenYearsAgo).membershipDate(oneYearAndOneMonthAgo);
429 
430         Specification<Customer> spec = specify(Customer.class)
431                 .where("gender", is(FEMALE))
432                 .and("membershipDate", isBefore(oneYearAgo))
433                 .or("gender", is(MALE))
434                 .and("birthDate", is(not(afterOrAtTheSameTimeAs(tenYearsAgo))));
435 
436         assertTrue(spec.isSatisfiedBy(aVeteranFemaleCustomer));
437         assertFalse(spec.isSatisfiedBy(aFreshFemaleCustomer));
438         assertTrue(spec.isSatisfiedBy(aMaleCustomer));
439         assertFalse(spec.isSatisfiedBy(aTeenageFemaleCustomer));
440         assertTrue(spec.isSatisfiedBy(aTeenageMaleCustomer));
441         assertFalse(spec.isSatisfiedBy(aChildFemaleCustomer));
442         assertFalse(spec.isSatisfiedBy(aChildMaleCustomer));
443 
444         Specification<Customer> femaleCustomer = specify(Customer.class).where("gender", is(FEMALE));
445         Specification<Customer> maleCustomer = specify(Customer.class).where("gender", is(MALE));
446         Specification<Customer> veteranCustomer = specify(Customer.class).where("membershipDate", isBefore(oneYearAgo));
447         Specification<Customer> childCustomer = specify(Customer.class).where("birthDate", isAfterOrAtTheSameTimeAs(tenYearsAgo));
448 
449         Specification<Customer> notChildCustomer = not(a(childCustomer));
450 
451         spec = ((both(femaleCustomer, veteranCustomer)).or(a(maleCustomer))).and(not(a(childCustomer)));
452 
453         assertTrue(spec.isSatisfiedBy(aVeteranFemaleCustomer));
454         assertFalse(spec.isSatisfiedBy(aFreshFemaleCustomer));
455         assertTrue(spec.isSatisfiedBy(aMaleCustomer));
456         assertFalse(spec.isSatisfiedBy(aTeenageFemaleCustomer));
457         assertTrue(spec.isSatisfiedBy(aTeenageMaleCustomer));
458         assertFalse(spec.isSatisfiedBy(aChildFemaleCustomer));
459         assertFalse(spec.isSatisfiedBy(aChildMaleCustomer));
460 
461         // Without uneccesary paranthesis and wrapper specifications
462         spec = both(femaleCustomer, veteranCustomer).or(maleCustomer).and(not(childCustomer));
463 
464         assertTrue(spec.isSatisfiedBy(aVeteranFemaleCustomer));
465         assertFalse(spec.isSatisfiedBy(aFreshFemaleCustomer));
466         assertTrue(spec.isSatisfiedBy(aMaleCustomer));
467         assertFalse(spec.isSatisfiedBy(aTeenageFemaleCustomer));
468         assertTrue(spec.isSatisfiedBy(aTeenageMaleCustomer));
469         assertFalse(spec.isSatisfiedBy(aChildFemaleCustomer));
470         assertFalse(spec.isSatisfiedBy(aChildMaleCustomer));
471 
472         // Without uneccesary logic...
473         spec = a(veteranCustomer).or(maleCustomer).and(not(childCustomer));
474 
475         assertTrue(spec.isSatisfiedBy(aVeteranFemaleCustomer));
476         assertFalse(spec.isSatisfiedBy(aFreshFemaleCustomer));
477         assertTrue(spec.isSatisfiedBy(aMaleCustomer));
478         assertFalse(spec.isSatisfiedBy(aTeenageFemaleCustomer));
479         assertTrue(spec.isSatisfiedBy(aTeenageMaleCustomer));
480         assertFalse(spec.isSatisfiedBy(aChildFemaleCustomer));
481         assertFalse(spec.isSatisfiedBy(aChildMaleCustomer));
482         assertFalse(spec.isSatisfiedBy(aVeteranChildFemaleCustomer));
483 
484         // NOT the same as
485         spec = maleCustomer.and(not(childCustomer)).or(veteranCustomer);
486 
487         assertTrue(spec.isSatisfiedBy(aVeteranFemaleCustomer));
488         assertFalse(spec.isSatisfiedBy(aFreshFemaleCustomer));
489         assertTrue(spec.isSatisfiedBy(aMaleCustomer));
490         assertFalse(spec.isSatisfiedBy(aTeenageFemaleCustomer));
491         assertTrue(spec.isSatisfiedBy(aTeenageMaleCustomer));
492         assertFalse(spec.isSatisfiedBy(aChildFemaleCustomer));
493         assertFalse(spec.isSatisfiedBy(aChildMaleCustomer));
494         assertTrue(spec.isSatisfiedBy(aVeteranChildFemaleCustomer));
495 
496         // also, NOT the same as
497         spec = a(maleCustomer).and(not(a(childCustomer))).or(a(veteranCustomer));
498 
499         assertTrue(spec.isSatisfiedBy(aVeteranFemaleCustomer));
500         assertFalse(spec.isSatisfiedBy(aFreshFemaleCustomer));
501         assertTrue(spec.isSatisfiedBy(aMaleCustomer));
502         assertFalse(spec.isSatisfiedBy(aTeenageFemaleCustomer));
503         assertTrue(spec.isSatisfiedBy(aTeenageMaleCustomer));
504         assertFalse(spec.isSatisfiedBy(aChildFemaleCustomer));
505         assertFalse(spec.isSatisfiedBy(aChildMaleCustomer));
506         assertTrue(spec.isSatisfiedBy(aVeteranChildFemaleCustomer));
507     }
508 
509 
510     @Test
511     public void testCollectionSpecification_1() {
512         // "Good customers" -> all customers having at least 4 orders in total
513         Specification<Customer> goodCustomers = all(Customer.class).where("orders", haveSizeOf(atLeast(4)));
514 
515         assertFalse(goodCustomers.isSatisfiedBy(null));
516         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
517         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
518         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
519         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
520         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
521         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
522         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
523         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
524     }
525 
526 
527     @Test
528     public void testCollectionSpecification_2_1() {
529         // "Good customers" -> all customers having at least 2 orders from each of the three latest calendar years
530         final Date beginningOfNextYear = getTime(thisYear + 1);
531         final Date beginningOfThisYear = getTime(thisYear);
532         final Date beginningOfLastYear = getTime(thisYear - 1);
533         final Date beginningOfNextToLastYear = getTime(thisYear - 2);
534         final Date beginningOfNextToNextToLastYear = getTime(thisYear - 3);
535         final Date beginningOfNextToNextToNextToLastYear = getTime(thisYear - 4);
536         final Date beginningOfNextToNextToNextToNextToLastYear = getTime(thisYear - 5);
537 
538         final Specification<Date> beforeThisYear = isBefore(beginningOfThisYear);
539         final Specification<Date> beforeLastYear = isBefore(beginningOfLastYear);
540         final Specification<Date> beforeNextToLastYear = isBefore(beginningOfNextToLastYear);
541         final Specification<Date> beforeNextToNextToLastYear = isBefore(beginningOfNextToNextToLastYear);
542         final Specification<Date> beforeNextToNextToNextToLastYear = isBefore(beginningOfNextToNextToNextToLastYear);
543         final Specification<Date> beforeNextToNextToNextToNextToLastYear = isBefore(beginningOfNextToNextToNextToNextToLastYear);
544 
545         final Specification<Date> afterThisYear = isAfterOrAtTheSameTimeAs(beginningOfNextYear);
546         final Specification<Date> afterLastYear = isAfterOrAtTheSameTimeAs(beginningOfThisYear);
547         final Specification<Date> afterNextToLastYear = isAfterOrAtTheSameTimeAs(beginningOfLastYear);
548         final Specification<Date> afterNextToNextToLastYear = isAfterOrAtTheSameTimeAs(beginningOfNextToLastYear);
549         final Specification<Date> afterNextToNextToNextToLastYear = isAfterOrAtTheSameTimeAs(beginningOfNextToNextToLastYear);
550         final Specification<Date> afterNextToNextToNextToNextToLastYear = isAfterOrAtTheSameTimeAs(beginningOfNextToNextToNextToLastYear);
551 
552         final Specification<Date> duringThisYear = is(not(beforeThisYear)).and(not(afterThisYear));
553         final Specification<Date> duringLastYear = is(beforeThisYear).and(afterNextToLastYear);
554         final Specification<Date> duringNextToLastYear = is(beforeLastYear).and(afterNextToNextToLastYear);
555         final Specification<Date> duringNextToNextToLastYear = is(beforeNextToLastYear).and(afterNextToNextToNextToLastYear);
556         final Specification<Date> duringNextToNextToNextToLastYear = is(beforeNextToNextToLastYear).and(afterNextToNextToNextToNextToLastYear);
557 
558         final Specification<Order> ordersFromThisYear = all(Order.class).where("orderDate", isFrom(duringThisYear));
559         assertTrue(ordersFromThisYear.isSatisfiedBy(orderFromThisYear_1));
560         assertTrue(ordersFromThisYear.isSatisfiedBy(orderFromThisYear_2));
561         assertFalse(ordersFromThisYear.isSatisfiedBy(orderFromOneYearAgo_1));
562         assertFalse(ordersFromThisYear.isSatisfiedBy(orderFromTwoYearsAgo_1));
563         final Specification<Order> ordersFromLastYear = all(Order.class).where("orderDate", isFrom(duringLastYear));
564         assertFalse(ordersFromLastYear.isSatisfiedBy(orderFromThisYear_1));
565         assertFalse(ordersFromLastYear.isSatisfiedBy(orderFromThisYear_2));
566         assertTrue(ordersFromLastYear.isSatisfiedBy(orderFromOneYearAgo_1));
567         assertFalse(ordersFromLastYear.isSatisfiedBy(orderFromTwoYearsAgo_1));
568         final Specification<Order> ordersFromNextToLastYear = all(Order.class).where("orderDate", isFrom(duringNextToLastYear));
569         assertFalse(ordersFromNextToLastYear.isSatisfiedBy(orderFromThisYear_1));
570         assertFalse(ordersFromNextToLastYear.isSatisfiedBy(orderFromThisYear_2));
571         assertFalse(ordersFromNextToLastYear.isSatisfiedBy(orderFromOneYearAgo_1));
572         assertTrue(ordersFromNextToLastYear.isSatisfiedBy(orderFromTwoYearsAgo_1));
573 
574         CompositeSpecification<Customer> goodCustomers = all(Customer.class)
575                 .where("orders", include(atLeast(2), ordersFromThisYear));
576         assertFalse(goodCustomers.isSatisfiedBy(null));
577         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
578         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
579         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
580         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
581         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
582         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
583         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
584         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
585 
586         goodCustomers = goodCustomers.and("orders", include(atLeast(2), ordersFromLastYear));
587         assertFalse(goodCustomers.isSatisfiedBy(null));
588         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
589         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
590         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
591         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
592         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
593         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
594         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
595         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
596 
597         goodCustomers = goodCustomers.and("orders", include(atLeast(2), ordersFromNextToLastYear));
598         assertFalse(goodCustomers.isSatisfiedBy(null));
599         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
600         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
601         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
602         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
603         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
604         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
605         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
606         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
607     }
608 
609 
610     @Test
611     public void testCollectionSpecification_2() {
612         Date beginningOfNextYear = getTime(thisYear + 1);
613         Date beginningOfThisYear = getTime(thisYear);
614         Date beginningOfLastYear = getTime(thisYear - 1);
615         Date beginningOfTwoYearsAgo = getTime(thisYear - 2);
616 
617         Specification<Date> afterThisYear = isAfterOrAtTheSameTimeAs(beginningOfNextYear);
618         Specification<Date> beforeThisYear = isBefore(beginningOfThisYear);
619         Specification<Date> beforeLastYear = isBefore(beginningOfLastYear);
620         Specification<Date> beforeTheYearBeforeLastYear = isBefore(beginningOfTwoYearsAgo);
621 
622         Specification<Date> duringThisYear = is(not(beforeThisYear)).and(not(afterThisYear));
623         Specification<Date> duringLastYear = is(not(beforeLastYear)).and(beforeThisYear);
624         Specification<Date> duringTheYearBeforeLastYear = is(not(beforeTheYearBeforeLastYear)).and(beforeLastYear);
625 
626         Specification<Order> ordersFromThisYear = all(Order.class).where("orderDate", isFrom(duringThisYear));
627         Specification<Order> ordersFromLastYear = all(Order.class).where("orderDate", isFrom(duringLastYear));
628         Specification<Order> ordersFromTheYearBeforeLastYear = all(Order.class).where("orderDate", isFrom(duringTheYearBeforeLastYear));
629 
630         CompositeSpecification<Customer> goodCustomers = all(Customer.class)
631                 .where("orders", include(atLeast(2), ordersFromThisYear))
632                 .and("orders", include(atLeast(2), ordersFromLastYear))
633                 .and("orders", include(atLeast(2), ordersFromTheYearBeforeLastYear));
634         assertFalse(goodCustomers.isSatisfiedBy(null));
635         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
636         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
637         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
638         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
639         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
640         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
641         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
642         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
643 
644         // More specifications
645         Date aMonthAgo = getTime(thisYear, thisMonth - 1, thisDate);
646 
647         Specification<OrderLine> cancelledOrderLines = all(OrderLine.class).where("cancelled", isTrue());
648         Specification<OrderLine> pendingOrderLines = all(OrderLine.class).where("paymentReceived", isFalse());
649 
650         Specification<Order> badOrders = all(Order.class).where("orderLines", includeAPercentageOf(atLeast(25), cancelledOrderLines));
651         Specification<Order> pendingOrders = all(Order.class).where("orderDate", isBefore(aMonthAgo)).and("orderLines", includeAPercentageOf(atLeast(50), pendingOrderLines));
652 
653         // Our 'good' customers are our 'good/regular' and not marked as 'bad' customers
654         Specification<Customer> badCustomers = all(Customer.class)
655                 .where("orders", not(includeAPercentageOf(atMost(20), badOrders))) // Just to show negated collection specifications...
656                 .or("orders", includeAPercentageOf(moreThan(10), pendingOrders));
657 
658         goodCustomers = goodCustomers.and(not(badCustomers));
659 
660         assertFalse(goodCustomers.isSatisfiedBy(null));
661         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
662         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
663         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
664         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
665         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
666         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
667         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
668         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
669 
670         badCustomers = all(Customer.class)
671                 .where("orders", includeAPercentageOf(moreThan(20), badOrders))
672                 .or("orders", includeAPercentageOf(moreThan(10), pendingOrders));
673 
674         goodCustomers = goodCustomers.and(not(badCustomers));
675 
676         assertFalse(goodCustomers.isSatisfiedBy(null));
677         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
678         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
679         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
680         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
681         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
682         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
683         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
684         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
685 
686         Specification<OrderLine> activeOrderLines = all(OrderLine.class).where("cancelled", isFalse());
687         Specification<Order> consistentOrders = all(Order.class).where("orderLines", includeAPercentageOf(atLeast(75), not(cancelledOrderLines))); // Just to show negated specs
688 
689         badCustomers = all(Customer.class)
690                 .where("orders", includeAPercentageOf(not(moreThan(80)), consistentOrders))
691                 .or("orders", includeAPercentageOf(moreThan(10), pendingOrders));
692         goodCustomers = goodCustomers.and(not(badCustomers));
693 
694         assertFalse(goodCustomers.isSatisfiedBy(null));
695         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithNoOrders));
696         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithOneOrder));
697         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithTwoOrders));
698         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithThreeOrders));
699         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFourOrders));
700         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithFiveOrders));
701         assertTrue(goodCustomers.isSatisfiedBy(aCustomerWithSixOrders));
702         assertFalse(goodCustomers.isSatisfiedBy(aCustomerWithEightOrders));
703     }
704 
705 
706     // TODO: now go vice verca - specify all orders having "good customers"... :-)
707     @Test
708     public void testOrdersWithGoodCustomers() {}
709 }