/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.inject.Inject;
import org.dspace.kernel.DSpaceKernel;
import org.dspace.servicemanager.DSpaceKernelInit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Attempt to parse date strings in a variety of formats. This uses an external
* list of regular expressions and associated SimpleDateFormat strings. Inject
* the list as pairs of strings using {@link #setPatterns}. {@link #parse} walks
* the provided list in the order provided and tries each entry against a String.
*
* Dates are parsed as being in the UTC zone.
*
* @author mwood
*/
public class MultiFormatDateParser
{
private static final Logger log = LoggerFactory.getLogger(MultiFormatDateParser.class);
/** A list of rules, each binding a regular expression to a date format. */
private static final ArrayList<Rule> rules = new ArrayList<>();
private static final TimeZone UTC_ZONE = TimeZone.getTimeZone("UTC");
/** Format for displaying a result of testing. */
private static final ThreadLocal<DateFormat> formatter;
static
{
formatter = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue() {
DateFormat dateTimeInstance = SimpleDateFormat.getDateTimeInstance();
dateTimeInstance.setTimeZone(UTC_ZONE);
return dateTimeInstance;
}
};
}
@Inject
public void setPatterns(Map<String, String> patterns)
{
for (Entry<String, String> rule : patterns.entrySet())
{
Pattern pattern;
try {
pattern = Pattern.compile(rule.getKey(),Pattern.CASE_INSENSITIVE);
} catch (PatternSyntaxException ex) {
log.error("Skipping format with unparseable pattern '{}'",
rule.getKey());
continue;
}
SimpleDateFormat format;
try {
format = new SimpleDateFormat(rule.getValue());
} catch (IllegalArgumentException ex) {
log.error("Skipping uninterpretable date format '{}'",
rule.getValue());
continue;
}
format.setCalendar(Calendar.getInstance(UTC_ZONE));
format.setLenient(false);
rules.add(new Rule(pattern, format));
}
}
/**
* Compare a string to each injected regular expression in entry order, and
* when it matches, attempt to parse it using the associated format.
*
* @param dateString the supposed date to be parsed.
* @return the result of the first successful parse, or {@code null} if none.
*/
static public Date parse(String dateString)
{
for (Rule candidate : rules)
{
if (candidate.pattern.matcher(dateString).matches())
{
Date result;
try {
synchronized(candidate.format) {
result = candidate.format.parse(dateString);
}
} catch (ParseException ex) {
log.info("Date string '{}' matched pattern '{}' but did not parse: {}",
new String[] {dateString, candidate.format.toPattern(), ex.getMessage()});
continue;
}
return result;
}
}
return null;
}
public static void main(String[] args)
throws IOException
{
DSpaceKernelInit.getKernel(null); // Mainly to initialize Spring
// TODO direct log to stdout/stderr somehow
if (args.length > 0) // Test data supplied on the command line
{
for (String arg : args)
{
testDate(arg);
}
}
else // Get test data from the environment
{
String arg;
if (null == System.console()) // Possibly piped input
{
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String line;
while (null != (line = input.readLine()))
testDate(line.trim());
}
else // Loop, prompting for input
while(null != (arg = System.console().readLine("Enter a date-time: ")))
{
testDate(arg);
}
}
}
/**
* Try to parse a date, and report the outcome.
*
* @param arg date-time string to be tested.
*/
private static void testDate(String arg)
{
Date result = parse(arg);
if (null == result)
System.out.println("Did not match any pattern.");
else
{
System.out.println(formatter.get().format(result));
}
}
/**
* Holder for a pair: compiled regex, compiled SimpleDateFormat.
*/
private static class Rule
{
final Pattern pattern;
final SimpleDateFormat format;
public Rule(Pattern pattern, SimpleDateFormat format)
{
this.pattern = pattern;
this.format = format;
}
}
}