1 /*
2 * $Id: DoubleRangeValidator.java,v 1.51.4.1 2007/09/26 19:22:44 rlubke Exp $
3 */
4
5 /*
6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
7 *
8 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
9 *
10 * The contents of this file are subject to the terms of either the GNU
11 * General Public License Version 2 only ("GPL") or the Common Development
12 * and Distribution License("CDDL") (collectively, the "License"). You
13 * may not use this file except in compliance with the License. You can obtain
14 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
15 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
16 * language governing permissions and limitations under the License.
17 *
18 * When distributing the software, include this License Header Notice in each
19 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
20 * Sun designates this particular file as subject to the "Classpath" exception
21 * as provided by Sun in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the License
23 * Header, with the fields enclosed by brackets [] replaced by your own
24 * identifying information: "Portions Copyrighted [year]
25 * [name of copyright owner]"
26 *
27 * Contributor(s):
28 *
29 * If you wish your version of this file to be governed by only the CDDL or
30 * only the GPL Version 2, indicate your decision by adding "[Contributor]
31 * elects to include this software in this distribution under the [CDDL or GPL
32 * Version 2] license." If you don't indicate a single choice of license, a
33 * recipient has the option to distribute your version of this file under
34 * either the CDDL, the GPL Version 2 or to extend the choice of license to
35 * its licensees as provided above. However, if you add GPL Version 2 code
36 * and therefore, elected the GPL Version 2 license, then the option applies
37 * only if the new code is made subject to such option by the copyright
38 * holder.
39 */
40
41 package javax.faces.validator;
42
43
44 import javax.faces.component.StateHolder;
45 import javax.faces.component.UIComponent;
46 import javax.faces.context.FacesContext;
47 import javax.faces.convert.Converter;
48
49 /**
50 * <p><strong>DoubleRangeValidator</strong> is a {@link Validator} that checks
51 * the value of the corresponding component against specified minimum and
52 * maximum values. The following algorithm is implemented:</p>
53 * <ul>
54 * <li>If the passed value is <code>null</code>, exit immediately.</li>
55 * <li>If the current component value is not a floating point type, or
56 * a String that is convertible to double, throw a
57 * {@link ValidatorException} containing a
58 * TYPE_MESSAGE_ID message.</li>
59 * <li>If both a <code>maximum</code> and <code>minimum</code> property
60 * has been configured on this {@link Validator}, check the component
61 * value against both limits. If the component value is not within
62 * this specified range, throw a {@link ValidatorException} containing a
63 * {@link #NOT_IN_RANGE_MESSAGE_ID} message.</li>
64 * <li>If a <code>maximum</code> property has been configured on this
65 * {@link Validator}, check the component value against
66 * this limit. If the component value is greater than the
67 * specified maximum, throw a {@link ValidatorException} containing a
68 * MAXIMUM_MESSAGE_ID message.</li>
69 * <li>If a <code>minimum</code> property has been configured on this
70 * {@link Validator}, check the component value against
71 * this limit. If the component value is less than the
72 * specified minimum, throw a {@link ValidatorException} containing a
73 * MINIMUM_MESSAGE_ID message.</li>
74 * </ul>
75 * <p/>
76 * <p>For all of the above cases that cause a {@link ValidatorException}
77 * to be thrown, if there are parameters to the message that match up
78 * with validator parameters, the values of these parameters must be
79 * converted using the {@link Converter} registered in the application
80 * under the converter id <code>javax.faces.Number</code>. This allows
81 * the values to be localized according to the current
82 * <code>Locale</code>.</p>
83 */
84
85 public class DoubleRangeValidator implements Validator, StateHolder {
86
87 // ------------------------------------------------------ Manifest Constants
88
89
90 /**
91 * <p>The standard converter id for this converter.</p>
92 */
93 public static final String VALIDATOR_ID = "javax.faces.DoubleRange";
94
95
96 /**
97 * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
98 * to be created if the maximum value check fails. The message format
99 * string for this message may optionally include the following
100 * placeholders:
101 * <ul>
102 * <li><code>{0}</code> replaced by the configured maximum value.</li>
103 * <li><code>{1}</code> replaced by a <code>String</code> whose value
104 * is the label of the input component that produced this message.</li>
105 * </ul></p>
106 */
107 public static final String MAXIMUM_MESSAGE_ID =
108 "javax.faces.validator.DoubleRangeValidator.MAXIMUM";
109
110 /**
111 * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
112 * to be created if the minimum value check fails. The message format
113 * string for this message may optionally include the following
114 * placeholders:
115 * <ul>
116 * <li><code>{0}</code> replaced by the configured minimum value.</li>
117 * <li><code>{1}</code> replaced by a <code>String</code> whose value
118 * is the label of the input component that produced this message.</li>
119 * </ul></p>
120 */
121 public static final String MINIMUM_MESSAGE_ID =
122 "javax.faces.validator.DoubleRangeValidator.MINIMUM";
123
124 /**
125 * <p>The message identifier of the {@link javax.faces.application.FacesMessage} to be created if
126 * the maximum or minimum value check fails, and both the maximum
127 * and minimum values for this validator have been set. The message
128 * format string for this message may optionally include the following
129 * placeholders:
130 * <ul>
131 * <li><code>{0}</code> replaced by the configured minimum value.</li>
132 * <li><code>{1}</code> replaced by the configured maximum value.</li>
133 * <li><code>{2}</code> replaced by a <code>String</code> whose value
134 * is the label of the input component that produced this message.</li>
135 * </ul></p>
136 */
137 public static final String NOT_IN_RANGE_MESSAGE_ID =
138 "javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE";
139
140 /**
141 * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
142 * to be created if the current value of this component is not of the
143 * correct type. The message format string for this message may
144 * optionally include a <code>{0}</code> placeholder that will be
145 * replaced by a <code>String</code> whose value is the label of
146 * the input component that produced this message.</p>
147 */
148 public static final String TYPE_MESSAGE_ID =
149 "javax.faces.validator.DoubleRangeValidator.TYPE";
150
151 // ------------------------------------------------------------ Constructors
152
153
154 /**
155 * <p>Construct a {@link Validator} with no preconfigured limits.</p>
156 */
157 public DoubleRangeValidator() {
158
159 super();
160
161 }
162
163
164 /**
165 * <p>Construct a {@link Validator} with the specified preconfigured
166 * limit.</p>
167 *
168 * @param maximum Maximum value to allow
169 */
170 public DoubleRangeValidator(double maximum) {
171
172 super();
173 setMaximum(maximum);
174
175 }
176
177
178 /**
179 * <p>Construct a {@link Validator} with the specified preconfigured
180 * limits.</p>
181 *
182 * @param maximum Maximum value to allow
183 * @param minimum Minimum value to allow
184 */
185 public DoubleRangeValidator(double maximum, double minimum) {
186
187 super();
188 setMaximum(maximum);
189 setMinimum(minimum);
190
191 }
192
193 // -------------------------------------------------------------- Properties
194
195
196 private Double maximum;
197
198 /**
199 * <p>Return the maximum value to be enforced by this {@link
200 * Validator} or <code>Double.MAX_VALUE</code> if it has not been
201 * set.</p>
202 */
203 public double getMaximum() {
204
205 return (this.maximum != null ? this.maximum : Double.MAX_VALUE);
206
207 }
208
209
210 /**
211 * <p>Set the maximum value to be enforced by this {@link Validator}.</p>
212 *
213 * @param maximum The new maximum value
214 */
215 public void setMaximum(double maximum) {
216
217 this.maximum = maximum;
218
219 }
220
221
222 private Double minimum;
223
224
225 /**
226 * <p>Return the minimum value to be enforced by this {@link
227 * Validator}, or <code>Double.MIN_VALUE</code> if it has not been
228 * set.</p>
229 */
230 public double getMinimum() {
231
232 return (this.minimum != null ? this.minimum : Double.MIN_VALUE);
233
234 }
235
236
237 /**
238 * <p>Set the minimum value to be enforced by this {@link Validator}.</p>
239 *
240 * @param minimum The new minimum value
241 */
242 public void setMinimum(double minimum) {
243
244 this.minimum = minimum;
245
246 }
247
248 // ------------------------------------------------------- Validator Methods
249
250 /**
251 * @throws NullPointerException {@inheritDoc}
252 * @throws ValidatorException {@inheritDoc}
253 */
254 public void validate(FacesContext context,
255 UIComponent component,
256 Object value) throws ValidatorException {
257 if ((context == null) || (component == null)) {
258 throw new NullPointerException();
259 }
260 if (value != null) {
261 try {
262 double converted = doubleValue(value);
263 if (isMaximumSet() &&
264 (converted > maximum)) {
265 if (isMinimumSet()) {
266 throw new ValidatorException(MessageFactory.getMessage
267 (context,
268 NOT_IN_RANGE_MESSAGE_ID,
269 stringValue(component, minimum, context),
270 stringValue(component, maximum, context),
271 MessageFactory.getLabel(context, component)));
272
273 } else {
274 throw new ValidatorException(MessageFactory.getMessage
275 (context,
276 MAXIMUM_MESSAGE_ID,
277 stringValue(component, maximum, context),
278 MessageFactory.getLabel(context, component)));
279 }
280 }
281 if (isMinimumSet() &&
282 (converted < minimum)) {
283 if (isMaximumSet()) {
284 throw new ValidatorException(MessageFactory.getMessage
285 (context,
286 NOT_IN_RANGE_MESSAGE_ID,
287 stringValue(component, minimum, context),
288 stringValue(component, maximum, context),
289 MessageFactory.getLabel(context, component)));
290
291 } else {
292 throw new ValidatorException(MessageFactory.getMessage
293 (context,
294 MINIMUM_MESSAGE_ID,
295 stringValue(component, minimum, context),
296 MessageFactory.getLabel(context, component)));
297 }
298 }
299 } catch (NumberFormatException e) {
300 throw new ValidatorException(MessageFactory.getMessage
301 (context, TYPE_MESSAGE_ID,
302 MessageFactory.getLabel(context, component)));
303 }
304 }
305
306 }
307
308
309 public boolean equals(Object otherObj) {
310
311 if (!(otherObj instanceof DoubleRangeValidator)) {
312 return false;
313 }
314 DoubleRangeValidator other = (DoubleRangeValidator) otherObj;
315 return ((this.getMaximum() == other.getMaximum())
316 && (this.getMinimum() == other.getMinimum())
317 && (this.isMaximumSet() == other.isMaximumSet())
318 && (this.isMinimumSet() == other.isMinimumSet()));
319
320 }
321
322
323 public int hashCode() {
324
325 int hashCode = (Double.valueOf(this.getMinimum()).hashCode()
326 + Double.valueOf(this.getMaximum()).hashCode()
327 + Boolean.valueOf(isMinimumSet()).hashCode()
328 + Boolean.valueOf(isMaximumSet()).hashCode());
329 return (hashCode);
330
331 }
332
333 // --------------------------------------------------------- Private Methods
334
335
336 /**
337 * <p>Return the specified attribute value, converted to a
338 * <code>double</code>.</p>
339 *
340 * @param attributeValue The attribute value to be converted
341 * @throws NumberFormatException if conversion is not possible
342 */
343 private static double doubleValue(Object attributeValue)
344 throws NumberFormatException {
345
346 if (attributeValue instanceof Number) {
347 return (((Number) attributeValue).doubleValue());
348 } else {
349 return (Double.parseDouble(attributeValue.toString()));
350 }
351
352 }
353
354 private static String stringValue(UIComponent component,
355 Double toConvert,
356 FacesContext context) {
357
358 Converter converter = context.getApplication().createConverter("javax.faces.Number");
359 return converter.getAsString(context, component, toConvert);
360
361 }
362
363 private boolean isMaximumSet() {
364
365 return (maximum != null);
366
367 }
368
369
370 private boolean isMinimumSet() {
371
372 return (minimum != null);
373
374 }
375
376 // ----------------------------------------------------- StateHolder Methods
377
378
379 public Object saveState(FacesContext context) {
380
381 Object values[] = new Object[2];
382 values[0] = maximum;
383 values[1] = minimum;
384 return (values);
385
386 }
387
388
389 public void restoreState(FacesContext context, Object state) {
390
391 Object values[] = (Object[]) state;
392 maximum = (Double) values[0];
393 minimum = (Double) values[1];
394
395 }
396
397
398 private boolean transientValue = false;
399
400
401 public boolean isTransient() {
402
403 return (this.transientValue);
404
405 }
406
407
408 public void setTransient(boolean transientValue) {
409
410 this.transientValue = transientValue;
411
412 }
413
414 }