View Javadoc

1   package net.sf.twip.parameterhandler;
2   
3   import java.util.regex.*;
4   
5   import net.sf.twip.Assume;
6   import net.sf.twip.internal.TwipConfigurationErrorIllegalAssumeExpression;
7   import net.sf.twip.util.Parameter;
8   
9   /**
10   * You can assume number parameters to be <code>&gt;</code>, <code>&gt;=</code>, <code>!=</code>,
11   * <code>&lt;=</code>, or <code>&lt;</code> to a numeric constant. You can assume float and double
12   * parameters additionally to be <code>!= NaN</code>, <code>!= INF</code>, and <code>!= -INF</code>.
13   * Note that <code>null</code> "slips through" these tests, i.e. if you assume that an Integer
14   * argument is <code>&gt;= 0</code>, then <code>null</code> is still passed in.
15   */
16  abstract public class AbstractNumberParameterHandler extends ParameterHandler {
17  
18  	private static final class AndTester implements Tester {
19  		private final Tester[] testers;
20  
21  		private AndTester(Tester... testers) {
22  			this.testers = testers;
23  		}
24  
25  		@Override
26  		public boolean test(Object value) {
27  			for (Tester tester : testers) {
28  				if (!tester.test(value)) {
29  					return false;
30  				}
31  			}
32  			return true;
33  		}
34  	}
35  
36  	private static final class EqualsTester implements Tester {
37  		private final Comparable<Object> comparable;
38  
39  		private EqualsTester(Comparable<Object> comparable) {
40  			this.comparable = comparable;
41  		}
42  
43  		@Override
44  		public boolean test(Object value) {
45  			return value == null || 0 != comparable.compareTo(value);
46  		}
47  	}
48  
49  	private static final class GreaterOrEqualsTester implements Tester {
50  		private final Comparable<Object> comparable;
51  
52  		private GreaterOrEqualsTester(Comparable<Object> comparable) {
53  			this.comparable = comparable;
54  		}
55  
56  		@Override
57  		public boolean test(Object value) {
58  			return value == null || 0 >= comparable.compareTo(value);
59  		}
60  	}
61  
62  	private static final class GreaterThanTester implements Tester {
63  		private final Comparable<Object> comparable;
64  
65  		private GreaterThanTester(Comparable<Object> comparable) {
66  			this.comparable = comparable;
67  		}
68  
69  		@Override
70  		public boolean test(Object value) {
71  			return value == null || 0 > comparable.compareTo(value);
72  		}
73  	}
74  
75  	private static final class LessOrEqualsTester implements Tester {
76  		private final Comparable<Object> comparable;
77  
78  		private LessOrEqualsTester(Comparable<Object> comparable) {
79  			this.comparable = comparable;
80  		}
81  
82  		@Override
83  		public boolean test(Object value) {
84  			return value == null || 0 <= comparable.compareTo(value);
85  		}
86  	}
87  
88  	private static final class LessThanTester implements Tester {
89  		private final Comparable<Object> comparable;
90  
91  		private LessThanTester(Comparable<Object> comparable) {
92  			this.comparable = comparable;
93  		}
94  
95  		@Override
96  		public boolean test(Object value) {
97  			return value == null || 0 < comparable.compareTo(value);
98  		}
99  	}
100 
101 	protected interface Tester {
102 		public boolean test(Object value);
103 	}
104 
105 	private static final class TrueTester implements Tester {
106 		@Override
107 		public boolean test(Object value) {
108 			return true;
109 		}
110 	}
111 
112 	private static final Pattern EXPRESSION = Pattern.compile("(<|>|!)(=?)\\s*(-?\\w+)");
113 
114 	private final Tester tester;
115 
116 	public AbstractNumberParameterHandler(Parameter parameter) {
117 		super(parameter);
118 		this.tester = getTester();
119 	}
120 
121 	protected abstract Comparable<? extends Number> getComparable(String numberExpression);
122 
123 	private Tester getTester() {
124 		final Assume assumption = parameter.getAnnotation(Assume.class);
125 		if (assumption == null)
126 			return new TrueTester();
127 
128 		final String expression = assumption.value();
129 		return getTester(expression);
130 	}
131 
132 	protected Tester getTester(final String expression) {
133 		int andIndex = expression.indexOf('&');
134 		if (andIndex >= 0) {
135 			String left = expression.substring(0, andIndex).trim();
136 			String right = expression.substring(andIndex + 1).trim();
137 			return new AndTester(getTester(left), getTester(right));
138 		}
139 		final Matcher matcher = EXPRESSION.matcher(expression);
140 		if (!matcher.matches())
141 			throw new TwipConfigurationErrorIllegalAssumeExpression("illegal assume expression ["
142 					+ expression + "] for [" + parameter.getType().getSimpleName() + "]");
143 		@SuppressWarnings("unchecked")
144 		final Comparable<Object> comparable = //
145 		(Comparable<Object>) (Comparable<?>) getComparable(matcher.group(3));
146 		if (">".equals(matcher.group(1))) {
147 			if ("=".equals(matcher.group(2))) {
148 				return new GreaterOrEqualsTester(comparable);
149 			} else {
150 				return new GreaterThanTester(comparable);
151 			}
152 		} else if ("<".equals(matcher.group(1))) {
153 			if ("=".equals(matcher.group(2))) {
154 				return new LessOrEqualsTester(comparable);
155 			} else {
156 				return new LessThanTester(comparable);
157 			}
158 		} else {
159 			if ("=".equals(matcher.group(2))) {
160 				return new EqualsTester(comparable);
161 			} else {
162 				throw new TwipConfigurationErrorIllegalAssumeExpression(
163 						"illegal assume expression [" + expression + "] for ["
164 								+ parameter.getType().getSimpleName() + "]");
165 			}
166 		}
167 	}
168 
169 	@Override
170 	public boolean test(Object value) {
171 		return tester.test(value);
172 	}
173 }