1 package org.apache.turbine.services.intake.validator;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.text.DateFormat;
20 import java.text.ParseException;
21 import java.text.SimpleDateFormat;
22
23 import java.util.ArrayList;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.apache.commons.lang.StringUtils;
29
30 import org.apache.turbine.services.intake.IntakeException;
31
32 /***
33 * Validates numbers with the following constraints in addition to those
34 * listed in DefaultValidator.
35 *
36 * <table>
37 * <tr><th>Name</th><th>Valid Values</th><th>Default Value</th></tr>
38 * <tr><td>format</td><td>see SimpleDateFormat javadoc</td>
39 * <td> </td></tr>
40 * <tr><td>formatx</td><td>see SimpleDateFormat javadoc</td>
41 * <td> </td></tr>
42 * <tr><td colspan=3>where x is >= 1 to specify multiple date
43 * formats. Only one format rule should have a message</td></tr>
44 * <tr><td>flexible</td><td>true, as long as DateFormat can parse the date,
45 * allow it, and false</td>
46 * <td>false</td></tr>
47 * </table>
48 *
49 * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
50 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
51 * @author <a href="mailto:Colin.Chalmers@maxware.nl">Colin Chalmers</a>
52 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
53 * @version $Id: DateStringValidator.java 264148 2005-08-29 14:21:04Z henning $
54 */
55 public class DateStringValidator
56 extends DefaultValidator
57 {
58 private static final String DEFAULT_DATE_MESSAGE =
59 "Date could not be parsed";
60
61 /*** */
62 private List dateFormats = null;
63
64 /*** */
65 private String dateFormatMessage = null;
66
67 /*** */
68 private boolean flexible = false;
69
70 /*** */
71 private DateFormat df = null;
72
73 /*** */
74 private SimpleDateFormat sdf = null;
75
76 public DateStringValidator(final Map paramMap)
77 throws IntakeException
78 {
79 init(paramMap);
80 }
81
82 /***
83 * Default Constructor
84 */
85 public DateStringValidator()
86 {
87 dateFormats = new ArrayList(5);
88 }
89
90 /***
91 * Constructor to use when initialising Object
92 *
93 * @param paramMap
94 * @throws InvalidMaskException
95 */
96 public void init(final Map paramMap)
97 throws InvalidMaskException
98 {
99 super.init(paramMap);
100
101 Constraint constraint = (Constraint) paramMap.get(FORMAT_RULE_NAME);
102
103 if (constraint != null)
104 {
105 dateFormats.add(constraint.getValue());
106 setDateFormatMessage(constraint.getMessage());
107 }
108
109 for(int i = 1 ;; i++)
110 {
111 constraint = (Constraint) paramMap.get(FORMAT_RULE_NAME + i);
112
113 if (constraint == null)
114 {
115 break;
116 }
117
118 dateFormats.add(constraint.getValue());
119 setDateFormatMessage(constraint.getMessage());
120 }
121
122 if (StringUtils.isEmpty(dateFormatMessage))
123 {
124 dateFormatMessage = DEFAULT_DATE_MESSAGE;
125 }
126
127 constraint = (Constraint) paramMap.get(FLEXIBLE_RULE_NAME);
128
129 if (constraint != null)
130 {
131 flexible = Boolean.valueOf(constraint.getValue()).booleanValue();
132 }
133
134 if (dateFormats.size() == 0)
135 {
136 df = DateFormat.getInstance();
137 sdf = null;
138 df.setLenient(flexible);
139 }
140 else
141 {
142 sdf = new SimpleDateFormat();
143 df = null;
144 sdf.setLenient(flexible);
145 }
146 }
147
148 /***
149 * Determine whether a testValue meets the criteria specified
150 * in the constraints defined for this validator
151 *
152 * @param testValue a <code>String</code> to be tested
153 * @exception ValidationException containing an error message if the
154 * testValue did not pass the validation tests.
155 */
156 public void assertValidity(final String testValue)
157 throws ValidationException
158 {
159 super.assertValidity(testValue);
160
161 if (required || StringUtils.isNotEmpty(testValue))
162 {
163 try
164 {
165 parse(testValue);
166 }
167 catch (ParseException e)
168 {
169 errorMessage = dateFormatMessage;
170 throw new ValidationException(dateFormatMessage);
171 }
172 }
173 }
174
175 /***
176 * Parses the String s according to the rules/formats for this validator.
177 * The formats provided by the "formatx" rules (where x is >= 1) are
178 * used <strong>before</strong> the "format" rules to allow for a display
179 * format that includes a 4 digit year, but that will parse the date using
180 * a format that accepts 2 digit years.
181 *
182 * @throws ParseException indicates that the string could not be
183 * parsed into a date.
184 */
185 public Date parse(final String s)
186 throws ParseException
187 {
188 Date date = null;
189
190 if (s == null)
191 {
192 throw new ParseException("Input string was null", -1);
193 }
194
195 if (sdf != null)
196 {
197
198
199 for (int i = 1 ; i < dateFormats.size() - 1; i++)
200 {
201 sdf.applyPattern((String) dateFormats.get(i));
202
203 try
204 {
205 date = sdf.parse(s);
206 break;
207 }
208 catch (ParseException e)
209 {
210
211 }
212 }
213
214
215
216
217
218 if (date == null)
219 {
220 sdf.applyPattern((String) dateFormats.get(0));
221
222 try
223 {
224 date = sdf.parse(s);
225 }
226 catch (ParseException e)
227 {
228
229 }
230 }
231 }
232
233
234
235
236 if (date == null && df != null)
237 {
238 date = df.parse(s);
239 }
240
241
242
243 if (date == null)
244 {
245 throw new ParseException("Could not parse the date", 0);
246 }
247
248 return date;
249 }
250
251 /***
252 * Formats a date into a String. The format used is from
253 * the first format rule found for the field.
254 *
255 * @param date the Date object to convert into a string.
256 * @return formatted date
257 */
258 public String format(final Date date)
259 {
260 String s = null;
261 if (date != null)
262 {
263 if (sdf != null)
264 {
265 sdf.applyPattern((String) dateFormats.get(0));
266 s = sdf.format(date);
267 }
268 else
269 {
270 s = df.format(date);
271 }
272 }
273 return s;
274 }
275
276
277
278
279
280
281 /***
282 * Get the value of minLengthMessage.
283 *
284 * @return value of minLengthMessage.
285 */
286 public String getDateFormatMessage()
287 {
288 return dateFormatMessage;
289 }
290
291 /***
292 * Only sets the message if the new message has some information.
293 * So the last setMessage call with valid data wins. But later calls
294 * with null or empty string will not affect a previous valid setting.
295 *
296 * @param message Value to assign to minLengthMessage.
297 */
298 public void setDateFormatMessage(final String message)
299 {
300 if (StringUtils.isNotEmpty(message))
301 {
302 dateFormatMessage = message;
303 }
304 }
305
306 /***
307 * Get the value of dateFormats.
308 *
309 * @return value of dateFormats.
310 */
311 public List getDateFormats()
312 {
313 return dateFormats;
314 }
315
316 /***
317 * Set the value of dateFormats.
318 *
319 * @param formats Value to assign to dateFormats.
320 */
321 public void setDateFormats(final List formats)
322 {
323 this.dateFormats = formats;
324 }
325
326 /***
327 * Get the value of flexible.
328 *
329 * @return value of flexible.
330 */
331 public boolean isFlexible()
332 {
333 return flexible;
334 }
335
336 /***
337 * Set the value of flexible.
338 *
339 * @param flexible Value to assign to flexible.
340 */
341 public void setFlexible(final boolean flexible)
342 {
343 this.flexible = flexible;
344 }
345 }