View Javadoc

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.math.BigDecimal;
20  import java.math.BigInteger;
21  import java.util.Calendar;
22  import java.util.Date;
23  import java.util.Iterator;
24  
25  import org.apache.commons.lang.Validate;
26  
27  import static java.lang.Boolean.FALSE;
28  import static java.lang.Boolean.TRUE;
29  import static net.sourceforge.domian.util.ReflectionUtils.canCastAtLeastOneWay;
30  import static net.sourceforge.domian.util.ReflectionUtils.isDate;
31  
32  
33  abstract class AbstractComparableValueBoundSpecification<T extends Comparable> extends AbstractValueBoundSpecification<T> {
34  
35      private static long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
36  
37  
38      AbstractComparableValueBoundSpecification(final T value) {
39          super(value);
40      }
41  
42  
43      @Override
44      protected Boolean isInvertible() {
45          return TRUE;
46      }
47  
48  
49      @Override
50      protected Specification<T> invert() {
51          return createValueBoundSpecification(getBinaryRelation(TRUE), this.value);
52      }
53  
54  
55      /**
56       * @return this value subtracted from the candidate value if possible;
57       *         otherwise the {@code compareTo}} value if possible;
58       *         otherwise {@code null}
59       */
60      @SuppressWarnings("unchecked")
61      protected Long getCompareValue(final T candidateOperandValue) {
62          if (candidateOperandValue instanceof Number && this.value instanceof Number) {
63              final BigDecimal thisBigDecimal = new BigDecimal(this.value.toString());
64              final BigDecimal candidateBigDecimal = new BigDecimal(candidateOperandValue.toString());
65              final BigDecimal subtractedValue = candidateBigDecimal.subtract(thisBigDecimal);
66              BigInteger subtractedBigInteger;
67              try {
68                  subtractedBigInteger = subtractedValue.toBigIntegerExact();
69              } catch (ArithmeticException e) {
70                  subtractedBigInteger = subtractedValue.unscaledValue();
71              }
72              return subtractedBigInteger.longValue();
73  
74          } else if (isDate(candidateOperandValue) && isDate(this.value)) {
75              if (isMidnight((Date) this.value) && isMidnight((Date) candidateOperandValue)) {
76                  return getDayNumberInEra((Date) candidateOperandValue) - getDayNumberInEra((Date) this.value);
77              } else {
78                  return ((Date) candidateOperandValue).getTime() - ((Date) this.value).getTime();
79              }
80  
81          } else if (candidateOperandValue instanceof Comparable) {
82              return (long) candidateOperandValue.compareTo(this.value);
83  
84          } else {
85              return null;
86          }
87      }
88  
89  
90      private static boolean isMidnight(Date date) {
91          Calendar calendar = Calendar.getInstance();
92          calendar.setTime(date);
93  
94          final int dateHour = calendar.get(Calendar.HOUR);
95          final int dateMinute = calendar.get(Calendar.MINUTE);
96          final int dateSecond = calendar.get(Calendar.SECOND);
97          final int dateMillisecond = calendar.get(Calendar.MILLISECOND);
98  
99          if (dateHour == 0
100             && dateMinute == 0
101             && dateSecond == 0
102             && dateMillisecond == 0) {
103             return true;
104         }
105         return false;
106     }
107 
108 
109     private static long getDayNumberInEra(Date date) {
110         return date.getTime() / MILLISECONDS_PER_DAY;
111     }
112 
113 
114     @Override
115     @SuppressWarnings("unchecked")
116     public Boolean isDisjointWith(final Specification<?> specification) {
117         Validate.notNull(specification, "Specification parameter cannot be null");
118 
119         // If same/equal specifications:
120         if (this == specification || this.equals(specification)) {
121             return FALSE;
122         }
123 
124         // If specifications are not of the same type:
125         if (!canCastAtLeastOneWay(this.getType(), specification.getType())) {
126             return TRUE;
127         }
128 
129         if (specification instanceof ValueBoundSpecification) {
130             final AbstractValueBoundSpecification valueBoundSpecificationCandidate = (AbstractValueBoundSpecification) specification;
131             /* @SuppressWarnings("unchecked") -> "I solemnly swear that this statement will not throw any ClassCastException!" -eirik torske- */
132             /* Specification candidate type is tested above */
133             return getDisjointDecision(valueBoundSpecificationCandidate.getBinaryRelation(),
134                                        (T) valueBoundSpecificationCandidate.getValue());
135         }
136 
137         if (specification instanceof JointDenialSpecification) {
138             // Extraordinary JointDenialSpecification purification - may result in another CompositeSpecification type
139             Specification purifiedSpec = ((AbstractSpecification) specification).purify(true);
140             if (purifiedSpec instanceof JointDenialSpecification) {
141                 Specification<?> negatedSpec = getNegatedSpecification((JointDenialSpecification<?>) purifiedSpec);
142                 if (negatedSpec instanceof ValueBoundSpecification) {
143                     final AbstractValueBoundSpecification negatedValueBoundSpecification = (AbstractValueBoundSpecification) negatedSpec;
144                     if (isOddNumber(getNumberOfLevelsOfNegations((JointDenialSpecification) purifiedSpec, 0))) {
145                         negatedSpec = negatedValueBoundSpecification.invert();
146                     } else {
147                         negatedSpec = negatedValueBoundSpecification;
148                     }
149 
150                     if (negatedSpec instanceof DisjunctionSpecification) {
151                         final AbstractCompositeSpecification compositeSpecification = (AbstractCompositeSpecification) negatedSpec;
152                         final Iterator<Specification<?>> iterator = compositeSpecification.specifications.iterator();
153                         if (compositeSpecification instanceof DisjunctionSpecification) {
154                             for (; iterator.hasNext();) {
155                                 final Specification<?> spec = iterator.next();
156                                 if (canCastAtLeastOneWay(spec.getType(), this.getType())) {
157                                     if (!this.isDisjointWith(spec)) {
158                                         return FALSE;
159                                     }
160                                 }
161                             }
162                             return TRUE;
163 
164                         } else { // ConjunctionSpecification
165                             return TRUE;
166                         }
167                     }
168 
169                     /* @SuppressWarnings("unchecked") -> "I solemnly swear that this statement will not throw any ClassCastException!" -eirik torske- */
170                     /* Specification candidate type is tested above */
171                     return getDisjointDecision(((AbstractValueBoundSpecification) negatedSpec).getBinaryRelation(),
172                                                (T) ((AbstractValueBoundSpecification) negatedSpec).getValue());
173 
174 
175                 } else if (negatedSpec instanceof DisjunctionSpecification) {
176                     final Specification resolvedSpecification;
177                     if (isOddNumber(getNumberOfLevelsOfNegations((JointDenialSpecification) specification, 0)) &&
178                         ((AbstractSpecification) specification).isInvertible()) {
179                         resolvedSpecification = ((DisjunctionSpecification) negatedSpec).invert();
180                     } else {
181                         resolvedSpecification = negatedSpec;
182                     }
183 
184                     if (resolvedSpecification instanceof ValueBoundSpecification) {
185                         return resolvedSpecification.isDisjointWith(this);
186                     }
187 
188                     // If one of combinations is NOT disjoint => return false else true
189                     for (final Object composite : ((AbstractCompositeSpecification) resolvedSpecification).specifications) {
190                         if (!this.isDisjointWith((Specification<?>) composite)) {
191                             return FALSE;
192                         }
193                     }
194                     return TRUE;
195                 }
196             } else {
197                 //throw new NotImplementedException();
198                 return this.isDisjointWith(purifiedSpec);
199             }
200         }
201 
202         if (specification instanceof DisjunctionSpecification) {
203             for (final Object composite : ((AbstractCompositeSpecification) specification).specifications) {
204                 if (!((Specification<?>) composite).isDisjointWith(this)) {
205                     return FALSE;
206                 }
207             }
208             return TRUE;
209         }
210 
211         if (specification instanceof CompositeSpecification) {
212             for (final Object composite : ((AbstractCompositeSpecification) specification).specifications) {
213                 if (!this.isDisjointWith((Specification<?>) composite)) {
214                     return FALSE;
215                 }
216             }
217             return TRUE;
218         }
219 
220         return TRUE;
221     }
222 }