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.util.HashSet;
20  import java.util.Map;
21  import java.util.Set;
22  
23  import org.apache.commons.lang.Validate;
24  
25  import static java.lang.Boolean.FALSE;
26  import static java.lang.Boolean.TRUE;
27  import static net.sourceforge.domian.util.ReflectionUtils.canCast;
28  import static net.sourceforge.domian.util.ReflectionUtils.canCastAtLeastOneWay;
29  
30  
31  /**
32   * A composite specification where <i>all</i> of the wrapped specifications
33   * must be satisfied for this overall composite specification to be satisfied.
34   * <p/>
35   * The boolean logic operator equivalent is <i><b>AND</b></i>.
36   *
37   * @author Eirik Torske
38   * @since 0.1
39   */
40  final class ConjunctionSpecification<T> extends AbstractCompositeSpecification<T> {
41  
42      ConjunctionSpecification(final Class<T> type) {
43          super(type);
44      }
45  
46      @Override
47      public Boolean isSatisfiedBy(final T candidate) {
48          if (super.isSatisfiedBy(candidate)) {
49              for (final Specification<? super T> wrappedSpec : this.specifications) {
50                  if (wrappedSpec != null && !wrappedSpec.isSatisfiedBy(candidate)) {
51                      return false;
52                  }
53              }
54              return true;
55          }
56          return false;
57      }
58  
59  
60      @Override
61      public Boolean isDisjointWith(final Specification<?> specification) {
62          Validate.notNull(specification, "Specification parameter cannot be null");
63  
64          // Pruning: if same/equal specifications
65          if (this == specification || this.equals(specification)) {
66              return FALSE;
67          }
68  
69          // Pruning: if specifications and types are fundamentally disjoint
70          if (!canCastAtLeastOneWay(this.getType(), specification.getType())) {
71              return TRUE;
72          }
73  
74          // Pruning: if type-only spec - and candidate is negated type-only-spec
75          if (this.isSpecifyingAllInstancesOfItsType()) {
76              if (specification instanceof JointDenialSpecification) {
77                  final Specification<?> negatedSpec = getNegatedSpecification((JointDenialSpecification<T>) specification);
78                  if (negatedSpec.equals(this)) {
79                      return TRUE;
80                  }
81              }
82              return FALSE;
83          }
84  
85          // Pruning: if building a composite specification with ParameterizedSpecification
86          if (specification instanceof ParameterizedSpecification) {
87              return FALSE;
88          }
89  
90          if (specification instanceof ValueBoundSpecification) {
91              for (final Specification<?> spec : this.specifications) {
92                  if (!spec.isDisjointWith(specification)) {
93                      return FALSE;
94                  }
95              }
96              return TRUE;
97          }
98  
99          if (specification instanceof JointDenialSpecification) {
100             // Pruning: if specification and types are fundamentally disjoint
101             final JointDenialSpecification jointDenialSpecification = (JointDenialSpecification) specification;
102             final Specification negatedSpec = getNegatedSpecification(jointDenialSpecification);
103             if (canCast(this.getType(), negatedSpec.getType()) && jointDenialSpecification.isTypeExcludingSpecification()) {
104                 return TRUE;
105             }
106             // Delegate negated stuff to JointDenialSpecification
107             return specification.isDisjointWith(this);
108         }
109 
110         if (specification instanceof DisjunctionSpecification) {
111             // Delegate to DisjunctionSpecification
112             return specification.isDisjointWith(this);
113         }
114 
115         // We have two conjunctions at hand...
116         if (this.hasParameterization()) {
117             final AbstractCompositeSpecification candidateCompositeSpecification = (AbstractCompositeSpecification) specification;
118             if (candidateCompositeSpecification.hasParameterization()) {
119                 final Map<String, Set<LeafSpecification>> leafSpecificationMap = this.getLeafSpecificationMap();
120                 final Map<String, Set<LeafSpecification>> candidateLeafSpecificationMap = candidateCompositeSpecification.getLeafSpecificationMap();
121                 for (final String memberName : leafSpecificationMap.keySet()) {
122                     if (candidateLeafSpecificationMap.keySet().contains(memberName)) {
123                         final Set<LeafSpecification> specificationSet = leafSpecificationMap.get(memberName);
124                         final Set<LeafSpecification> memberSpecificationSet = candidateLeafSpecificationMap.get(memberName);
125                         for (final Specification leafSpecification : specificationSet) {
126                             for (final Specification memberLeafSpecification : memberSpecificationSet) {
127                                 if (leafSpecification.isDisjointWith(memberLeafSpecification)) {
128                                     return TRUE;
129                                 }
130                             }
131                         }
132                     } else {
133                         return FALSE;
134                     }
135                 }
136                 for (final String memberName : candidateLeafSpecificationMap.keySet()) {
137                     if (leafSpecificationMap.keySet().contains(memberName)) {
138                         final Set<LeafSpecification> specificationSet = leafSpecificationMap.get(memberName);
139                         final Set<LeafSpecification> memberSpecificationSet = candidateLeafSpecificationMap.get(memberName);
140                         for (final Specification leafSpecification : memberSpecificationSet) {
141                             for (final Specification memberLeafSpecification : specificationSet) {
142                                 if (leafSpecification.isDisjointWith(memberLeafSpecification)) {
143                                     return TRUE;
144                                 }
145                             }
146                         }
147                     } else {
148                         return FALSE;
149                     }
150                 }
151                 return FALSE;
152 
153             } else {
154                 return FALSE;
155             }
156 
157         } else {
158 
159             // We have a non-parametrized/simple conjunction at hand...
160             // Lemma: hvis alle disjointe til alle -> disjoint
161             // Lemma: hvis alle de kandidat-wrappede specs som de "conjuncte" wrappede specs ikke er disjoint til tilsammen er lik kandidat-wrappede-spec'ene -> disjoint
162             final AbstractCompositeSpecification<?> compositeSpecification = (AbstractCompositeSpecification) specification;
163             final Set<Specification> notDisjointToSpecSet = new HashSet<Specification>();
164             for (final Specification wrappedSpec : this.specifications) {
165                 for (final Specification spec : compositeSpecification.specifications) {
166                     if (!wrappedSpec.isDisjointWith(spec)) {
167                         notDisjointToSpecSet.add(spec);
168                     }
169                 }
170             }
171             return notDisjointToSpecSet.isEmpty() || notDisjointToSpecSet.equals(compositeSpecification.specifications);
172         }
173     }
174 
175 
176     @Override
177     protected Boolean isSpecifyingAllInstancesOfItsType() {
178         for (final Specification<? super T> wrappedSpec : this.specifications) {
179             if (wrappedSpec instanceof AbstractCompositeSpecification) {
180                 if (!((AbstractCompositeSpecification) wrappedSpec).isSpecifyingAllInstancesOfItsType()) {
181                     return false;
182                 }
183             } else {
184                 return false;
185             }
186         }
187         return true;
188     }
189 
190 
191     @Override
192     protected AbstractSpecification<T> purify(final boolean doPurifyInversions) {
193         //setPurified(true);
194         if (this.specifications.isEmpty()) {
195             return this;
196         }
197         if (this.specifications.size() == 1) {
198             Specification<? super T> wrappedSpec = this.specifications.iterator().next();
199             // N/A for parameterized specs
200             if (!(wrappedSpec instanceof ParameterizedSpecification)) {
201                 return ((AbstractSpecification<T>) wrappedSpec).purify(true);
202             }
203         }
204         final ConjunctionSpecification<T> newPurifiedConjunctionSpecification = new ConjunctionSpecification<T>(this.getType());
205         //newPurifiedConjunctionSpecification.setPurified(true);
206         for (final Specification wrappedSpec : this.specifications) {
207             AbstractSpecification wrappedAbstractSpec = ((AbstractSpecification) wrappedSpec);
208             //if (!wrappedAbstractSpec.isPurified()) {
209             wrappedAbstractSpec = wrappedAbstractSpec.purify(true);
210             //}
211             if (wrappedAbstractSpec instanceof AbstractCompositeSpecification) {
212                 if (((AbstractCompositeSpecification) wrappedAbstractSpec).isSpecifyingAllInstancesOfItsType()) {
213                     if (this.specifications.size() < 2) {
214                         newPurifiedConjunctionSpecification.specifications.add(wrappedAbstractSpec);
215                     }
216                 } else {
217                     newPurifiedConjunctionSpecification.specifications.add(wrappedAbstractSpec);
218                 }
219             } else {
220                 newPurifiedConjunctionSpecification.specifications.add(wrappedAbstractSpec);
221 
222             }
223         }
224         // Check one last time...
225         if (this.specifications.size() == 1) {
226             Specification<? super T> wrappepSpec = this.specifications.iterator().next();
227             // N/A for parameterized specs
228             if (!(wrappepSpec instanceof ParameterizedSpecification)) {
229                 return ((AbstractSpecification<T>) wrappepSpec).purify(true);
230             }
231         }
232         return newPurifiedConjunctionSpecification;
233     }
234 
235 
236     @Override
237     protected Boolean isInvertible() {
238         return containsValueBoundSpecificationsOnly(this) || containsSimpleCompositesOnly(this);
239     }
240 
241 
242     @Override
243     protected Specification<T> invert() {
244         if (containsValueBoundSpecificationsOnly(this) || containsSimpleCompositesOnly(this)) {
245             return doBooleanAlgebraInversion();
246         }
247         throw new IllegalStateException();
248     }
249 
250 
251     private Specification<T> doBooleanAlgebraInversion() {
252         final DisjunctionSpecification<T> disjunctionSpecification = new DisjunctionSpecification<T>(getType());
253         for (final Specification<?> specification : this.getAllSpecifications()) {
254             disjunctionSpecification.specifications.add(((AbstractSpecification<? super T>) specification).invert());
255         }
256         return disjunctionSpecification;
257     }
258 }