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 }