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 */
017package org.apache.commons.validator.routines;
018
019import java.text.Format;
020import java.text.ParsePosition;
021import java.util.Locale;
022import java.io.Serializable;
023
024/**
025 * <p>Abstract class for <i>Format</i> based Validation.</p>
026 *
027 * <p>This is a <i>base</i> class for building Date and Number
028 *    Validators using format parsing.</p>
029 *
030 * @version $Revision: 1227719 $ $Date: 2012-01-05 18:45:51 +0100 (Do, 05 Jan 2012) $
031 * @since Validator 1.3.0
032 */
033public abstract class AbstractFormatValidator implements Serializable {
034
035    private static final long serialVersionUID = -4690687565200568258L;
036
037    private final boolean strict;
038
039    /**
040     * Construct an instance with the specified strict setting.
041     *
042     * @param strict <code>true</code> if strict
043     *        <code>Format</code> parsing should be used.
044     */
045    public AbstractFormatValidator(boolean strict) {
046        this.strict = strict;
047    }
048
049    /**
050     * <p>Indicates whether validated values should adhere
051     *    strictly to the <code>Format</code> used.</p>
052     *
053     * <p>Typically implementations of <code>Format</code>
054     *    ignore invalid characters at the end of the value
055     *    and just stop parsing. For example parsing a date
056     *    value of <code>01/01/20x0</code> using a pattern
057     *    of <code>dd/MM/yyyy</code> will result in a year
058     *    of <code>20</code> if <code>strict</code> is set
059     *    to <code>false</code>, whereas setting <code>strict</code>
060     *    to <code>true</code> will cause this value to fail
061     *    validation.</p>
062     *
063     * @return <code>true</code> if strict <code>Format</code>
064     *         parsing should be used.
065     */
066    public boolean isStrict() {
067        return strict;
068    }
069
070    /**
071     * <p>Validate using the default <code>Locale</code>.
072     *
073     * @param value The value validation is being performed on.
074     * @return <code>true</code> if the value is valid.
075     */
076    public boolean isValid(String value) {
077        return isValid(value, (String)null, (Locale)null);
078    }
079
080    /**
081     * <p>Validate using the specified <i>pattern</i>.
082     *
083     * @param value The value validation is being performed on.
084     * @param pattern The pattern used to validate the value against.
085     * @return <code>true</code> if the value is valid.
086     */
087    public boolean isValid(String value, String pattern) {
088        return isValid(value, pattern, (Locale)null);
089    }
090
091    /**
092     * <p>Validate using the specified <code>Locale</code>.
093     *
094     * @param value The value validation is being performed on.
095     * @param locale The locale to use for the Format, defaults to the default
096     * @return <code>true</code> if the value is valid.
097     */
098    public boolean isValid(String value, Locale locale) {
099        return isValid(value, (String)null, locale);
100    }
101
102    /**
103     * <p>Validate using the specified pattern and/or <code>Locale</code>.
104     *
105     * @param value The value validation is being performed on.
106     * @param pattern The pattern used to format the value.
107     * @param locale The locale to use for the Format, defaults to the default
108     * @return <code>true</code> if the value is valid.
109     */
110    public abstract boolean isValid(String value, String pattern, Locale locale);
111
112    /**
113     * <p>Format an object into a <code>String</code> using
114     * the default Locale.</p>
115     *
116     * @param value The value validation is being performed on.
117     * @return The value formatted as a <code>String</code>.
118     */
119    public String format(Object value) {
120        return format(value, (String)null, (Locale)null);
121    }
122
123    /**
124     * <p>Format an object into a <code>String</code> using
125     * the specified pattern.</p>
126     *
127     * @param value The value validation is being performed on.
128     * @param pattern The pattern used to format the value.
129     * @return The value formatted as a <code>String</code>.
130     */
131    public String format(Object value, String pattern) {
132        return format(value, pattern, (Locale)null);
133    }
134
135    /**
136     * <p>Format an object into a <code>String</code> using
137     * the specified Locale.</p>
138     *
139     * @param value The value validation is being performed on.
140     * @param locale The locale to use for the Format.
141     * @return The value formatted as a <code>String</code>.
142     */
143    public String format(Object value, Locale locale) {
144        return format(value, (String)null, locale);
145    }
146
147    /**
148     * <p>Format an object using the specified pattern and/or
149     *    <code>Locale</code>.
150     *
151     * @param value The value validation is being performed on.
152     * @param pattern The pattern used to format the value.
153     * @param locale The locale to use for the Format.
154     * @return The value formatted as a <code>String</code>.
155     */
156    public String format(Object value, String pattern, Locale locale) {
157        Format formatter = getFormat(pattern, locale);
158        return format(value, formatter);
159    }
160
161    /**
162     * <p>Format a value with the specified <code>Format</code>.</p>
163     *
164     * @param value The value to be formatted.
165     * @param formatter The Format to use.
166     * @return The formatted value.
167     */
168    protected String format(Object value, Format formatter) {
169        return formatter.format(value);
170    }
171
172    /**
173     * <p>Parse the value with the specified <code>Format</code>.</p>
174     *
175     * @param value The value to be parsed.
176     * @param formatter The Format to parse the value with.
177     * @return The parsed value if valid or <code>null</code> if invalid.
178     */
179    protected Object parse(String value, Format formatter) {
180
181        ParsePosition pos = new ParsePosition(0);
182        Object parsedValue = formatter.parseObject(value, pos);
183        if (pos.getErrorIndex() > -1) {
184            return null;
185        }
186
187        if (isStrict() && pos.getIndex() < value.length()) {
188            return null;
189        }
190
191        if (parsedValue != null) {
192            parsedValue = processParsedValue(parsedValue, formatter);
193        }
194
195        return parsedValue;
196
197    }
198
199    /**
200     * <p>Process the parsed value, performing any further validation
201     *    and type conversion required.</p>
202     *
203     * @param value The parsed object created.
204     * @param formatter The Format used to parse the value with.
205     * @return The parsed value converted to the appropriate type
206     *         if valid or <code>null</code> if invalid.
207     */
208    protected abstract Object processParsedValue(Object value, Format formatter);
209
210    /**
211     * <p>Returns a <code>Format</code> for the specified <i>pattern</i>
212     *    and/or <code>Locale</code>.</p>
213     *
214     * @param pattern The pattern used to validate the value against or
215     *        <code>null</code> to use the default for the <code>Locale</code>.
216     * @param locale The locale to use for the currency format, system default if null.
217     * @return The <code>NumberFormat</code> to created.
218     */
219    protected abstract Format getFormat(String pattern, Locale locale);
220
221}