1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.validator;
18
19 import java.io.Serializable;
20 import java.text.DateFormat;
21 import java.text.NumberFormat;
22 import java.text.ParseException;
23 import java.text.ParsePosition;
24 import java.text.SimpleDateFormat;
25 import java.util.Date;
26 import java.util.Locale;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 /**
32 * This class contains basic methods for performing validations that return the
33 * correctly typed class based on the validation performed.
34 */
35 public class GenericTypeValidator implements Serializable {
36
37 private static final long serialVersionUID = 5487162314134261703L;
38
39 private static final Log LOG = LogFactory.getLog(GenericTypeValidator.class);
40
41 /**
42 * Checks if the value can safely be converted to a byte primitive.
43 *
44 * @param value The value validation is being performed on.
45 * @return the converted Byte value.
46 */
47 public static Byte formatByte(final String value) {
48 if (value == null) {
49 return null;
50 }
51
52 try {
53 return Byte.valueOf(value);
54 } catch (final NumberFormatException e) {
55 return null;
56 }
57
58 }
59
60 /**
61 * Checks if the value can safely be converted to a byte primitive.
62 *
63 * @param value The value validation is being performed on.
64 * @param locale The locale to use to parse the number (system default if
65 * null)
66 * @return the converted Byte value.
67 */
68 public static Byte formatByte(final String value, final Locale locale) {
69 Byte result = null;
70
71 if (value != null) {
72 NumberFormat formatter = null;
73 if (locale != null) {
74 formatter = NumberFormat.getNumberInstance(locale);
75 } else {
76 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
77 }
78 formatter.setParseIntegerOnly(true);
79 final ParsePosition pos = new ParsePosition(0);
80 final Number num = formatter.parse(value, pos);
81
82 // If there was no error and we used the whole string
83 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
84 num.doubleValue() >= Byte.MIN_VALUE &&
85 num.doubleValue() <= Byte.MAX_VALUE) {
86 result = Byte.valueOf(num.byteValue());
87 }
88 }
89
90 return result;
91 }
92
93 /**
94 * Checks if the field is a valid credit card number.
95 *
96 * <p>Reference Sean M. Burke's <a href="https://www.ling.nwu.edu/~sburke/pub/luhn_lib.pl">
97 * script</a>.</p>
98 *
99 * @param value The value validation is being performed on.
100 * @return the converted Credit Card number.
101 */
102 public static Long formatCreditCard(final String value) {
103 return GenericValidator.isCreditCard(value) ? Long.valueOf(value) : null;
104 }
105
106 /**
107 * Checks if the field is a valid date.
108 *
109 * <p>The {@link Locale} is used with {@link java.text.DateFormat}. The {@link java.text.DateFormat#setLenient(boolean)}
110 * method is set to {@code false} for all.
111 * </p>
112 *
113 * @param value The value validation is being performed on.
114 * @param locale The Locale to use to parse the date (system default if null)
115 * @return the converted Date value.
116 */
117 public static Date formatDate(final String value, final Locale locale) {
118 Date date = null;
119
120 if (value == null) {
121 return null;
122 }
123
124 try {
125 // Get the formatters to check against
126 DateFormat formatterShort = null;
127 DateFormat formatterDefault = null;
128 if (locale != null) {
129 formatterShort =
130 DateFormat.getDateInstance(DateFormat.SHORT, locale);
131 formatterDefault =
132 DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
133 } else {
134 formatterShort =
135 DateFormat.getDateInstance(
136 DateFormat.SHORT,
137 Locale.getDefault());
138 formatterDefault =
139 DateFormat.getDateInstance(
140 DateFormat.DEFAULT,
141 Locale.getDefault());
142 }
143
144 // Turn off lenient parsing
145 formatterShort.setLenient(false);
146 formatterDefault.setLenient(false);
147
148 // Firstly, try with the short form
149 try {
150 date = formatterShort.parse(value);
151 } catch (final ParseException e) {
152 // Fall back on the default one
153 date = formatterDefault.parse(value);
154 }
155 } catch (final ParseException e) {
156 // Bad date, so LOG and return null
157 if (LOG.isDebugEnabled()) {
158 LOG.debug("Date parse failed value=[" + value + "], " +
159 "locale=[" + locale + "] " + e);
160 }
161 }
162
163 return date;
164 }
165
166 /**
167 * Checks if the field is a valid date.
168 *
169 * <p>The pattern is used with {@link java.text.SimpleDateFormat}.
170 * If strict is true, then the length will be checked so '2/12/1999' will
171 * not pass validation with the format 'MM/dd/yyyy' because the month isn't
172 * two digits. The {@link java.text.SimpleDateFormat#setLenient(boolean)}
173 * method is set to {@code false} for all.
174 * </p>
175 *
176 * @param value The value validation is being performed on.
177 * @param datePattern The pattern passed to {@link SimpleDateFormat}.
178 * @param strict Whether or not to have an exact match of the
179 * datePattern.
180 * @return the converted Date value.
181 */
182 public static Date formatDate(final String value, final String datePattern, final boolean strict) {
183 Date date = null;
184
185 if (value == null
186 || datePattern == null
187 || datePattern.isEmpty()) {
188 return null;
189 }
190
191 try {
192 final SimpleDateFormat formatter = new SimpleDateFormat(datePattern);
193 formatter.setLenient(false);
194
195 date = formatter.parse(value);
196
197 if (strict && datePattern.length() != value.length()) {
198 date = null;
199 }
200 } catch (final ParseException e) {
201 // Bad date so return null
202 if (LOG.isDebugEnabled()) {
203 LOG.debug("Date parse failed value=[" + value + "], " +
204 "pattern=[" + datePattern + "], " +
205 "strict=[" + strict + "] " + e);
206 }
207 }
208
209 return date;
210 }
211
212 /**
213 * Checks if the value can safely be converted to a double primitive.
214 *
215 * @param value The value validation is being performed on.
216 * @return the converted Double value.
217 */
218 public static Double formatDouble(final String value) {
219 if (value == null) {
220 return null;
221 }
222
223 try {
224 return Double.valueOf(value);
225 } catch (final NumberFormatException e) {
226 return null;
227 }
228
229 }
230
231 /**
232 * Checks if the value can safely be converted to a double primitive.
233 *
234 * @param value The value validation is being performed on.
235 * @param locale The locale to use to parse the number (system default if
236 * null)
237 * @return the converted Double value.
238 */
239 public static Double formatDouble(final String value, final Locale locale) {
240 Double result = null;
241
242 if (value != null) {
243 NumberFormat formatter = null;
244 if (locale != null) {
245 formatter = NumberFormat.getInstance(locale);
246 } else {
247 formatter = NumberFormat.getInstance(Locale.getDefault());
248 }
249 final ParsePosition pos = new ParsePosition(0);
250 final Number num = formatter.parse(value, pos);
251
252 // If there was no error and we used the whole string
253 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
254 num.doubleValue() >= Double.MAX_VALUE * -1 &&
255 num.doubleValue() <= Double.MAX_VALUE) {
256 result = Double.valueOf(num.doubleValue());
257 }
258 }
259
260 return result;
261 }
262
263 /**
264 * Checks if the value can safely be converted to a float primitive.
265 *
266 * @param value The value validation is being performed on.
267 * @return the converted Float value.
268 */
269 public static Float formatFloat(final String value) {
270 if (value == null) {
271 return null;
272 }
273
274 try {
275 return Float.valueOf(value);
276 } catch (final NumberFormatException e) {
277 return null;
278 }
279
280 }
281
282 /**
283 * Checks if the value can safely be converted to a float primitive.
284 *
285 * @param value The value validation is being performed on.
286 * @param locale The locale to use to parse the number (system default if
287 * null)
288 * @return the converted Float value.
289 */
290 public static Float formatFloat(final String value, final Locale locale) {
291 Float result = null;
292
293 if (value != null) {
294 NumberFormat formatter = null;
295 if (locale != null) {
296 formatter = NumberFormat.getInstance(locale);
297 } else {
298 formatter = NumberFormat.getInstance(Locale.getDefault());
299 }
300 final ParsePosition pos = new ParsePosition(0);
301 final Number num = formatter.parse(value, pos);
302
303 // If there was no error and we used the whole string
304 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
305 num.doubleValue() >= Float.MAX_VALUE * -1 &&
306 num.doubleValue() <= Float.MAX_VALUE) {
307 result = Float.valueOf(num.floatValue());
308 }
309 }
310
311 return result;
312 }
313
314 /**
315 * Checks if the value can safely be converted to an int primitive.
316 *
317 * @param value The value validation is being performed on.
318 * @return the converted Integer value.
319 */
320 public static Integer formatInt(final String value) {
321 if (value == null) {
322 return null;
323 }
324
325 try {
326 return Integer.valueOf(value);
327 } catch (final NumberFormatException e) {
328 return null;
329 }
330
331 }
332
333 /**
334 * Checks if the value can safely be converted to an int primitive.
335 *
336 * @param value The value validation is being performed on.
337 * @param locale The locale to use to parse the number (system default if
338 * null)
339 * @return the converted Integer value.
340 */
341 public static Integer formatInt(final String value, final Locale locale) {
342 Integer result = null;
343
344 if (value != null) {
345 NumberFormat formatter = null;
346 if (locale != null) {
347 formatter = NumberFormat.getNumberInstance(locale);
348 } else {
349 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
350 }
351 formatter.setParseIntegerOnly(true);
352 final ParsePosition pos = new ParsePosition(0);
353 final Number num = formatter.parse(value, pos);
354
355 // If there was no error and we used the whole string
356 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
357 num.doubleValue() >= Integer.MIN_VALUE &&
358 num.doubleValue() <= Integer.MAX_VALUE) {
359 result = Integer.valueOf(num.intValue());
360 }
361 }
362
363 return result;
364 }
365
366 /**
367 * Checks if the value can safely be converted to a long primitive.
368 *
369 * @param value The value validation is being performed on.
370 * @return the converted Long value.
371 */
372 public static Long formatLong(final String value) {
373 if (value == null) {
374 return null;
375 }
376
377 try {
378 return Long.valueOf(value);
379 } catch (final NumberFormatException e) {
380 return null;
381 }
382
383 }
384
385 /**
386 * Checks if the value can safely be converted to a long primitive.
387 *
388 * @param value The value validation is being performed on.
389 * @param locale The locale to use to parse the number (system default if
390 * null)
391 * @return the converted Long value.
392 */
393 public static Long formatLong(final String value, final Locale locale) {
394 Long result = null;
395
396 if (value != null) {
397 NumberFormat formatter = null;
398 if (locale != null) {
399 formatter = NumberFormat.getNumberInstance(locale);
400 } else {
401 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
402 }
403 formatter.setParseIntegerOnly(true);
404 final ParsePosition pos = new ParsePosition(0);
405 final Number num = formatter.parse(value, pos);
406
407 // If there was no error and we used the whole string
408 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
409 num.doubleValue() >= Long.MIN_VALUE &&
410 num.doubleValue() <= Long.MAX_VALUE) {
411 result = Long.valueOf(num.longValue());
412 }
413 }
414
415 return result;
416 }
417
418 /**
419 * Checks if the value can safely be converted to a short primitive.
420 *
421 * @param value The value validation is being performed on.
422 * @return the converted Short value.
423 */
424 public static Short formatShort(final String value) {
425 if (value == null) {
426 return null;
427 }
428
429 try {
430 return Short.valueOf(value);
431 } catch (final NumberFormatException e) {
432 return null;
433 }
434
435 }
436
437 /**
438 * Checks if the value can safely be converted to a short primitive.
439 *
440 * @param value The value validation is being performed on.
441 * @param locale The locale to use to parse the number (system default if
442 * null)
443 * @return the converted Short value.
444 */
445 public static Short formatShort(final String value, final Locale locale) {
446 Short result = null;
447
448 if (value != null) {
449 NumberFormat formatter = null;
450 if (locale != null) {
451 formatter = NumberFormat.getNumberInstance(locale);
452 } else {
453 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
454 }
455 formatter.setParseIntegerOnly(true);
456 final ParsePosition pos = new ParsePosition(0);
457 final Number num = formatter.parse(value, pos);
458
459 // If there was no error and we used the whole string
460 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
461 num.doubleValue() >= Short.MIN_VALUE &&
462 num.doubleValue() <= Short.MAX_VALUE) {
463 result = Short.valueOf(num.shortValue());
464 }
465 }
466
467 return result;
468 }
469
470 /**
471 * Constructs a new instance.
472 *
473 * @deprecated Will be private in the next major version.
474 */
475 @Deprecated
476 public GenericTypeValidator() {
477 // empty
478 }
479
480 }
481