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;
17  
18  
19  import java.util.Date;
20  
21  import org.junit.Test;
22  
23  import net.sourceforge.domian.entity.Entity;
24  import net.sourceforge.domian.test.domain.Customer;
25  import net.sourceforge.domian.test.domain.Order;
26  import net.sourceforge.domian.test.domain.VipCustomer;
27  
28  import static net.sourceforge.domian.specification.CollectionSpecification.CollectionSpecificationScope.SIZE;
29  import static net.sourceforge.domian.specification.ParameterizedSpecification.createParameterizedSpecification;
30  import static net.sourceforge.domian.specification.SpecificationFactory.all;
31  import static net.sourceforge.domian.specification.SpecificationFactory.allInstancesOfType;
32  import static net.sourceforge.domian.specification.SpecificationFactory.allObjects;
33  import static net.sourceforge.domian.specification.SpecificationFactory.before;
34  import static net.sourceforge.domian.specification.SpecificationFactory.equalTo;
35  import static net.sourceforge.domian.specification.SpecificationFactory.exactly;
36  import static net.sourceforge.domian.specification.SpecificationFactory.greaterThanOrEqualTo;
37  import static net.sourceforge.domian.specification.SpecificationFactory.is;
38  import static net.sourceforge.domian.specification.SpecificationFactory.isAfter;
39  import static net.sourceforge.domian.specification.SpecificationFactory.isAfterOrAtTheSameTimeAs;
40  import static net.sourceforge.domian.specification.SpecificationFactory.isAtTheSameTimeAs;
41  import static net.sourceforge.domian.specification.SpecificationFactory.isBefore;
42  import static net.sourceforge.domian.specification.SpecificationFactory.isEqualTo;
43  import static net.sourceforge.domian.specification.SpecificationFactory.isGreaterThanOrEqualTo;
44  import static net.sourceforge.domian.specification.SpecificationFactory.lessThan;
45  import static net.sourceforge.domian.specification.SpecificationFactory.not;
46  import static net.sourceforge.domian.test.domain.Customer.Gender;
47  import static net.sourceforge.domian.test.domain.Customer.Gender.FEMALE;
48  import static net.sourceforge.domian.test.domain.Customer.Gender.MALE;
49  import static net.sourceforge.domian.test.domain.Testdata.today;
50  import static net.sourceforge.domian.test.domain.Testdata.tomorrow;
51  import static net.sourceforge.domian.test.domain.Testdata.twoDaysAgo;
52  import static net.sourceforge.domian.test.domain.Testdata.yesterday;
53  import static net.sourceforge.domian.util.DateUtils.getTime;
54  import static org.junit.Assert.assertEquals;
55  import static org.junit.Assert.assertFalse;
56  import static org.junit.Assert.assertTrue;
57  import static org.junit.Assert.fail;
58  
59  
60  /**
61   * Specification subsumption lemmas:
62   * <p/>
63   * <b>1.</b><br/>
64   * From original article:<br/>
65   * Given <code>SpecX = specA AND specB</code> then:
66   * <ol>
67   * <li/>specX.isSpecialCaseOf(specA)
68   * <li/>specX.isSpecialCaseOf(specB)
69   * </ol>
70   * Given <code>SpecX = specA OR specB</code> then:
71   * <ol>
72   * <li/>specX.isGeneralizationOf(specA)
73   * <li/>specX.isGeneralizationOf(specB)
74   * </ol>
75   * <b>2.</b>
76   * <ul>
77   * <li/>IF specA.isSatisfiedBy(candidate) AND specB.isGeneralizationOf(specA)    => specB.isSatisfiedBy(candidate)
78   * <li/>IF specA.isSatisfiedBy(candidate) AND NOT specB.isSatisfiedBy(candidate) => NOT specB.isGeneralizationOf(specA)
79   * </ul>
80   * <b>3.</b><br/>
81   * IF specA consists only of ConjunctionSpecifications AND includes ALL OF the field values of specB (which also only have ConjunctionSpecifications) then:
82   * <ul>
83   * <li/>specA.isGeneralizationOf(specB) if specB has more field values in conjunction with its other ones
84   * <li/>specA.isSpecialCaseOf(specB) if it has more field values in conjunction with its other ones AND specB has no more fields
85   * <li/>...ehhh
86   * </ul>
87   * <b>4.</b><br/>
88   * Given:
89   * <ol>
90   * <li/>Set K consisting of candidates specified by specA : specA.isSatisfiedBy(candidate)
91   * <li/>Set L consisting of candidates specified by specB : specB.isSatisfiedBy(candidate)
92   * </ol>
93   * Then:
94   * <ul>
95   * <li/>if specA.isGeneralizationOf(specB) => Set K contains Set L [Set K UNION Set L = Set K]
96   * <li/>if specA.isSpecializationOf(specB) => Set L contains Set K [Set K UNION Set L = Set L]
97   * </ul>
98   */
99  public class SubsumptionTest {
100 
101     /**
102      * Lemma 1.<br/>
103      * From original article:<br/>
104      * Given <code>SpecX = specA AND specB</code> then:
105      * <ol>
106      * <li/>specX.isSpecialCaseOf(specA)
107      * <li/>specX.isSpecialCaseOf(specB)
108      * </ol>
109      * Given <code>SpecX = specA OR specB</code>
110      * Then
111      * <ol>
112      * <li/>specX.isGeneralizationOf(specA)
113      * <li/>specX.isGeneralizationOf(specB)
114      * </ol>
115      */
116     public static void assertLemma1(final CompositeSpecification specA,
117                                     final CompositeSpecification specB) {
118         Specification combinedSpec;
119 
120         combinedSpec = specA.and(specB);
121         assertTrue(combinedSpec.isSpecialCaseOf(specA));
122         assertTrue(combinedSpec.isSpecialCaseOf(specB));
123         combinedSpec = specA.or(specB);
124         assertTrue(combinedSpec.isGeneralizationOf(specA));
125         assertTrue(combinedSpec.isGeneralizationOf(specB));
126 
127         combinedSpec = specB.and(specA);
128         assertTrue(combinedSpec.isSpecialCaseOf(specA));
129         assertTrue(combinedSpec.isSpecialCaseOf(specB));
130         combinedSpec = specB.or(specA);
131         assertTrue(combinedSpec.isGeneralizationOf(specA));
132         assertTrue(combinedSpec.isGeneralizationOf(specB));
133     }
134 
135 
136     /**
137      * Lemma 1.<br/>
138      * From original article:<br/>
139      * Given <code>SpecX = specA OR specB</code>
140      * Then
141      * <ol>
142      * <li/>specX.isGeneralizationOf(specA)
143      * <li/>specX.isGeneralizationOf(specB)
144      * </ol>
145      */
146     public static void assertLemma1_DisjunctionOnly(final CompositeSpecification specA,
147                                                     final CompositeSpecification specB) {
148         Specification combinedSpec;
149         combinedSpec = specA.or(specB);
150         assertTrue(combinedSpec.isGeneralizationOf(specA));
151         assertTrue(combinedSpec.isGeneralizationOf(specB));
152 
153         combinedSpec = specB.or(specA);
154         assertTrue(combinedSpec.isGeneralizationOf(specA));
155         assertTrue(combinedSpec.isGeneralizationOf(specB));
156     }
157 
158 
159     /**
160      * Lemma 2.
161      * <ul>
162      * <li/>IF specA.isSatisfiedBy(candidate) AND specB.isGeneralizationOf(specA)    => specB.isSatisfiedBy(candidate)
163      * <li/>IF specA.isSatisfiedBy(candidate) AND NOT specB.isSatisfiedBy(candidate) => NOT specB.isGeneralizationOf(specA)
164      * </ul>
165      */
166     public static <T> boolean assertLemma2(final Specification<T> specA,
167                                            final Specification<T> specB,
168                                            final T candidateObject) {
169         if (specA.isSatisfiedBy(candidateObject) && specB.isGeneralizationOf(specA)) {
170             return specB.isSatisfiedBy(candidateObject);
171         }
172         if (specB.isSatisfiedBy(candidateObject) && specA.isGeneralizationOf(specB)) {
173             return specA.isSatisfiedBy(candidateObject);
174         }
175 
176         if (specA.isSatisfiedBy(candidateObject) && !(specB.isSatisfiedBy(candidateObject))) {
177             return !(specB.isGeneralizationOf(specA));
178         }
179         if (specB.isSatisfiedBy(candidateObject) && !(specA.isSatisfiedBy(candidateObject))) {
180             return !(specA.isGeneralizationOf(specB));
181         }
182         return true;
183     }
184 
185 
186     //
187     // Value specifications - generalizationOf
188     //
189     @Test
190     public void shouldNotAcceptNull() {
191         try {
192             equalTo(101L).isGeneralizationOf(null);
193             fail("Should have thrown exception");
194 
195         } catch (IllegalArgumentException e) {
196             String expectedMessage = "Specification parameter cannot be null";
197             String actualMessage = e.getMessage();
198             assertEquals(expectedMessage, actualMessage);
199         }
200     }
201 
202 
203     @Test
204     public void shouldBeReflexive() {
205         Specification rawDateSpec = equalTo(tomorrow);
206         Specification anotherRawDateSpec = equalTo(tomorrow);
207         assertTrue(rawDateSpec.isGeneralizationOf(rawDateSpec));
208         assertTrue(rawDateSpec.isGeneralizationOf(anotherRawDateSpec));
209 
210         Specification<Integer> numberSpec = equalTo(42);
211         Specification rawNumberSpec = equalTo(42);
212         assertTrue(numberSpec.isGeneralizationOf(numberSpec));
213         assertTrue(numberSpec.isGeneralizationOf(rawNumberSpec));
214     }
215 
216 
217     @Test
218     public void shouldNotAcceptSpecificationOfDifferentType() {
219         Specification rawDateSpec = equalTo(new Date());
220         Specification rawNumberSpec = equalTo(42);
221         Specification rawCustomerSpec = all(Customer.class);
222 
223         assertFalse(rawDateSpec.isGeneralizationOf(rawNumberSpec));
224         assertFalse(rawNumberSpec.isGeneralizationOf(rawDateSpec));
225         assertFalse(rawDateSpec.isGeneralizationOf(rawCustomerSpec));
226         assertFalse(rawNumberSpec.isGeneralizationOf(rawCustomerSpec));
227     }
228 
229 
230     @Test
231     public void valueBoundSpecificationCannotBeGeneralizationOfEachOther() {
232         Specification<Date> todaySpec = equalTo(today);
233         Specification<Date> yesterdaySpec = equalTo(yesterday);
234 
235         assertFalse(todaySpec.isGeneralizationOf(yesterdaySpec));
236         assertFalse(yesterdaySpec.isGeneralizationOf(todaySpec));
237 
238         Specification<Integer> lessThan10 = lessThan(10);
239         Specification<Integer> greaterThanOrEqualTo1000 = greaterThanOrEqualTo(1000);
240 
241         assertFalse(lessThan10.isGeneralizationOf(greaterThanOrEqualTo1000));
242         assertFalse(greaterThanOrEqualTo1000.isGeneralizationOf(lessThan10));
243     }
244 
245 
246     @Test
247     public void checkRestOfValueBoundSpecifications() {
248         Specification<Date> todaySpec = equalTo(today);
249 
250         // NotNullSpecification stands a bit out here...
251         Specification notNullSpec = new NotNullSpecification(Object.class);
252         assertTrue(notNullSpec.isGeneralizationOf(notNullSpec));
253         assertTrue(notNullSpec.isGeneralizationOf(todaySpec));
254 
255         // The rest behaves similarly...
256         Specification<String> blankStringSpec = new DefaultValueSpecification<String>(String.class);
257         assertTrue(blankStringSpec.isGeneralizationOf(blankStringSpec));
258         assertFalse(blankStringSpec.isGeneralizationOf(exactly("Vienna")));
259 
260         Specification dateStringSpec = new DateStringSpecification("20070919");
261         assertTrue(dateStringSpec.isGeneralizationOf(dateStringSpec));
262         assertFalse(dateStringSpec.isGeneralizationOf(todaySpec));
263 
264         Specification enumNameStringSpec = new EnumNameStringSpecification(Gender.class);
265         assertTrue(enumNameStringSpec.isGeneralizationOf(enumNameStringSpec));
266         assertFalse(enumNameStringSpec.isGeneralizationOf(todaySpec));
267 
268         Specification regularExpressionMatcherStringSpec = new RegularExpressionMatcherStringSpecification("[^abc]");
269         assertTrue(regularExpressionMatcherStringSpec.isGeneralizationOf(regularExpressionMatcherStringSpec));
270         assertFalse(regularExpressionMatcherStringSpec.isGeneralizationOf(todaySpec));
271 
272         Specification wildcardExpressionMatcherStringSpec = new WildcardExpressionMatcherStringSpecification("j?h*y");
273         assertTrue(wildcardExpressionMatcherStringSpec.isGeneralizationOf(wildcardExpressionMatcherStringSpec));
274         assertFalse(wildcardExpressionMatcherStringSpec.isGeneralizationOf(todaySpec));
275 
276         Specification wildcardExpressionMatcherIgnoreCaseStringSpec = new WildcardExpressionMatcherIgnoreCaseStringSpecification("j?h*y");
277         assertTrue(wildcardExpressionMatcherIgnoreCaseStringSpec.isGeneralizationOf(wildcardExpressionMatcherIgnoreCaseStringSpec));
278         assertFalse(wildcardExpressionMatcherIgnoreCaseStringSpec.isGeneralizationOf(todaySpec));
279 
280         Specification parameterizedSpec = createParameterizedSpecification(Order.class, "orderId", new NotNullSpecification(Long.class));
281         assertTrue(parameterizedSpec.isGeneralizationOf(parameterizedSpec));
282         assertFalse(parameterizedSpec.isGeneralizationOf(todaySpec));
283 
284         Specification collectionSpec = new CollectionSpecification(SIZE, isGreaterThanOrEqualTo(42));
285         assertTrue(collectionSpec.isGeneralizationOf(collectionSpec));
286         assertFalse(collectionSpec.isGeneralizationOf(todaySpec));
287     }
288 
289 
290     //
291     // Value specifications - specialCaseOf
292     //
293     @Test
294     public void shouldNotAcceptNull_specialCaseOf() {
295         try {
296             equalTo(101L).isSpecialCaseOf(null);
297             fail("Should have thrown exception");
298 
299         } catch (IllegalArgumentException e) {
300             String expectedMessage = "Specification parameter cannot be null";
301             String actualMessage = e.getMessage();
302             assertEquals(expectedMessage, actualMessage);
303         }
304     }
305 
306 
307     @Test
308     public void shouldBeReflexive_specialCaseOf() {
309         Date date = getTime(2008);
310         Specification rawDateSpec = equalTo(date);
311         Specification anotherRawDateSpec = equalTo(date);
312         assertTrue(rawDateSpec.isSpecialCaseOf(rawDateSpec));
313         assertTrue(rawDateSpec.isSpecialCaseOf(anotherRawDateSpec));
314 
315         Specification<Integer> numberSpec = equalTo(42);
316         Specification rawNumberSpec = equalTo(42);
317         assertTrue(numberSpec.isSpecialCaseOf(numberSpec));
318         assertTrue(numberSpec.isSpecialCaseOf(rawNumberSpec));
319     }
320 
321 
322     @Test
323     public void shouldNotAcceptSpecificationOfDifferentType_specialCaseOf() {
324         Specification rawDateSpec = equalTo(new Date());
325         Specification rawNumberSpec = equalTo(42);
326         Specification rawCustomerSpec = all(Customer.class);
327 
328         assertFalse(rawDateSpec.isSpecialCaseOf(rawNumberSpec));
329         assertFalse(rawNumberSpec.isSpecialCaseOf(rawDateSpec));
330         assertFalse(rawDateSpec.isSpecialCaseOf(rawCustomerSpec));
331         assertFalse(rawNumberSpec.isSpecialCaseOf(rawCustomerSpec));
332     }
333 
334 
335     @Test
336     public void valueBoundSpecificationJustCannotBeSpecialCasesOfEachOther_specialCaseOf() {
337         Specification<Date> todaySpec = equalTo(today);
338         Specification<Date> yesterdaySpec = equalTo(yesterday);
339 
340         assertFalse(todaySpec.isSpecialCaseOf(yesterdaySpec));
341         assertFalse(yesterdaySpec.isSpecialCaseOf(todaySpec));
342 
343         Specification<Integer> lessThan10 = lessThan(10);
344         Specification<Integer> greaterThanOrEqualTo1000 = greaterThanOrEqualTo(1000);
345 
346         assertFalse(lessThan10.isSpecialCaseOf(greaterThanOrEqualTo1000));
347         assertFalse(greaterThanOrEqualTo1000.isSpecialCaseOf(lessThan10));
348     }
349 
350 
351     @Test
352     public void checkRestOfValueBoundSpecifications_specialCaseOf() {
353         Specification<Date> todaySpec = equalTo(today);
354 
355         Specification notNullSpec = new NotNullSpecification(Object.class);
356         assertTrue(notNullSpec.isSpecialCaseOf(notNullSpec));
357         assertFalse(notNullSpec.isSpecialCaseOf(todaySpec));
358 
359         Specification<String> blankStringSpec = new DefaultValueSpecification<String>(String.class);
360         assertTrue(blankStringSpec.isSpecialCaseOf(blankStringSpec));
361         assertFalse(blankStringSpec.isSpecialCaseOf(exactly("Vienna")));
362 
363         Specification dateStringSpec = new DateStringSpecification("20070919");
364         assertTrue(dateStringSpec.isSpecialCaseOf(dateStringSpec));
365         assertFalse(dateStringSpec.isSpecialCaseOf(todaySpec));
366 
367         Specification enumNameStringSpec = new EnumNameStringSpecification(Gender.class);
368         assertTrue(enumNameStringSpec.isSpecialCaseOf(enumNameStringSpec));
369         assertFalse(enumNameStringSpec.isSpecialCaseOf(todaySpec));
370 
371         Specification regularExpressionMatcherStringSpec = new RegularExpressionMatcherStringSpecification("[^abc]");
372         assertTrue(regularExpressionMatcherStringSpec.isSpecialCaseOf(regularExpressionMatcherStringSpec));
373         assertFalse(regularExpressionMatcherStringSpec.isSpecialCaseOf(todaySpec));
374 
375         Specification wildcardExpressionMatcherStringSpec = new WildcardExpressionMatcherStringSpecification("j?h*y");
376         assertTrue(wildcardExpressionMatcherStringSpec.isSpecialCaseOf(wildcardExpressionMatcherStringSpec));
377         assertFalse(wildcardExpressionMatcherStringSpec.isSpecialCaseOf(todaySpec));
378 
379         Specification wildcardExpressionMatcherIgnoreCaseStringSpec = new WildcardExpressionMatcherIgnoreCaseStringSpecification("j?h*y");
380         assertTrue(wildcardExpressionMatcherIgnoreCaseStringSpec.isSpecialCaseOf(wildcardExpressionMatcherIgnoreCaseStringSpec));
381         assertFalse(wildcardExpressionMatcherIgnoreCaseStringSpec.isSpecialCaseOf(todaySpec));
382 
383         Specification parameterizedSpec = createParameterizedSpecification(Order.class, "orderId", new NotNullSpecification(Long.class));
384         assertTrue(parameterizedSpec.isSpecialCaseOf(parameterizedSpec));
385         assertFalse(parameterizedSpec.isSpecialCaseOf(todaySpec));
386 
387         Specification collectionSpec = new CollectionSpecification(SIZE, isGreaterThanOrEqualTo(42));
388         assertTrue(collectionSpec.isSpecialCaseOf(collectionSpec));
389         assertFalse(collectionSpec.isSpecialCaseOf(todaySpec));
390     }
391 
392 
393     //
394     // Composite specifications
395     //
396     @Test
397     public void shouldNotAcceptNull_compositeSpecification() {
398         try {
399             all(Customer.class).isGeneralizationOf(null);
400             fail("Should have thrown exception");
401 
402         } catch (IllegalArgumentException e) {
403             String expectedMessage = "Specification parameter cannot be null";
404             String actualMessage = e.getMessage();
405             assertEquals(expectedMessage, actualMessage);
406         }
407         try {
408             all(Customer.class).isSpecialCaseOf(null);
409             fail("Should have thrown exception");
410 
411         } catch (IllegalArgumentException e) {
412             String expectedMessage = "Specification parameter cannot be null";
413             String actualMessage = e.getMessage();
414             assertEquals(expectedMessage, actualMessage);
415         }
416     }
417 
418 
419     @Test
420     public void basicLemma4Stuff() {
421         CompositeSpecification<Customer> customerSpec1 = all(Customer.class).where("name", is("Tommy"));
422         CompositeSpecification<Customer> customerSpec2 = all(Customer.class).where("membershipDate", isBefore(yesterday));
423         assertLemma1(customerSpec1, customerSpec2);
424         customerSpec1 = all(Customer.class).where("name", is("Tommy")).or("name", is("Johnny"));
425         customerSpec2 = all(Customer.class).where("membershipDate", isBefore(yesterday)).or("name", is("Johnny"));
426         assertLemma1(customerSpec1, customerSpec2);
427     }
428 
429 
430     @Test
431     public void shouldBeReflexive_compositeSpecification() {
432         Specification<Integer> integerSpec = all(Integer.class);
433         Specification rawIntegerSpec = all(Integer.class);
434         assertTrue(integerSpec.isGeneralizationOf(integerSpec));
435         assertTrue(integerSpec.isGeneralizationOf(rawIntegerSpec));
436         assertTrue(integerSpec.isSpecialCaseOf(integerSpec));
437         assertTrue(integerSpec.isSpecialCaseOf(rawIntegerSpec));
438         assertLemma1((CompositeSpecification) integerSpec, (CompositeSpecification) rawIntegerSpec);
439 
440         Specification rawCustomerSpec = all(Customer.class).where("name", isEqualTo("Tommy"));
441         Specification anotherRawCustomerSpec = all(Customer.class).where("name", isEqualTo("Tommy"));
442         assertTrue(rawCustomerSpec.isGeneralizationOf(rawCustomerSpec));
443         assertTrue(rawCustomerSpec.isGeneralizationOf(anotherRawCustomerSpec));
444         assertTrue(rawCustomerSpec.isSpecialCaseOf(rawCustomerSpec));
445         assertTrue(rawCustomerSpec.isSpecialCaseOf(anotherRawCustomerSpec));
446         assertLemma1((CompositeSpecification) rawCustomerSpec, (CompositeSpecification) anotherRawCustomerSpec);
447 
448         Specification customerSpec = all(Customer.class).where("name", isEqualTo("Tommy"));
449         Specification anotherCustomerSpec = all(Customer.class).where("name", isEqualTo("Tommy"));
450         assertTrue(customerSpec.isGeneralizationOf(customerSpec));
451         assertTrue(customerSpec.isGeneralizationOf(anotherCustomerSpec));
452         assertTrue(customerSpec.isSpecialCaseOf(customerSpec));
453         assertTrue(customerSpec.isSpecialCaseOf(anotherCustomerSpec));
454         assertLemma1((CompositeSpecification) customerSpec, (CompositeSpecification) anotherCustomerSpec);
455     }
456 
457 
458     @Test
459     public void notNullSpecificationShouldBeOnTopOfEverything() {
460         assertTrue(new NotNullSpecification(Object.class).isGeneralizationOf(new NotNullSpecification(Object.class)));
461         assertTrue(new NotNullSpecification(Object.class).isSpecialCaseOf(new NotNullSpecification(Object.class)));
462 
463         assertTrue(new NotNullSpecification(Order.class).isGeneralizationOf(all(Order.class)));
464         assertFalse(new NotNullSpecification(Order.class).isSpecialCaseOf(all(Order.class)));
465 
466         assertTrue(new NotNullSpecification(Object.class).isGeneralizationOf(isEqualTo(1)));
467         assertFalse(new NotNullSpecification(Date.class).isSpecialCaseOf(isAfter(new Date())));
468 
469         assertTrue(new NotNullSpecification<Object>(Object.class).isGeneralizationOf(new NotNullSpecification<Object>(Object.class)));
470         assertTrue(new NotNullSpecification<Object>(Object.class).isSpecialCaseOf(new NotNullSpecification<Object>(Object.class)));
471 
472         assertTrue(new NotNullSpecification<Object>(Object.class).isGeneralizationOf(all(Order.class)));
473         assertFalse(new NotNullSpecification<Order>(Order.class).isSpecialCaseOf(all(Order.class)));
474 
475         assertTrue(new NotNullSpecification<Object>(Object.class).isGeneralizationOf(isEqualTo(1)));
476         assertFalse(new NotNullSpecification<Date>(Date.class).isSpecialCaseOf(isAfter(new Date())));
477     }
478 
479 
480     @Test
481     public void entitySpecificationShouldBeOnTopOfAlmostEverything() {
482         assertTrue(allInstancesOfType(Entity.class).isGeneralizationOf(allInstancesOfType(Order.class)));
483         //assertFalse(allInstancesOfType(Entity.class).isSpecialCaseOf(allInstancesOfType(Order.class)));       // Should not compile - OK
484         assertTrue(allInstancesOfType(Order.class).isSpecialCaseOf(allInstancesOfType(Entity.class)));
485         //assertFalse(allInstancesOfType(Order.class).isGeneralizationOf(allInstancesOfType(Entity.class)));    // Should not compile - OK
486 
487         Specification rawEntitySpec = allInstancesOfType(Entity.class);
488         Specification rawOrderSpec = allInstancesOfType(Order.class);
489 
490         assertTrue(rawEntitySpec.isGeneralizationOf(rawOrderSpec));
491         assertFalse(rawOrderSpec.isGeneralizationOf(rawEntitySpec));
492         assertFalse(rawEntitySpec.isSpecialCaseOf(rawOrderSpec));
493         assertTrue(rawOrderSpec.isSpecialCaseOf(rawEntitySpec));
494     }
495 
496 
497     @Test
498     public void shouldAcceptSubtypeSpecification_compositeSpecification() {
499         CompositeSpecification rawCustomerSpec = all(Customer.class).where("name", is("Tommy"));
500         CompositeSpecification rawVipCustomerSpec = all(VipCustomer.class)
501                 .where("name", is("Tommy"))
502                 .and("vipCustomerSince", before(yesterday));
503 
504         assertTrue(rawCustomerSpec.isGeneralizationOf(rawVipCustomerSpec));
505         assertFalse(rawCustomerSpec.isSpecialCaseOf(rawVipCustomerSpec));
506         assertFalse(rawVipCustomerSpec.isGeneralizationOf(rawCustomerSpec));
507         assertTrue(rawVipCustomerSpec.isSpecialCaseOf(rawCustomerSpec));
508 
509         CompositeSpecification<Customer> customerSpec = all(Customer.class).where("name", is("Tommy"));
510         CompositeSpecification<VipCustomer> vipCustomerSpec = all(VipCustomer.class)
511                 .where("vipCustomerSince", before(yesterday))
512                 .and("name", is("Tommy"));
513 
514         //assertFalse(vipCustomerSpec.isGeneralizationOf(customerSpec));    // Should not compile - OK
515         assertTrue(customerSpec.isGeneralizationOf(vipCustomerSpec));
516         assertTrue(vipCustomerSpec.isSpecialCaseOf(customerSpec));
517         //assertFalse(customerSpec.isSpecialCaseOf(vipCustomerSpec));       // Should not compile - OK
518 
519         assertLemma2(rawCustomerSpec, rawVipCustomerSpec, new Customer().name("Tommy"));
520         assertLemma2(rawCustomerSpec, rawVipCustomerSpec, new VipCustomer().name("Tommy"));
521         assertLemma1(customerSpec, vipCustomerSpec);
522     }
523 
524 
525     @Test
526     public void shouldNotAcceptSpecificationOfDifferentType_compositeSpecification() {
527         CompositeSpecification rawCustomerSpec = all(Customer.class);
528         CompositeSpecification rawIntegerSpec = all(Integer.class);
529 
530         assertFalse(rawCustomerSpec.isGeneralizationOf(rawIntegerSpec));
531         assertFalse(rawIntegerSpec.isGeneralizationOf(rawCustomerSpec));
532         assertFalse(rawCustomerSpec.isSpecialCaseOf(rawIntegerSpec));
533         assertFalse(rawIntegerSpec.isSpecialCaseOf(rawCustomerSpec));
534 
535         assertLemma1_DisjunctionOnly(rawCustomerSpec, rawIntegerSpec);
536     }
537 
538 
539     @Test
540     public void checkSimpleFieldSpecificationVersusTypedSpecification() {
541         Specification<Customer> allCustomerSpec = all(Customer.class);
542         Specification<Customer> maleCustomerSpec = all(Customer.class).where("gender", isEqualTo(MALE));
543         Specification<Customer> femaleCustomerSpec = all(Customer.class).where("gender", isEqualTo(FEMALE));
544 
545         assertTrue(allCustomerSpec.isGeneralizationOf(maleCustomerSpec));
546         assertTrue(allCustomerSpec.isGeneralizationOf(femaleCustomerSpec));
547         assertFalse(maleCustomerSpec.isGeneralizationOf(femaleCustomerSpec));
548 
549         assertFalse(allCustomerSpec.isSpecialCaseOf(maleCustomerSpec));
550         assertFalse(allCustomerSpec.isSpecialCaseOf(femaleCustomerSpec));
551         assertFalse(maleCustomerSpec.isSpecialCaseOf(femaleCustomerSpec));
552 
553         assertTrue(maleCustomerSpec.isSpecialCaseOf(maleCustomerSpec));
554         assertTrue(femaleCustomerSpec.isSpecialCaseOf(femaleCustomerSpec));
555         assertFalse(femaleCustomerSpec.isSpecialCaseOf(maleCustomerSpec));
556 
557         assertLemma1((CompositeSpecification) allCustomerSpec, (CompositeSpecification) maleCustomerSpec);
558 
559         // Disjoint!
560         //assertLemma1((CompositeSpecification) femaleCustomerSpec, (CompositeSpecification) maleCustomerSpec);
561     }
562 
563 
564     protected final CompositeSpecification<Customer> customers = allInstancesOfType(Customer.class);
565     protected final CompositeSpecification<Customer> pioneerCustomers = customers.where("membershipDate", isBefore(twoDaysAgo));
566     protected final CompositeSpecification<Customer> newCustomers = customers.where("membershipDate", isAfterOrAtTheSameTimeAs(twoDaysAgo));
567 
568     @Test
569     public void disjunctSpecificationsShouldNotBeGeneralizationNorSpecialization() {
570         assertFalse(newCustomers.isSpecialCaseOf(pioneerCustomers));
571         assertFalse(newCustomers.isGeneralizationOf(pioneerCustomers));
572         assertFalse(pioneerCustomers.isSpecialCaseOf(newCustomers));
573         assertFalse(pioneerCustomers.isGeneralizationOf(newCustomers));
574     }
575 
576 
577     @Test
578     public void checkSimpleConjunctionSpecifications() {
579         Specification<Customer> spec = all(Customer.class)
580                 .where("gender", isEqualTo(MALE))
581                 .and("name", is("John"));
582         Specification<Customer> specialCaseOfSpec = all(Customer.class)
583                 .where("gender", isEqualTo(MALE))
584                 .and("membershipDate", isAtTheSameTimeAs(today))
585                 .and("name", is("John"));
586 
587         assertTrue(spec.isGeneralizationOf(specialCaseOfSpec));
588         assertFalse(spec.isSpecialCaseOf(specialCaseOfSpec));
589 
590         assertFalse(specialCaseOfSpec.isGeneralizationOf(spec));
591         assertTrue(specialCaseOfSpec.isSpecialCaseOf(spec));
592 
593         assertLemma1((CompositeSpecification) spec, (CompositeSpecification) specialCaseOfSpec);
594     }
595 
596 
597     @Test
598     public void checkSimpleDisjunctionSpecifications() {
599         CompositeSpecification<Customer> spec = all(Customer.class)
600                 .where("gender", is(MALE))
601                 .and("name", is("John"));
602 
603         CompositeSpecification<Customer> moreGeneralDisjunctionSpec = all(Customer.class)
604                 .where("gender", is(MALE))
605                 .and("name", is("John"))
606                 .or("membershipDate", isAtTheSameTimeAs(today));
607 
608         assertFalse(spec.isGeneralizationOf(moreGeneralDisjunctionSpec));
609         assertTrue(moreGeneralDisjunctionSpec.isGeneralizationOf(spec));
610         assertTrue(spec.isSpecialCaseOf(moreGeneralDisjunctionSpec));
611         assertFalse(moreGeneralDisjunctionSpec.isSpecialCaseOf(spec));
612         assertFalse(spec.isDisjointWith(moreGeneralDisjunctionSpec));                       // extra
613         assertLemma1(spec, moreGeneralDisjunctionSpec);
614 
615         CompositeSpecification<Customer> customerSpec = all(Customer.class);
616         CompositeSpecification<Customer> maleCustomerSpec = all(Customer.class).where("gender", isEqualTo(MALE));
617         CompositeSpecification<Customer> aCustomerNamedJohnSpec = all(Customer.class).where("name", is("John")).and("gender", isEqualTo(MALE));
618         CompositeSpecification<Customer> aCustomerNamedPeterSpec = all(Customer.class).where("gender", isEqualTo(MALE)).and("name", is("Peter"));
619         CompositeSpecification<Customer> aCustomerNamedJohnOrPeterSpec = aCustomerNamedJohnSpec.or(aCustomerNamedPeterSpec);
620 
621         assertTrue(customerSpec.isGeneralizationOf(aCustomerNamedJohnSpec));
622         assertFalse(customerSpec.isSpecialCaseOf(aCustomerNamedJohnSpec));
623         assertTrue(customerSpec.isGeneralizationOf(aCustomerNamedJohnOrPeterSpec));
624         assertFalse(customerSpec.isSpecialCaseOf(aCustomerNamedJohnOrPeterSpec));
625 
626         assertTrue(maleCustomerSpec.isGeneralizationOf(aCustomerNamedJohnSpec));
627         assertFalse(maleCustomerSpec.isSpecialCaseOf(aCustomerNamedJohnSpec));
628         assertTrue(maleCustomerSpec.isGeneralizationOf(aCustomerNamedJohnOrPeterSpec));
629         assertFalse(maleCustomerSpec.isSpecialCaseOf(aCustomerNamedJohnOrPeterSpec));
630 
631         assertFalse(aCustomerNamedJohnSpec.isGeneralizationOf(aCustomerNamedJohnOrPeterSpec));
632         assertTrue(aCustomerNamedJohnSpec.isSpecialCaseOf(aCustomerNamedJohnOrPeterSpec));
633         assertFalse(aCustomerNamedJohnSpec.isGeneralizationOf(aCustomerNamedJohnOrPeterSpec));
634         assertTrue(aCustomerNamedJohnSpec.isSpecialCaseOf(aCustomerNamedJohnOrPeterSpec));
635 
636         assertTrue(aCustomerNamedJohnOrPeterSpec.isGeneralizationOf(aCustomerNamedJohnSpec));
637         assertFalse(aCustomerNamedJohnOrPeterSpec.isSpecialCaseOf(aCustomerNamedJohnSpec));
638         assertTrue(aCustomerNamedJohnOrPeterSpec.isGeneralizationOf(aCustomerNamedJohnOrPeterSpec));
639         assertTrue(aCustomerNamedJohnOrPeterSpec.isSpecialCaseOf(aCustomerNamedJohnOrPeterSpec));
640 
641         assertFalse(aCustomerNamedJohnOrPeterSpec.isGeneralizationOf(customerSpec));
642         assertTrue(aCustomerNamedJohnOrPeterSpec.isSpecialCaseOf(customerSpec));
643         assertFalse(aCustomerNamedJohnOrPeterSpec.isGeneralizationOf(maleCustomerSpec));
644         assertTrue(aCustomerNamedJohnOrPeterSpec.isSpecialCaseOf(maleCustomerSpec));
645 
646         assertTrue(aCustomerNamedPeterSpec.isDisjointWith(aCustomerNamedJohnSpec));         // extra
647         assertFalse(aCustomerNamedPeterSpec.isDisjointWith(aCustomerNamedJohnOrPeterSpec)); // extra
648         assertFalse(aCustomerNamedJohnSpec.isDisjointWith(aCustomerNamedJohnOrPeterSpec));  // extra
649         assertLemma1(aCustomerNamedPeterSpec, aCustomerNamedJohnOrPeterSpec);
650     }
651 
652 
653     @Test
654     public void shouldDealWithNegatedSpecifications_1() {
655         Specification allObjects = allObjects();
656         Specification<Customer> customer = all(Customer.class);
657         Specification<Customer> notCustomer = not(customer);
658         Specification<Customer> customerNotNamedTommy = all(Customer.class).where("name", is(not("Tommy")));
659 
660         assertTrue(allObjects.isGeneralizationOf(customer));
661         assertTrue(allObjects.isGeneralizationOf(notCustomer));
662         assertFalse(customer.isGeneralizationOf(allObjects));
663         assertFalse(notCustomer.isGeneralizationOf(allObjects));
664         assertFalse(allObjects.isSpecialCaseOf(customer));
665         assertFalse(allObjects.isSpecialCaseOf(notCustomer));
666         assertTrue(customer.isSpecialCaseOf(allObjects));
667         assertTrue(notCustomer.isSpecialCaseOf(allObjects));
668 
669         assertFalse(notCustomer.isGeneralizationOf(customer));
670         assertFalse(notCustomer.isSpecialCaseOf(customer));
671         assertFalse(customer.isGeneralizationOf(notCustomer));
672         assertFalse(customer.isSpecialCaseOf(notCustomer));
673         assertFalse(notCustomer.isGeneralizationOf(customerNotNamedTommy));
674         assertFalse(notCustomer.isSpecialCaseOf(customerNotNamedTommy));
675         assertFalse(customerNotNamedTommy.isGeneralizationOf(notCustomer));
676         assertFalse(customerNotNamedTommy.isSpecialCaseOf(notCustomer));
677 
678         assertTrue(customer.isGeneralizationOf(customerNotNamedTommy));
679         assertFalse(customerNotNamedTommy.isGeneralizationOf(customer));
680         assertFalse(customer.isSpecialCaseOf(customerNotNamedTommy));
681         assertTrue(customerNotNamedTommy.isSpecialCaseOf(customer));
682 
683         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) customer, (CompositeSpecification<Customer>) notCustomer);
684         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) notCustomer, (CompositeSpecification<Customer>) customerNotNamedTommy);
685     }
686 
687 
688     @Test
689     public void shouldDealWithNegatedSpecifications_2() {
690         Specification<Customer> customer = all(Customer.class);
691         Specification<Customer> notCustomer = not(customer);
692         Specification<Customer> femaleOrMaleNotNamedTommyOrJohnnyCustomer = all(Customer.class).where("name", is(not("Tommy")).and(is(not("Johnny")))).or("gender", is(FEMALE));
693 
694         assertFalse(notCustomer.isGeneralizationOf(customer));
695         assertFalse(notCustomer.isSpecialCaseOf(customer));
696         assertFalse(customer.isGeneralizationOf(notCustomer));
697         assertFalse(customer.isSpecialCaseOf(notCustomer));
698         assertFalse(notCustomer.isGeneralizationOf(femaleOrMaleNotNamedTommyOrJohnnyCustomer));
699         assertFalse(notCustomer.isSpecialCaseOf(femaleOrMaleNotNamedTommyOrJohnnyCustomer));
700         assertFalse(femaleOrMaleNotNamedTommyOrJohnnyCustomer.isGeneralizationOf(notCustomer));
701         assertFalse(femaleOrMaleNotNamedTommyOrJohnnyCustomer.isSpecialCaseOf(notCustomer));
702 
703         assertTrue(customer.isGeneralizationOf(femaleOrMaleNotNamedTommyOrJohnnyCustomer));
704         assertFalse(femaleOrMaleNotNamedTommyOrJohnnyCustomer.isGeneralizationOf(customer));
705         assertFalse(customer.isSpecialCaseOf(femaleOrMaleNotNamedTommyOrJohnnyCustomer));
706         assertTrue(femaleOrMaleNotNamedTommyOrJohnnyCustomer.isSpecialCaseOf(customer));
707 
708         assertLemma1((CompositeSpecification) customer, (CompositeSpecification) femaleOrMaleNotNamedTommyOrJohnnyCustomer);
709         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) notCustomer, (CompositeSpecification<Customer>) femaleOrMaleNotNamedTommyOrJohnnyCustomer);
710     }
711 
712 
713     @Test
714     public void shouldDealWithNegatedSpecifications_3() {
715         Specification<Customer> notCustomer = not(all(Customer.class));
716         Specification<Customer> notCustomerNamedTommy = not(all(Customer.class).where("name", is("Tommy")));
717         Specification<Customer> notCustomerNotNamedTommy = not(all(Customer.class).where("name", is(not("Tommy"))));
718         Specification<Customer> notCustomerFromYesterday = not(all(Customer.class).where("membershipDate", isAtTheSameTimeAs(yesterday)));
719         Specification notOrder = not(all(Order.class));
720 
721         assertFalse(notCustomer.isGeneralizationOf(notCustomerNamedTommy));
722         assertFalse(notCustomer.isSpecialCaseOf(notCustomerNamedTommy));
723         assertFalse(notCustomerNamedTommy.isGeneralizationOf(notCustomer));
724         assertFalse(notCustomerNamedTommy.isSpecialCaseOf(notCustomer));
725 
726         assertFalse(notCustomerNamedTommy.isGeneralizationOf(notCustomerNotNamedTommy));
727         assertFalse(notCustomerNamedTommy.isSpecialCaseOf(notCustomerNotNamedTommy));
728         assertFalse(notCustomerNotNamedTommy.isGeneralizationOf(notCustomerNamedTommy));
729         assertFalse(notCustomerNotNamedTommy.isSpecialCaseOf(notCustomerNamedTommy));
730 
731         assertFalse(notCustomerNamedTommy.isGeneralizationOf(notCustomerFromYesterday));
732         assertFalse(notCustomerNamedTommy.isSpecialCaseOf(notCustomerFromYesterday));
733         assertFalse(notCustomerFromYesterday.isGeneralizationOf(notCustomerNamedTommy));
734         assertFalse(notCustomerFromYesterday.isSpecialCaseOf(notCustomerNamedTommy));
735 
736         assertFalse(notCustomer.isGeneralizationOf(notOrder));
737         assertFalse(notCustomer.isSpecialCaseOf(notOrder));
738         assertFalse(notOrder.isGeneralizationOf(notCustomer));
739         assertFalse(notOrder.isSpecialCaseOf(notCustomer));
740 
741         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) notCustomer, (CompositeSpecification<Customer>) notCustomerNamedTommy);
742         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) notCustomer, (CompositeSpecification<Customer>) notCustomerNotNamedTommy);
743         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) notCustomer, (CompositeSpecification<Customer>) notCustomerFromYesterday);
744         assertLemma1_DisjunctionOnly((CompositeSpecification<Customer>) notCustomer, (CompositeSpecification<Customer>) notOrder);
745     }
746 }