001    package org.apache.commons.contract.constraints;
002    
003    import java.math.BigDecimal;
004    import java.util.ArrayList;
005    import java.util.Arrays;
006    import java.util.Iterator;
007    import java.util.List;
008    
009    import org.apache.commons.contract.Context;
010    import org.apache.commons.i18n.bundles.ErrorBundle;
011    import org.apache.commons.i18n.bundles.TextBundle;
012    
013    public class NumberConstraints implements Constraints {
014        public final static NumberConstraints UNCONSTRAINED = new NumberConstraints();
015        public final static NumberConstraints POSITIVE = new NumberConstraints(new Integer(0), null, false);
016        public final static NumberConstraints NEGATIVE = new NumberConstraints(null, new Integer(0), false);
017        
018            protected boolean constrained, inclusive;
019        protected List allowedValues = new ArrayList();
020        protected Number minimum, maximum;
021        
022        public NumberConstraints() {
023            this.constrained = false;
024        }
025    
026        public NumberConstraints(Number minimum, Number maximum) {
027            this(minimum, maximum, true);
028        }
029    
030        public NumberConstraints(Number minimum, Number maximum, boolean inclusive) {
031            constrained = true;
032            this.minimum = minimum;
033            this.maximum = maximum;
034            this.inclusive = inclusive;
035        }
036    
037        public NumberConstraints(Number[] allowedValues) {
038            this.constrained = true;
039            this.allowedValues = new ArrayList(Arrays.asList(allowedValues));
040        }
041    
042        public boolean isConstrained() {
043            return constrained;
044        }
045    
046        public void addAllowedValue(Number value) {
047            allowedValues.add(value);
048        }
049    
050        public Number[] getAllowedValues() {
051            if ( allowedValues.isEmpty() ) return null;
052            return (Number [])allowedValues.toArray(new Number[0]);
053        }
054    
055        public void setMinimum(Number minimum) {
056            constrained = true;
057            this.minimum = minimum;
058        }
059        
060        public Number getMinimum() {
061            return minimum;
062        }
063    
064        public void setMaximum(Number maximum) {
065            constrained = true;
066            this.maximum = maximum;
067        }
068        
069        public Number getMaximum() {
070            return maximum;
071        }
072    
073        public Object cast(Object value, Context context) throws CastException {
074            if ( value instanceof Number) {
075                return (Number)value;
076            } else {
077                    try {
078                    return new BigDecimal(StringConstraints.UNCONSTRAINED.cast(value, null).toString());
079                } catch ( NumberFormatException exception ) {
080                    throw new CastException(new ErrorBundle("uncastableNumber", new Object[] { value }), exception);
081                } catch ( CastException exception ) {
082                    throw new CastException(new ErrorBundle("uncastableNumber", new Object[] { value }), exception);
083                    }
084            }
085        }
086    
087        public void validate(Object value, Context context) throws ValidationException {
088            if ( constrained ) {
089                    Number number = (Number)value;
090                    if ( getAllowedValues() != null ) {
091                            for ( Iterator i = allowedValues.iterator(); i.hasNext(); ) {
092                                    Number allowedNumber = (Number)i.next();
093                                    if ( allowedNumber.equals(number) ) {
094                                            return;
095                                    }
096                            }
097                            throw new ValidationException(new ErrorBundle("invalidNumber", new Object[] { value }));
098                    } else {
099                        if ( inclusive && minimum != null && minimum.doubleValue() >  number.doubleValue() ) {
100                            throw new ValidationException(new ErrorBundle("numberMustBeGreater", new Object[] { number, minimum }));
101                        }
102                    if ( !inclusive && minimum != null && minimum.doubleValue() >=  number.doubleValue() ) {
103                        throw new ValidationException(new ErrorBundle("numberMustBeGreaterOrEqual", new Object[] { number, minimum }));
104                    }
105                    if ( inclusive && maximum != null && maximum.doubleValue() <  number.doubleValue() ) {
106                        throw new ValidationException(new ErrorBundle("numberMustBeLess", new Object[] { number, minimum }));
107                    }
108                    if ( !inclusive && maximum != null && maximum.doubleValue() >=  number.doubleValue() ) {
109                        throw new ValidationException(new ErrorBundle("numberMustBeLessOrEqual", new Object[] { number, minimum }));
110                    }
111                    }
112            }
113        }
114        
115        public TextBundle verboseConstraints() {
116            if ( constrained ) {
117                if ( inclusive && maximum != null && minimum == null ) {
118                    return new TextBundle("lessOrEqual", new Object[] { maximum } );
119                } else if ( !inclusive && maximum != null && minimum == null ) {
120                    return new TextBundle("less", new Object[] { maximum } );
121                } else if ( inclusive && minimum != null && maximum == null ) {
122                    return new TextBundle("greaterOrEqual", new Object[] { minimum } );
123                } else if ( !inclusive && minimum != null && maximum == null ) {
124                    return new TextBundle("greater", new Object[] { minimum } );
125                } else if ( inclusive && maximum != null && minimum != null ) {
126                    return new TextBundle("rangeIncluding", new Object[] { minimum, maximum } );
127                } else {
128                    return new TextBundle("rangeExcluding", new Object[] { minimum, maximum } );
129                }
130            } else {
131                return new TextBundle("numbers");
132            }
133        }
134    }