View Javadoc

1   package org.apache.turbine.services.intake.validator;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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>&nbsp;</td></tr>
40   * <tr><td>formatx</td><td>see SimpleDateFormat javadoc</td>
41   * <td>&nbsp;</td></tr>
42   * <tr><td colspan=3>where x is &gt;= 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; // for
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 &gt;= 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) // This implies dateFormats.size() > 0
196         {
197             // First test the FORMATx patterns. If any of these match, break
198             // the loop.
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; // We got a matching date. Break the loop
207                 }
208                 catch (ParseException e)
209                 {
210                     // ignore
211                 }
212             }
213 
214             // Now test the FORMAT pattern which is the first one in the array.
215             // if no format but just FORMATx has been given, all of the patterns
216             // have been shifted "one down", e.g. tested as format2, format3, format4, format1
217             // in sequence.
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                     // ignore
229                 }
230             }
231         }
232 
233         // Still no match. Either we had no format patterns or no pattern matched.
234         // See if we have a DateFormat object around. If there were patterns given
235         // and just none matched, that we might have date==null and df==null...
236         if (date == null && df != null)
237         {
238             date = df.parse(s);
239         }
240 
241         // if the date still has not been parsed at this point, throw
242         // a ParseException.
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) // implies dateFormats.size() > 0
264             {
265                 sdf.applyPattern((String) dateFormats.get(0));
266                 s = sdf.format(date);
267             }
268             else // implied df != null
269             {
270                 s = df.format(date);
271             }
272         }
273         return s;
274     }
275 
276 
277     // ************************************************************
278     // **                Bean accessor methods                   **
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 }