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 }