View Javadoc

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