View Javadoc

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    *     http://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.cli2.validation;
18  
19  import java.text.DateFormat;
20  import java.text.ParsePosition;
21  
22  import java.util.Date;
23  import java.util.List;
24  import java.util.ListIterator;
25  
26  import org.apache.commons.cli2.resource.ResourceConstants;
27  import org.apache.commons.cli2.resource.ResourceHelper;
28  
29  /**
30   * The <code>DateValidator</code> validates the argument values
31   * are date or time value(s).
32   *
33   * The following example shows how to validate that
34   * an argument value(s) is a Date of the following
35   * type: d/M/yy (see {@link java.text.DateFormat}).
36   *
37   * <pre>
38   * DateFormat date = new SimpleDateFormat("d/M/yy");
39   * ...
40   * ArgumentBuilder builder = new ArgumentBuilder();
41   * Argument dateFormat =
42   *     builder.withName("date");
43   *            .withValidator(new DateValidator(dateFormat));
44   * </pre>
45   *
46   * The following example shows how to validate that
47   * an argument value(s) is a time of the following
48   * type: HH:mm:ss (see {@link java.text.DateFormat}).
49   *
50   * <pre>
51   * DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
52   * ...
53   * ArgumentBuilder builder = new ArgumentBuilder();
54   * Argument time =
55   *     builder.withName("time");
56   *            .withValidator(new DateValidator(timeFormat));
57   * </pre>
58   *
59   * @author John Keyes
60   *
61   * @see java.text.DateFormat
62   */
63  public class DateValidator implements Validator {
64      /** i18n */
65      private static final ResourceHelper resources = ResourceHelper.getResourceHelper();
66  
67      /** an array of permitted DateFormats */
68      private DateFormat[] formats;
69  
70      /** minimum Date allowed i.e: a valid date occurs later than this date */
71      private Date minimum;
72  
73      /** maximum Date allowed i.e: a valid date occurs earlier than this date */
74      private Date maximum;
75  
76      /** leniant parsing */
77      private boolean isLenient;
78  
79      /**
80       * Creates a Validator for the default date/time format
81       */
82      public DateValidator() {
83          this(DateFormat.getInstance());
84      }
85  
86      /**
87       * Creates a Validator for the specified DateFormat.
88       *
89       * @param format
90       *            a DateFormat which dates must conform to
91       */
92      public DateValidator(final DateFormat format) {
93          setFormat(format);
94      }
95  
96      /**
97       * Creates a Validator for the List of specified DateFormats.
98       *
99       * @param formats
100      *            a List of DateFormats which dates must conform to
101      */
102     public DateValidator(final List formats) {
103         setFormats(formats);
104     }
105 
106     /**
107      * Creates a Validator for dates.
108      *
109      * @return DateValidator a Validator for dates
110      */
111     public static DateValidator getDateInstance() {
112         return new DateValidator(DateFormat.getDateInstance());
113     }
114 
115     /**
116      * Creates a Validator for times.
117      *
118      * @return DateValidator a Validator for times
119      */
120     public static DateValidator getTimeInstance() {
121         return new DateValidator(DateFormat.getTimeInstance());
122     }
123 
124     /**
125      * Creates a Validator for date/times
126      *
127      * @return DateValidator a Validator for date/times
128      */
129     public static DateValidator getDateTimeInstance() {
130         return new DateValidator(DateFormat.getDateTimeInstance());
131     }
132 
133     /**
134      * Validate each String value in the specified List against this instances
135      * permitted DateFormats.
136      *
137      * If a value is valid then it's <code>String</code> value in the list is
138      * replaced with it's <code>Date</code> value.
139      *
140      * @see org.apache.commons.cli2.validation.Validator#validate(java.util.List)
141      */
142     public void validate(final List values)
143         throws InvalidArgumentException {
144         // for each value
145         for (final ListIterator i = values.listIterator(); i.hasNext();) {
146             final String value = (String) i.next();
147 
148             Date date = null;
149 
150             // create a resuable ParsePosition instance
151             final ParsePosition pp = new ParsePosition(0);
152 
153             // for each permitted DateFormat
154             for (int f = 0; (f < this.formats.length) && (date == null); ++f) {
155                 // reset the parse position
156                 pp.setIndex(0);
157                 date = this.formats[f].parse(value, pp);
158 
159                 // if the wrong number of characters have been parsed
160                 if (pp.getIndex() < value.length()) {
161                     date = null;
162                 }
163             }
164 
165             // if date has not been set throw an InvalidArgumentException
166             if (date == null) {
167                 throw new InvalidArgumentException(value);
168             }
169 
170             // if the date is outside the bounds
171             if (isDateEarlier(date) || isDateLater(date)) {
172                 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.DATEVALIDATOR_DATE_OUTOFRANGE,
173                                                                         value));
174             }
175 
176             // replace the value in the list with the actual Date
177             i.set(date);
178         }
179     }
180 
181     /**
182      * Sets whether this validator uses lenient parsing.
183      *
184      * @param lenient whether this validator uses lenient parsing
185      */
186     public void setLenient(final boolean lenient) {
187         for (int i = 0; i < this.formats.length; i++) {
188             this.formats[i].setLenient(lenient);
189         }
190 
191         this.isLenient = lenient;
192     }
193 
194     /**
195      * Returns whether this validator uses lenient parsing.
196      *
197      * @return whether this validator uses lenient parsing
198      */
199     public boolean isLenient() {
200         return this.isLenient;
201     }
202 
203     /**
204      * Returns the maximum date permitted.
205      *
206      * @return Date the maximum date permitted. If no maximum date has been
207      *         specified then return <code>null</code>.
208      */
209     public Date getMaximum() {
210         return maximum;
211     }
212 
213     /**
214      * Sets the maximum Date to the specified value.
215      *
216      * @param maximum
217      *            the maximum Date permitted
218      */
219     public void setMaximum(final Date maximum) {
220         this.maximum = maximum;
221     }
222 
223     /**
224      * Returns the minimum date permitted.
225      *
226      * @return Date the minimum date permitted. If no minimum date has been
227      *         specified then return <code>null</code>.
228      */
229     public Date getMinimum() {
230         return minimum;
231     }
232 
233     /**
234      * Sets the minimum Date to the specified value.
235      *
236      * @param minimum
237      *            the minimum Date permitted
238      */
239     public void setMinimum(Date minimum) {
240         this.minimum = minimum;
241     }
242 
243     /**
244      * Returns whether the specified Date is later than the maximum date.
245      *
246      * @param date
247      *            the Date to evaluate
248      *
249      * @return boolean whether <code>date</code> is earlier than the maximum
250      *         date
251      */
252     private boolean isDateLater(Date date) {
253         return (maximum != null) && (date.getTime() > maximum.getTime());
254     }
255 
256     /**
257      * Returns whether the specified Date is earlier than the minimum date.
258      *
259      * @param date
260      *            the Date to evaluate
261      *
262      * @return boolean whether <code>date</code> is earlier than the minimum
263      *         date
264      */
265     private boolean isDateEarlier(Date date) {
266         return (minimum != null) && (date.getTime() < minimum.getTime());
267     }
268 
269     /**
270      * Sets the date format permitted.
271      *
272      * @param format
273      *              the format to use
274      */
275     public void setFormat(final DateFormat format) {
276         setFormats(new DateFormat[] { format });
277     }
278 
279     /**
280      * Sets the date formats permitted.
281      *
282      * @param formats
283      *               the List of DateFormats to use
284      */
285     public void setFormats(final List formats) {
286         setFormats((DateFormat[]) formats.toArray(new DateFormat[formats.size()]));
287     }
288 
289     /**
290      * Sets the date formats permitted.
291      *
292      * @param formats
293      *               the array of DateFormats to use
294      */
295     public void setFormats(final DateFormat[] formats) {
296         this.formats = formats;
297         setLenient(this.isLenient);
298     }
299 
300     /**
301      * Gets the date formats permitted.
302      *
303      * @return the permitted formats
304      */
305     public DateFormat[] getFormats() {
306         return this.formats;
307     }
308 }