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 org.apache.commons.lang.Validate;
20  import org.apache.commons.lang.builder.EqualsBuilder;
21  import org.apache.commons.lang.builder.HashCodeBuilder;
22  
23  import static java.lang.Boolean.FALSE;
24  import static java.lang.Boolean.TRUE;
25  import static net.sourceforge.domian.specification.RelationalOperator.DO_INVERT;
26  import static net.sourceforge.domian.util.ReflectionUtils.canCastAtLeastOneWay;
27  import static net.sourceforge.domian.util.ReflectionUtils.cloneOrDeepCopyIfNotImmutable;
28  import static net.sourceforge.domian.util.ReflectionUtils.isEntity;
29  
30  
31  /**
32   * An abstract superclass for <i>value bound specifications</i>.
33   * <p/>
34   * This class defines the data structure of Domian value bound specifications, consisting of:
35   * <ul>
36   * <li/>The type of this {@link Specification} (parameterized as <code>T</code>)
37   * <li/>The object/value of type <code>T</code> that is bound to this specification
38   * <li/>A strategy on how to use the value object for candidate object approval (sublassed strategies of this class)
39   * </ul>
40   * <p/>
41   * All objects given and then bound to this class are cloned, ensuring specification object immutability.
42   *
43   * @author Eirik Torske
44   * @see <a href="../util/ReflectionUtils.html#cloneOrDeepCopyIfNotImmutable(java.lang.Object)"><code>ReflectionUtils.cloneOrDeepCopyIfNotImmutable(java.lang.Object)</code></a>
45   * @see <a href="../util/ReflectionUtils.html#DO_COPY_OBJECTS"><code>ReflectionUtils.DO_COPY_OBJECTS</code></a>
46   * @see <a href="../util/ReflectionUtils.html#RECURSIVE_COPYING_DEPTH_TRESHOLD"><code>ReflectionUtils.RECURSIVE_COPYING_DEPTH_TRESHOLD</code></a>
47   * @since 0.1
48   */
49  abstract class AbstractValueBoundSpecification<T> extends AbstractSpecification<T> implements ValueBoundSpecification<T> {
50  
51      protected final T value;
52      protected final T originalValue;
53  
54      @SuppressWarnings("unchecked")
55      AbstractValueBoundSpecification(final T value) {
56          Validate.notNull(value, "Value parameter cannot be null");
57          /* @SuppressWarnings("unchecked") -> Just apply proper type parameters, and you'll be safe */
58          this.type = (Class<T>) value.getClass();
59          this.originalValue = value;
60  
61          final boolean doCopyEntites = true;
62          if (this.originalValue instanceof Comparable<?> || !isEntity(this.originalValue)) {
63              this.value = cloneOrDeepCopyIfNotImmutable(this.originalValue, doCopyEntites);
64          } else {
65              this.value = cloneOrDeepCopyIfNotImmutable(this.originalValue, !doCopyEntites);
66          }
67      }
68  
69      @Override
70      public T getValue() {
71          return this.originalValue;
72      }
73  
74      /**
75       * @return the mathematical binary relation bound to this value bound specification
76       * @since 0.5
77       */
78      protected RelationalOperator getBinaryRelation() {
79          return getBinaryRelation(FALSE);
80      }
81  
82      /**
83       * @param negated when {@code true} the returned binary relation will be inverted
84       * @return the mathematical binary relation bound to this value bound specification
85       * @since 0.5
86       */
87      protected abstract RelationalOperator getBinaryRelation(Boolean negated);
88  
89      /**
90       * @return {@code true} if the given candidate relation/operand pair leads to a disjoint relationship with this value bound specification
91       * @since 0.5
92       */
93      protected abstract Boolean getDisjointDecision(final RelationalOperator candidateRelationalOperator, final T candidateOperandValue);
94  
95      @Override
96      public Boolean isDisjointWith(final Specification<?> specification) {
97          Validate.notNull(specification, "Specification parameter cannot be null");
98          // If same/equal specifications:
99          if (this == specification || this.equals(specification)) {
100             return FALSE;
101         }
102         // If specifications are not of the same type:
103         if (!canCastAtLeastOneWay(this.getType(), specification.getType())) {
104             return TRUE;
105         }
106         if (specification instanceof EqualSpecification) {
107             return !this.equals(specification);
108         }
109         return TRUE;
110     }
111 
112     @Override
113     protected Specification<T> invert() {
114         // TODO: fix generics!
115         return createValueBoundSpecification(this.getBinaryRelation(DO_INVERT), this.value);
116     }
117 
118     @Override
119     public int hashCode() {
120         return new HashCodeBuilder(4877, 8555).append(this.value).toHashCode();
121     }
122 
123     @Override
124     public boolean equals(final Object otherObject) {
125         if (otherObject == null) { return false; }
126         if (!(otherObject instanceof AbstractValueBoundSpecification)) { return false; }
127         return new EqualsBuilder()
128                 .append(this.getClass(), otherObject.getClass())
129                 .append(this.value, ((AbstractValueBoundSpecification) otherObject).value)
130                 .isEquals();
131     }
132 }