001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.rules.constraint;
017    
018    import java.util.ArrayList;
019    import java.util.Arrays;
020    import java.util.Iterator;
021    import java.util.List;
022    
023    import org.springframework.rules.constraint.Constraint;
024    import org.springframework.rules.closure.support.Algorithms;
025    import org.springframework.rules.closure.support.Block;
026    import org.springframework.core.style.ToStringCreator;
027    import org.springframework.util.Assert;
028    
029    /**
030     * Abstract base class for unary constraints which compose other constraints.
031     * 
032     * @author Keith Donald
033     */
034    public abstract class CompoundConstraint extends AbstractConstraint {
035    
036            private List constraints = new ArrayList();
037    
038            /**
039             * Constructs a compound constraint with no initial members. It is expected
040             * the client will call "add" to add individual constraints.
041             */
042            public CompoundConstraint() {
043    
044            }
045    
046            /**
047             * Creates a CompoundUnaryPredicate composed of two constraints.
048             * 
049             * @param constraint1
050             *            the first constraint
051             * @param constraint2
052             *            the second constraint
053             */
054            public CompoundConstraint(Constraint constraint1, Constraint constraint2) {
055                    Assert.isTrue(constraint1 != null && constraint2 != null, "Both constraints are required");
056                    constraints.add(constraint1);
057                    constraints.add(constraint2);
058            }
059    
060            /**
061             * Creates a CompoundUnaryPredicate composed of the specified constraints.
062             * 
063             * @param constraints
064             *            the aggregated constraints
065             */
066            public CompoundConstraint(Constraint[] constraints) {
067                    this.constraints.addAll(Arrays.asList(constraints));
068            }
069    
070            /**
071             * Add the specified constraint to the set of constraints aggregated by this
072             * compound constraint.
073             * 
074             * @param constraint
075             *            the constraint to add
076             * @return A reference to this, to support chaining.
077             */
078            public CompoundConstraint add(Constraint constraint) {
079                    this.constraints.add(constraint);
080                    return this;
081            }
082    
083            /**
084             * Add the list of constraints to the set of constraints aggregated by this
085             * compound constraint.
086             * 
087             * @param constraints
088             *            the list of constraints to add
089             * @return A reference to this, to support chaining.
090             */
091            public CompoundConstraint addAll(List constraints) {
092                    Algorithms.instance().forEach(constraints, new Block() {
093                            protected void handle(Object o) {
094                                    add((Constraint)o);
095                            }
096                    });
097                    return this;
098            }
099    
100            public void remove(Constraint constraint) {
101                    constraints.remove(constraint);
102            }
103    
104            public int indexOf(Constraint child) {
105                    return constraints.indexOf(child);
106            }
107    
108            public Constraint get(int index) {
109                    return (Constraint)constraints.get(index);
110            }
111    
112            public void copyInto(CompoundConstraint p) {
113                    p.constraints.clear();
114                    p.constraints.addAll(constraints);
115            }
116    
117            public void set(int index, Constraint constraint) {
118                    constraints.set(index, constraint);
119            }
120    
121            /**
122             * Return an iterator over the aggregated constraints.
123             * 
124             * @return An iterator
125             */
126            public Iterator iterator() {
127                    return constraints.iterator();
128            }
129    
130            /**
131             * Returns the number of constraints aggregated by this compound constraint.
132             * 
133             * @return The size.
134             */
135            public int size() {
136                    return constraints.size();
137            }
138    
139            public abstract boolean test(Object argument);
140    
141            public void validateTypeSafety(final Class constraintType) {
142                    Assert.notNull(constraintType, "Constraint type is required");
143                    Assert.isTrue(Constraint.class.isAssignableFrom(constraintType),
144                                    "Argument must be a specialization of the Constraint interface");
145                    boolean result = new AbstractConstraint() {
146                            public boolean test(Object o) {
147                                    return constraintType.isAssignableFrom(o.getClass());
148                            }
149                    }.allTrue(iterator());
150                    Assert.isTrue(result, "One or more of the aggregated constraints is not assignable to " + constraintType);
151            }
152    
153            public String toString() {
154                    return new ToStringCreator(this).append("constraints", constraints).toString();
155            }
156    
157    }