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.Comparator;
019    
020    import org.springframework.rules.constraint.Constraint;
021    import org.springframework.core.style.ToStringCreator;
022    import org.springframework.util.Assert;
023    
024    /**
025     * A range whose edges are defined by a minimum Comparable and a maximum
026     * Comparable.
027     * 
028     * @author Keith Donald
029     */
030    public final class Range extends AbstractConstraint {
031            private Object min;
032    
033            private Object max;
034    
035            private boolean inclusive = true;
036    
037            private Constraint rangeConstraint;
038    
039            /**
040             * Creates a range with the specified <code>Comparable</code> min and max
041             * edges.
042             * 
043             * @param min
044             *            the low edge of the range
045             * @param max
046             *            the high edge of the range
047             */
048            public Range(Comparable min, Comparable max) {
049                    this(min, max, true);
050            }
051    
052            /**
053             * Creates a range with the specified <code>Comparable</code> min and max
054             * edges.
055             * 
056             * @param min
057             *            the low edge of the range
058             * @param max
059             *            the high edge of the range
060             * @param inclusive
061             *            the range is inclusive?
062             */
063            public Range(Comparable min, Comparable max, boolean inclusive) {
064                    commonAssert(min, max);
065                    Constraint minimum;
066                    Constraint maximum;
067                    this.inclusive = inclusive;
068                    if (this.inclusive) {
069                            Assert.isTrue(LessThanEqualTo.instance().test(min, max), "Minimum " + min
070                                            + " must be less than or equal to maximum " + max);
071                            minimum = gte(min);
072                            maximum = lte(max);
073                    }
074                    else {
075                            Assert.isTrue(LessThan.instance().test(min, max), "Minimum " + min + " must be less than maximum " + max);
076                            minimum = gt(min);
077                            maximum = lt(max);
078                    }
079                    this.rangeConstraint = and(minimum, maximum);
080                    this.min = min;
081                    this.max = max;
082            }
083    
084            /**
085             * Creates a range with the specified min and max edges.
086             * 
087             * @param min
088             *            the low edge of the range
089             * @param max
090             *            the high edge of the range
091             * @param comparator
092             *            the comparator to use to perform value comparisons
093             */
094            public Range(Object min, Object max, Comparator comparator) {
095                    this(min, max, comparator, true);
096            }
097    
098            /**
099             * Creates a range with the specified min and max edges.
100             * 
101             * @param min
102             *            the low edge of the range
103             * @param max
104             *            the high edge of the range
105             * @param comparator
106             *            the comparator to use to perform value comparisons
107             */
108            public Range(Object min, Object max, Comparator comparator, boolean inclusive) {
109                    commonAssert(min, max);
110                    Constraint minimum;
111                    Constraint maximum;
112                    this.inclusive = inclusive;
113                    if (this.inclusive) {
114                            Assert.isTrue(LessThanEqualTo.instance(comparator).test(min, max), "Minimum " + min
115                                            + " must be less than or equal to maximum " + max);
116                            minimum = bind(GreaterThanEqualTo.instance(comparator), min);
117                            maximum = bind(LessThanEqualTo.instance(comparator), max);
118                    }
119                    else {
120                            Assert.isTrue(LessThan.instance(comparator).test(min, max), "Minimum " + min
121                                            + " must be less than maximum " + max);
122                            minimum = bind(GreaterThan.instance(comparator), min);
123                            maximum = bind(LessThan.instance(comparator), max);
124                    }
125                    this.rangeConstraint = and(minimum, maximum);
126                    this.min = min;
127                    this.max = max;
128            }
129    
130            private void commonAssert(Object min, Object max) {
131                    Assert.isTrue(min != null && max != null, "Min and max are required");
132                    Assert.isTrue(min.getClass().equals(max.getClass()), "Min and max must be of the same class");
133            }
134    
135            /**
136             * Convenience constructor for <code>int</code> ranges.
137             */
138            public Range(int min, int max) {
139                    this(new Integer(min), new Integer(max));
140            }
141    
142            /**
143             * Convenience constructor for <code>int</code> ranges.
144             */
145            public Range(int min, int max, boolean inclusive) {
146                    this(new Integer(min), new Integer(max), inclusive);
147            }
148    
149            /**
150             * Convenience constructor for <code>float</code> ranges.
151             */
152            public Range(float min, float max) {
153                    this(new Float(min), new Float(max));
154            }
155    
156            /**
157             * Convenience constructor for <code>float</code> ranges.
158             */
159            public Range(float min, float max, boolean inclusive) {
160                    this(new Float(min), new Float(max), inclusive);
161            }
162    
163            /**
164             * Convenience constructor for <code>double</code> ranges.
165             */
166            public Range(double min, double max) {
167                    this(new Double(min), new Double(max));
168            }
169    
170            /**
171             * Convenience constructor for <code>double</code> ranges.
172             */
173            public Range(double min, double max, boolean inclusive) {
174                    this(new Double(min), new Double(max), inclusive);
175            }
176    
177            public Object getMin() {
178                    return min;
179            }
180    
181            public Object getMax() {
182                    return max;
183            }
184    
185            public boolean isInclusive() {
186                    return inclusive;
187            }
188    
189            /**
190             * Test if the specified argument falls within the established range.
191             * 
192             * @see Constraint#test(java.lang.Object)
193             */
194            public boolean test(Object argument) {
195                    return this.rangeConstraint.test(argument);
196            }
197    
198            public String toString() {
199                    return new ToStringCreator(this).append("rangeConstraint", rangeConstraint).toString();
200            }
201    
202    }