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 }