blob: 6b243269c1def94c5f71b097ee5725433213a2fa [file] [log] [blame]
package org.apache.fulcrum.intake.validator;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
/**
* Validates numbers with the following constraints in addition to those
* listed in DefaultValidator.
*
* <table>
* <caption>Validation rules</caption>
* <tr><th>Name</th><th>Valid Values</th><th>Default Value</th></tr>
* <tr><td>format</td><td>see SimpleDateFormat javadoc</td>
* <td>&nbsp;</td></tr>
* <tr><td>formatx</td><td>see SimpleDateFormat javadoc</td>
* <td>&nbsp;</td></tr>
* <tr><td colspan=3>where x is &gt;= 1 to specify multiple date
* formats. Only one format rule should have a message</td></tr>
* <tr><td>flexible</td><td>true, as long as DateFormat can parse the date,
* allow it, and false</td>
* <td>false</td></tr>
* </table>
*
* @author <a href="mailto:jmcnally@collab.net">John McNally</a>
* @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
* @author <a href="mailto:Colin.Chalmers@maxware.nl">Colin Chalmers</a>
* @author <a href="mailto:jh@byteaction.de">J&uuml;rgen Hoffmann</a>
* @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
* @version $Id$
*/
public class DateStringValidator
extends DefaultValidator<Date>
{
private static final String DEFAULT_DATE_MESSAGE =
"Date could not be parsed";
/** A list of date formats to try */
private List<String> dateFormats = null;
/** An error message if no date could be parsed */
private String dateFormatMessage = null;
/** A flag that is passed to the DateFormat lenient feature */
private boolean flexible = false;
/**
* Default Constructor
*/
public DateStringValidator()
{
super();
dateFormats = new ArrayList<String>(5);
}
/**
* Constructor to use when initializing Object
*
* @param paramMap a map of parameters
* @throws InvalidMaskException one of the mask rules is invalid
*/
@Override
public void init(Map<String, ? extends Constraint> paramMap)
throws InvalidMaskException
{
super.init(paramMap);
Constraint constraint = paramMap.get(FORMAT_RULE_NAME);
if (constraint != null)
{
dateFormats.add(constraint.getValue());
setDateFormatMessage(constraint.getMessage());
}
for(int i = 1 ;; i++)
{
constraint = paramMap.get(FORMAT_RULE_NAME + i);
if (constraint == null)
{
break; // for
}
dateFormats.add(constraint.getValue());
setDateFormatMessage(constraint.getMessage());
}
if (StringUtils.isEmpty(dateFormatMessage))
{
dateFormatMessage = DEFAULT_DATE_MESSAGE;
}
constraint = paramMap.get(FLEXIBLE_RULE_NAME);
if (constraint != null)
{
flexible = Boolean.valueOf(constraint.getValue()).booleanValue();
}
}
/**
* Determine whether a testValue meets the criteria specified
* in the constraints defined for this validator
*
* @param testValue a <code>String</code> to be tested
* @throws ValidationException containing an error message if the
* testValue did not pass the validation tests.
*/
@Override
public void assertValidity(String testValue)
throws ValidationException
{
super.assertValidity(testValue);
if (required || StringUtils.isNotEmpty(testValue))
{
try
{
parse(testValue);
}
catch (ParseException e)
{
errorMessage = dateFormatMessage;
throw new ValidationException(dateFormatMessage);
}
}
}
/**
* Parses the String s according to the rules/formats for this validator.
* The formats provided by the "formatx" rules (where x is &gt;= 1) are
* used <strong>before</strong> the "format" rules to allow for a display
* format that includes a 4 digit year, but that will parse the date using
* a format that accepts 2 digit years.
*
* @param s possibly a date string
* @return the date parsed
* @throws ParseException indicates that the string could not be
* parsed into a date.
*/
public Date parse(String s)
throws ParseException
{
Date date = null;
if (s == null)
{
throw new ParseException("Input string was null", -1);
}
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setLenient(flexible);
for (int i = 1; i < dateFormats.size() && date == null; i++)
{
sdf.applyPattern(dateFormats.get(i));
try
{
date = sdf.parse(s);
}
catch (ParseException e)
{
// ignore
}
}
if (date == null)
{
sdf.applyPattern(dateFormats.get(0));
try
{
date = sdf.parse(s);
}
catch (ParseException e)
{
// ignore
}
}
if (date == null)
{
// Try default
date = SimpleDateFormat.getInstance().parse(s);
}
// if the date still has not been parsed at this point, throw
// a ParseException.
if (date == null)
{
throw new ParseException("Could not parse the date", 0);
}
return date;
}
/**
* Formats a date into a String. The format used is from
* the first format rule found for the field.
*
* @param date the Date object to convert into a string.
* @return formatted date
*/
public String format(Date date)
{
String s = null;
if (date != null && !dateFormats.isEmpty())
{
SimpleDateFormat sdf = new SimpleDateFormat(dateFormats.get(0));
s = sdf.format(date);
}
return s;
}
// ************************************************************
// ** Bean accessor methods **
// ************************************************************
/**
* Get the value of minLengthMessage.
*
* @return value of minLengthMessage.
*/
public String getDateFormatMessage()
{
return dateFormatMessage;
}
/**
* Only sets the message if the new message has some information.
* So the last setMessage call with valid data wins. But later calls
* with null or empty string will not affect a previous valid setting.
*
* @param message Value to assign to minLengthMessage.
*/
public void setDateFormatMessage(String message)
{
if (StringUtils.isNotEmpty(message))
{
dateFormatMessage = message;
}
}
/**
* Get the value of dateFormats.
*
* @return value of dateFormats.
*/
public List<String> getDateFormats()
{
return dateFormats;
}
/**
* Set the value of dateFormats.
*
* @param formats Value to assign to dateFormats.
*/
public void setDateFormats(List<String> formats)
{
this.dateFormats = formats;
}
/**
* Get the value of flexible.
*
* @return value of flexible.
*/
public boolean isFlexible()
{
return flexible;
}
/**
* Set the value of flexible.
*
* @param flexible Value to assign to flexible.
*/
public void setFlexible(boolean flexible)
{
this.flexible = flexible;
}
}