001 package org.apache.fulcrum.intake.validator;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.text.DateFormat;
023 import java.text.ParseException;
024 import java.text.SimpleDateFormat;
025
026 import java.util.ArrayList;
027 import java.util.Date;
028 import java.util.List;
029 import java.util.Map;
030
031 import org.apache.commons.lang.StringUtils;
032
033 import org.apache.fulcrum.intake.IntakeException;
034
035 /**
036 * Validates numbers with the following constraints in addition to those
037 * listed in DefaultValidator.
038 *
039 * <table>
040 * <tr><th>Name</th><th>Valid Values</th><th>Default Value</th></tr>
041 * <tr><td>format</td><td>see SimpleDateFormat javadoc</td>
042 * <td> </td></tr>
043 * <tr><td>formatx</td><td>see SimpleDateFormat javadoc</td>
044 * <td> </td></tr>
045 * <tr><td colspan=3>where x is >= 1 to specify multiple date
046 * formats. Only one format rule should have a message</td></tr>
047 * <tr><td>flexible</td><td>true, as long as DateFormat can parse the date,
048 * allow it, and false</td>
049 * <td>false</td></tr>
050 * </table>
051 *
052 * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
053 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
054 * @author <a href="mailto:Colin.Chalmers@maxware.nl">Colin Chalmers</a>
055 * @author <a href="mailto:jh@byteaction.de">Jürgen Hoffmann</a>
056 * @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
057 * @version $Id: DateStringValidator.java 535465 2007-05-05 06:58:06Z tv $
058 */
059 public class DateStringValidator
060 extends DefaultValidator
061 {
062 private static final String DEFAULT_DATE_MESSAGE =
063 "Date could not be parsed";
064
065 /** */
066 private List dateFormats = null;
067
068 /** */
069 private String dateFormatMessage = null;
070
071 /** */
072 private boolean flexible = false;
073
074 /** */
075 private DateFormat df = null;
076
077 /** */
078 private SimpleDateFormat sdf = null;
079
080 public DateStringValidator(Map paramMap)
081 throws IntakeException
082 {
083 init(paramMap);
084 }
085
086 /**
087 * Default Constructor
088 */
089 public DateStringValidator()
090 {
091 dateFormats = new ArrayList(5);
092 }
093
094 /**
095 * Constructor to use when initialising Object
096 *
097 * @param paramMap
098 * @throws InvalidMaskException
099 */
100 public void init(Map paramMap)
101 throws InvalidMaskException
102 {
103 super.init(paramMap);
104
105 Constraint constraint = (Constraint) paramMap.get(FORMAT_RULE_NAME);
106
107 if (constraint != null)
108 {
109 dateFormats.add(constraint.getValue());
110 setDateFormatMessage(constraint.getMessage());
111 }
112
113 for(int i = 1 ;; i++)
114 {
115 constraint = (Constraint) paramMap.get(FORMAT_RULE_NAME + i);
116
117 if (constraint == null)
118 {
119 break; // for
120 }
121
122 dateFormats.add(constraint.getValue());
123 setDateFormatMessage(constraint.getMessage());
124 }
125
126 if (StringUtils.isEmpty(dateFormatMessage))
127 {
128 dateFormatMessage = DEFAULT_DATE_MESSAGE;
129 }
130
131 constraint = (Constraint) paramMap.get(FLEXIBLE_RULE_NAME);
132
133 if (constraint != null)
134 {
135 flexible = Boolean.valueOf(constraint.getValue()).booleanValue();
136 }
137
138 if (dateFormats.size() == 0)
139 {
140 df = DateFormat.getInstance();
141 df.setLenient(flexible);
142 }
143 else
144 {
145 sdf = new SimpleDateFormat();
146 sdf.setLenient(flexible);
147 }
148 }
149
150 /**
151 * Determine whether a testValue meets the criteria specified
152 * in the constraints defined for this validator
153 *
154 * @param testValue a <code>String</code> to be tested
155 * @exception ValidationException containing an error message if the
156 * testValue did not pass the validation tests.
157 */
158 public void assertValidity(String testValue)
159 throws ValidationException
160 {
161 super.assertValidity(testValue);
162
163 if (required || StringUtils.isNotEmpty(testValue))
164 {
165 try
166 {
167 parse(testValue);
168 }
169 catch (ParseException e)
170 {
171 errorMessage = dateFormatMessage;
172 throw new ValidationException(dateFormatMessage);
173 }
174 }
175 }
176
177 /**
178 * Parses the String s according to the rules/formats for this validator.
179 * The formats provided by the "formatx" rules (where x is >= 1) are
180 * used <strong>before</strong> the "format" rules to allow for a display
181 * format that includes a 4 digit year, but that will parse the date using
182 * a format that accepts 2 digit years.
183 *
184 * @throws ParseException indicates that the string could not be
185 * parsed into a date.
186 */
187 public Date parse(String s)
188 throws ParseException
189 {
190 Date date = null;
191
192 if (s == null)
193 {
194 throw new ParseException("Input string was null", -1);
195 }
196
197 for (int i = 1; i < dateFormats.size() && date == null; i++)
198 {
199 sdf.applyPattern((String) dateFormats.get(i));
200
201 try
202 {
203 date = sdf.parse(s);
204 }
205 catch (ParseException e)
206 {
207 // ignore
208 }
209 }
210
211 if (date == null)
212 {
213 sdf.applyPattern((String) dateFormats.get(0));
214
215 try
216 {
217 date = sdf.parse(s);
218 }
219 catch (ParseException e)
220 {
221 // ignore
222 }
223 }
224
225 if (date == null && df != null)
226 {
227 date = df.parse(s);
228 }
229
230 // if the date still has not been parsed at this point, throw
231 // a ParseException.
232 if (date == null)
233 {
234 throw new ParseException("Could not parse the date", 0);
235 }
236
237 return date;
238 }
239
240 /**
241 * Formats a date into a String. The format used is from
242 * the first format rule found for the field.
243 *
244 * @param date the Date object to convert into a string.
245 * @return formatted date
246 */
247 public String format(Date date)
248 {
249 String s = null;
250 if (date != null)
251 {
252 sdf.applyPattern((String) dateFormats.get(0));
253 s = sdf.format(date);
254 }
255 return s;
256 }
257
258
259 // ************************************************************
260 // ** Bean accessor methods **
261 // ************************************************************
262
263 /**
264 * Get the value of minLengthMessage.
265 *
266 * @return value of minLengthMessage.
267 */
268 public String getDateFormatMessage()
269 {
270 return dateFormatMessage;
271 }
272
273 /**
274 * Only sets the message if the new message has some information.
275 * So the last setMessage call with valid data wins. But later calls
276 * with null or empty string will not affect a previous valid setting.
277 *
278 * @param message Value to assign to minLengthMessage.
279 */
280 public void setDateFormatMessage(String message)
281 {
282 if (StringUtils.isNotEmpty(message))
283 {
284 dateFormatMessage = message;
285 }
286 }
287
288 /**
289 * Get the value of dateFormats.
290 *
291 * @return value of dateFormats.
292 */
293 public List getDateFormats()
294 {
295 return dateFormats;
296 }
297
298 /**
299 * Set the value of dateFormats.
300 *
301 * @param formats Value to assign to dateFormats.
302 */
303 public void setDateFormats(List formats)
304 {
305 this.dateFormats = formats;
306 }
307
308 /**
309 * Get the value of flexible.
310 *
311 * @return value of flexible.
312 */
313 public boolean isFlexible()
314 {
315 return flexible;
316 }
317
318 /**
319 * Set the value of flexible.
320 *
321 * @param flexible Value to assign to flexible.
322 */
323 public void setFlexible(boolean flexible)
324 {
325 this.flexible = flexible;
326 }
327 }