001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.validator;
018    
019    import java.io.Serializable;
020    import java.util.Date;
021    import java.util.Locale;
022    import java.text.DateFormat;
023    import java.text.SimpleDateFormat;
024    import java.text.NumberFormat;
025    import java.text.ParseException;
026    import java.text.ParsePosition;
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    
030    /**
031     *  This class contains basic methods for performing validations that return the
032     *  correctly typed class based on the validation performed.
033     *
034     * @version $Revision: 1227719 $ $Date: 2012-01-05 18:45:51 +0100 (Thu, 05 Jan 2012) $
035     */
036    public class GenericTypeValidator implements Serializable {
037    
038        private static final long serialVersionUID = 5487162314134261703L;
039    
040        /**
041         *  Checks if the value can safely be converted to a byte primitive.
042         *
043         *@param  value  The value validation is being performed on.
044         *@return the converted Byte value.
045         */
046        public static Byte formatByte(String value) {
047            if (value == null) {
048                return null;
049            }
050    
051            try {
052                return new Byte(value);
053            } catch (NumberFormatException e) {
054                return null;
055            }
056    
057        }
058    
059        /**
060         *  Checks if the value can safely be converted to a byte primitive.
061         *
062         *@param  value   The value validation is being performed on.
063         *@param  locale  The locale to use to parse the number (system default if
064         *      null)
065         *@return the converted Byte value.
066         */
067        public static Byte formatByte(String value, Locale locale) {
068            Byte result = null;
069    
070            if (value != null) {
071                NumberFormat formatter = null;
072                if (locale != null) {
073                    formatter = NumberFormat.getNumberInstance(locale);
074                } else {
075                    formatter = NumberFormat.getNumberInstance(Locale.getDefault());
076                }
077                formatter.setParseIntegerOnly(true);
078                ParsePosition pos = new ParsePosition(0);
079                Number num = formatter.parse(value, pos);
080    
081                // If there was no error      and we used the whole string
082                if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length()) {
083                    if (num.doubleValue() >= Byte.MIN_VALUE &&
084                        num.doubleValue() <= Byte.MAX_VALUE) {
085                        result = new Byte(num.byteValue());
086                    }
087                }
088            }
089    
090            return result;
091        }
092    
093        /**
094         *  Checks if the value can safely be converted to a short primitive.
095         *
096         *@param  value  The value validation is being performed on.
097         *@return the converted Short value.
098         */
099        public static Short formatShort(String value) {
100            if (value == null) {
101                return null;
102            }
103    
104            try {
105                return new Short(value);
106            } catch (NumberFormatException e) {
107                return null;
108            }
109    
110        }
111    
112        /**
113         *  Checks if the value can safely be converted to a short primitive.
114         *
115         *@param  value   The value validation is being performed on.
116         *@param  locale  The locale to use to parse the number (system default if
117         *      null)vv
118         *@return the converted Short value.
119         */
120        public static Short formatShort(String value, Locale locale) {
121            Short result = null;
122    
123            if (value != null) {
124                NumberFormat formatter = null;
125                if (locale != null) {
126                    formatter = NumberFormat.getNumberInstance(locale);
127                } else {
128                    formatter = NumberFormat.getNumberInstance(Locale.getDefault());
129                }
130                formatter.setParseIntegerOnly(true);
131                ParsePosition pos = new ParsePosition(0);
132                Number num = formatter.parse(value, pos);
133    
134                // If there was no error      and we used the whole string
135                if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length()) {
136                    if (num.doubleValue() >= Short.MIN_VALUE &&
137                        num.doubleValue() <= Short.MAX_VALUE) {
138                        result = new Short(num.shortValue());
139                    }
140                }
141            }
142    
143            return result;
144        }
145    
146        /**
147         *  Checks if the value can safely be converted to a int primitive.
148         *
149         *@param  value  The value validation is being performed on.
150         *@return the converted Integer value.
151         */
152        public static Integer formatInt(String value) {
153            if (value == null) {
154                return null;
155            }
156    
157            try {
158                return new Integer(value);
159            } catch (NumberFormatException e) {
160                return null;
161            }
162    
163        }
164    
165        /**
166         *  Checks if the value can safely be converted to an int primitive.
167         *
168         *@param  value   The value validation is being performed on.
169         *@param  locale  The locale to use to parse the number (system default if
170         *      null)
171         *@return the converted Integer value.
172         */
173        public static Integer formatInt(String value, Locale locale) {
174            Integer result = null;
175    
176            if (value != null) {
177                NumberFormat formatter = null;
178                if (locale != null) {
179                    formatter = NumberFormat.getNumberInstance(locale);
180                } else {
181                    formatter = NumberFormat.getNumberInstance(Locale.getDefault());
182                }
183                formatter.setParseIntegerOnly(true);
184                ParsePosition pos = new ParsePosition(0);
185                Number num = formatter.parse(value, pos);
186    
187                // If there was no error      and we used the whole string
188                if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length()) {
189                    if (num.doubleValue() >= Integer.MIN_VALUE &&
190                        num.doubleValue() <= Integer.MAX_VALUE) {
191                        result = new Integer(num.intValue());
192                    }
193                }
194            }
195    
196            return result;
197        }
198    
199        /**
200         *  Checks if the value can safely be converted to a long primitive.
201         *
202         *@param  value  The value validation is being performed on.
203         *@return the converted Long value.
204         */
205        public static Long formatLong(String value) {
206            if (value == null) {
207                return null;
208            }
209    
210            try {
211                return new Long(value);
212            } catch (NumberFormatException e) {
213                return null;
214            }
215    
216        }
217    
218        /**
219         *  Checks if the value can safely be converted to a long primitive.
220         *
221         *@param  value   The value validation is being performed on.
222         *@param  locale  The locale to use to parse the number (system default if
223         *      null)
224         *@return the converted Long value.
225         */
226        public static Long formatLong(String value, Locale locale) {
227            Long result = null;
228    
229            if (value != null) {
230                NumberFormat formatter = null;
231                if (locale != null) {
232                    formatter = NumberFormat.getNumberInstance(locale);
233                } else {
234                    formatter = NumberFormat.getNumberInstance(Locale.getDefault());
235                }
236                formatter.setParseIntegerOnly(true);
237                ParsePosition pos = new ParsePosition(0);
238                Number num = formatter.parse(value, pos);
239    
240                // If there was no error      and we used the whole string
241                if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length()) {
242                    if (num.doubleValue() >= Long.MIN_VALUE &&
243                        num.doubleValue() <= Long.MAX_VALUE) {
244                        result = new Long(num.longValue());
245                    }
246                }
247            }
248    
249            return result;
250        }
251    
252        /**
253         *  Checks if the value can safely be converted to a float primitive.
254         *
255         *@param  value  The value validation is being performed on.
256         *@return the converted Float value.
257         */
258        public static Float formatFloat(String value) {
259            if (value == null) {
260                return null;
261            }
262    
263            try {
264                return new Float(value);
265            } catch (NumberFormatException e) {
266                return null;
267            }
268    
269        }
270    
271        /**
272         *  Checks if the value can safely be converted to a float primitive.
273         *
274         *@param  value   The value validation is being performed on.
275         *@param  locale  The locale to use to parse the number (system default if
276         *      null)
277         *@return the converted Float value.
278         */
279        public static Float formatFloat(String value, Locale locale) {
280            Float result = null;
281    
282            if (value != null) {
283                NumberFormat formatter = null;
284                if (locale != null) {
285                    formatter = NumberFormat.getInstance(locale);
286                } else {
287                    formatter = NumberFormat.getInstance(Locale.getDefault());
288                }
289                ParsePosition pos = new ParsePosition(0);
290                Number num = formatter.parse(value, pos);
291    
292                // If there was no error      and we used the whole string
293                if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length()) {
294                    if (num.doubleValue() >= (Float.MAX_VALUE * -1) &&
295                        num.doubleValue() <= Float.MAX_VALUE) {
296                        result = new Float(num.floatValue());
297                    }
298                }
299            }
300    
301            return result;
302        }
303    
304        /**
305         *  Checks if the value can safely be converted to a double primitive.
306         *
307         *@param  value  The value validation is being performed on.
308         *@return the converted Double value.
309         */
310        public static Double formatDouble(String value) {
311            if (value == null) {
312                return null;
313            }
314    
315            try {
316                return new Double(value);
317            } catch (NumberFormatException e) {
318                return null;
319            }
320    
321        }
322    
323        /**
324         *  Checks if the value can safely be converted to a double primitive.
325         *
326         *@param  value   The value validation is being performed on.
327         *@param  locale  The locale to use to parse the number (system default if
328         *      null)
329         *@return the converted Double value.
330         */
331        public static Double formatDouble(String value, Locale locale) {
332            Double result = null;
333    
334            if (value != null) {
335                NumberFormat formatter = null;
336                if (locale != null) {
337                    formatter = NumberFormat.getInstance(locale);
338                } else {
339                    formatter = NumberFormat.getInstance(Locale.getDefault());
340                }
341                ParsePosition pos = new ParsePosition(0);
342                Number num = formatter.parse(value, pos);
343    
344                // If there was no error      and we used the whole string
345                if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length()) {
346                    if (num.doubleValue() >= (Double.MAX_VALUE * -1) &&
347                        num.doubleValue() <= Double.MAX_VALUE) {
348                        result = new Double(num.doubleValue());
349                    }
350                }
351            }
352    
353            return result;
354        }
355    
356        /**
357         *  <p>
358         *
359         *  Checks if the field is a valid date. The <code>Locale</code> is used
360         *  with <code>java.text.DateFormat</code>. The setLenient method is set to
361         *  <code>false</code> for all.</p>
362         *
363         *@param  value   The value validation is being performed on.
364         *@param  locale  The Locale to use to parse the date (system default if
365         *      null)
366         *@return the converted Date value.
367         */
368        public static Date formatDate(String value, Locale locale) {
369            Date date = null;
370    
371            if (value == null) {
372                return null;
373            }
374    
375            try {
376                // Get the formatters to check against
377                DateFormat formatterShort = null;
378                DateFormat formatterDefault = null;
379                if (locale != null) {
380                    formatterShort =
381                       DateFormat.getDateInstance(DateFormat.SHORT, locale);
382                    formatterDefault =
383                       DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
384                } else {
385                    formatterShort =
386                        DateFormat.getDateInstance(
387                        DateFormat.SHORT,
388                        Locale.getDefault());
389                    formatterDefault =
390                       DateFormat.getDateInstance(
391                       DateFormat.DEFAULT,
392                       Locale.getDefault());
393                }
394    
395                // Turn off lenient parsing
396                formatterShort.setLenient(false);
397                formatterDefault.setLenient(false);
398    
399                // Firstly, try with the short form
400                try {
401                   date = formatterShort.parse(value);
402                } catch (ParseException e) {
403                   // Fall back on the default one
404                   date = formatterDefault.parse(value);
405                }
406            } catch (ParseException e) {
407                // Bad date, so log and return null
408                Log log = LogFactory.getLog(GenericTypeValidator.class);
409                if (log.isDebugEnabled()) {
410                    log.debug("Date parse failed value=[" + value  + "], " +
411                                               "locale=[" + locale + "] "  + e);
412                }
413            }
414    
415            return date;
416        }
417    
418        /**
419         *  <p>
420         *  Checks if the field is a valid date. The pattern is used with <code>java.text.SimpleDateFormat</code>
421         *  . If strict is true, then the length will be checked so '2/12/1999' will
422         *  not pass validation with the format 'MM/dd/yyyy' because the month isn't
423         *  two digits. The setLenient method is set to <code>false</code> for all.
424         *  </p>
425         *
426         *@param  value        The value validation is being performed on.
427         *@param  datePattern  The pattern passed to <code>SimpleDateFormat</code>.
428         *@param  strict       Whether or not to have an exact match of the
429         *      datePattern.
430         *@return the converted Date value.
431         */
432        public static Date formatDate(String value, String datePattern, boolean strict) {
433            Date date = null;
434    
435            if (value == null
436                 || datePattern == null
437                 || datePattern.length() == 0) {
438                return null;
439            }
440    
441            try {
442                SimpleDateFormat formatter = new SimpleDateFormat(datePattern);
443                formatter.setLenient(false);
444    
445                date = formatter.parse(value);
446    
447                if (strict) {
448                    if (datePattern.length() != value.length()) {
449                        date = null;
450                    }
451                }
452            } catch (ParseException e) {
453                // Bad date so return null
454                Log log = LogFactory.getLog(GenericTypeValidator.class);
455                if (log.isDebugEnabled()) {
456                    log.debug("Date parse failed value=[" + value       + "], " +
457                                              "pattern=[" + datePattern + "], " +
458                                               "strict=[" + strict      + "] "  + e);
459                }
460            }
461    
462            return date;
463        }
464    
465        /**
466         *  <p>
467         *  Checks if the field is a valid credit card number.</p> <p>
468         *
469         *  Reference Sean M. Burke's <a href="http://www.ling.nwu.edu/~sburke/pub/luhn_lib.pl">
470         *  script</a> .</p>
471         *
472         *@param  value  The value validation is being performed on.
473         *@return the converted Credit Card number.
474         */
475        public static Long formatCreditCard(String value) {
476            return GenericValidator.isCreditCard(value) ? new Long(value) : null;
477        }
478    
479    }
480