/******************************************************************************* * * Copyright 2010 Alexandru Craciun, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ******************************************************************************/ package org.netxilia.api.impl.value; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.regex.Pattern; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.netxilia.api.utils.DateUtils; import org.netxilia.api.value.DateValue; import org.netxilia.api.value.IGenericValue; import org.netxilia.api.value.IGenericValueParser; import org.springframework.beans.factory.InitializingBean; /** * inspired by Stripes Date converter * * @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a> * */ public class DateTimeParser implements IGenericValueParser, InitializingBean { private Locale locale = Locale.getDefault(); private String datePatterns[]; private String timePatterns[]; private List<DateTimeFormatter> dateTimeFormatters; /** * <p> * A pattern used to pre-process Strings before the parsing attempt is made. Since SimpleDateFormat strictly * enforces that the separator characters in the input are the same as those in the pattern, this regular expression * is used to remove commas, slashes, hyphens and periods from the input String (replacing them with spaces) and to * collapse multiple white-space characters into a single space. * </p> * * <p> * This pattern can be changed by providing a different value under the * <code>'stripes.dateTypeConverter.preProcessPattern'</code> key in the resource bundle. The default value is * <code>(?<!GMT)[\\s,-/\\.]+</code>. * </p> */ private static final Pattern PRE_PROCESS_PATTERN = Pattern.compile("(?<!GMT)[\\s/\\.-]+"); @Override public IGenericValue parse(String text) { String input = preProcessInput(text); for (DateTimeFormatter formatter : dateTimeFormatters) { try { return new DateValue(DateUtils.parsePartial(formatter, input)); } catch (IllegalArgumentException e) { // try next pattern } } return null; } /** * Pre-processes the input String to improve the chances of parsing it. First uses the regular expression Pattern * returned by getPreProcessPattern() to remove all separator chars and ensure that components are separated by * single spaces. Then invokes {@link #checkAndAppendYear(String)} to append the year to the date in case the date * is in a format like "12/25" which would otherwise fail to parse. */ protected String preProcessInput(String input) { input = PRE_PROCESS_PATTERN.matcher(input.trim()).replaceAll(" "); input = checkAndAppendYear(input); return input; } /** * Checks to see how many 'parts' there are to the date (separated by spaces) and if there are only two parts it * adds the current year to the end by getting the Locale specific year string from a Calendar instance. * * @param input * the date string after the pre-process pattern has been run against it * @return either the date string as is, or with the year appended to the end */ protected String checkAndAppendYear(String input) { // Count the spaces, date components = spaces + 1 int count = 0; for (char ch : input.toCharArray()) { if (ch == ' ') { ++count; } } // Looks like we probably only have a day and month component, that won't work! if (count == 1) { input += " " + Calendar.getInstance(locale).get(Calendar.YEAR); } return input; } public String[] getDatePatterns() { return datePatterns; } public void setDatePatterns(String[] datePatterns) { this.datePatterns = datePatterns; } public String[] getTimePatterns() { return timePatterns; } public void setTimePatterns(String[] timePatterns) { this.timePatterns = timePatterns; } @Override public void afterPropertiesSet() throws Exception { // add date, time, and date + time dateTimeFormatters = new ArrayList<DateTimeFormatter>(); for (String datePattern : datePatterns) { dateTimeFormatters.add(DateTimeFormat.forPattern(datePattern)); } for (String timePattern : timePatterns) { dateTimeFormatters.add(DateTimeFormat.forPattern(timePattern)); } for (String datePattern : datePatterns) { for (String timePattern : timePatterns) { dateTimeFormatters.add(DateTimeFormat.forPattern(datePattern + " " + timePattern)); } } } }