/*
* Copyright 2007 T-Rank AS
*
* Licensed 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.
*/
package no.trank.openpipe.step;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import no.trank.openpipe.api.MultiInputOutputFieldPipelineStep;
import no.trank.openpipe.api.PipelineException;
import no.trank.openpipe.api.document.AnnotatedField;
import no.trank.openpipe.api.document.Document;
import no.trank.openpipe.config.annotation.NotEmpty;
/**
* This step converts date formats using {@link SimpleDateFormat}.
*
* @version $Revision$
*/
public class ConvertDate extends MultiInputOutputFieldPipelineStep {
private static Logger log = LoggerFactory.getLogger(ConvertDate.class);
@NotEmpty
private LinkedHashMap<String, String> patternMap;
private List<FormatPair> formats;
private boolean failOnError;
private boolean blankError;
public ConvertDate() {
super(false);
}
@Override
public void prepare() throws PipelineException {
super.prepare();
formats = new ArrayList<FormatPair>(patternMap.size());
try {
for (Entry<String, String> e : patternMap.entrySet()) {
formats.add(new FormatPair(e.getKey(), e.getValue()));
}
} catch (RuntimeException e) {
throw new PipelineException(e);
}
}
@Override
protected void process(Document doc, String inputFieldName, List<AnnotatedField> inputFields, String outputFieldName)
throws PipelineException {
if (!inputFields.isEmpty()) {
List<String> output = new ArrayList<String>();
for (AnnotatedField field : inputFields) {
try {
output.add(getOutputValue(inputFieldName, field.getValue()));
} catch (ParseException e) {
if (failOnError) {
throw new PipelineException("Could not parse date " + field.getValue());
}
}
}
doc.setFieldValues(outputFieldName, output);
} else if (blankError) {
throw new PipelineException("Field '" + inputFieldName + "' is empty");
}
}
@Override
public void finish(boolean success) throws PipelineException {
formats = null;
}
private String getOutputValue(String fromFieldName, String fromValue) throws ParseException {
for (FormatPair format : formats) {
final SimpleDateFormat from = format.getFrom();
final SimpleDateFormat to = format.getTo();
String ret = to.format(from.parse(fromValue));
if (log.isDebugEnabled()) {
log.debug("Parsed field '" + fromFieldName + "' with pattern '" + from.toPattern() +
". Output pattern: '" + to.toPattern() + "'");
}
return ret;
}
return null; // will never be reached
}
@Override
public String getRevision() {
return "$Revision$";
}
/**
* Gets the ordered map of from/to date format pairs.
*
* @return the pattern map
*/
public LinkedHashMap<String, String> getPatternMap() {
return patternMap;
}
/**
* Sets the ordered map of from/to date format pairs. When applied to the input, consecutive pairs act as a fallback
* should the previous one generate an error. The step only errors if the last pair errors.
*
* @param patternMap an ordered map containing the from/to format pairs
*/
public void setPatternMap(LinkedHashMap<String, String> patternMap) {
this.patternMap = patternMap;
}
/**
* Gets whether an exception will be thrown if an error occurs.
*
* @return <code>true</code> if an exception will be thrown, <code>false</code> otherwise
*/
public boolean isFailOnError() {
return failOnError;
}
/**
* Sets whether an exception will be thrown if an error occurs.
*
* @param failOnError
*/
public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}
/**
* Gets whether a blank input field will be treated as an error.
*
* @return <code>true</code> if a blank input field will be treated as an error, <code>false</code> otherwise
*/
public boolean isBlankError() {
return blankError;
}
/**
* Sets whether a blank input field will be treated as an error.
*
* @param blankError
*/
public void setBlankError(boolean blankError) {
this.blankError = blankError;
}
private static final class FormatPair {
private final SimpleDateFormat from;
private final SimpleDateFormat to;
public FormatPair(String fromPattern, String toPattern) {
from = new SimpleDateFormat(fromPattern);
to = new SimpleDateFormat(toPattern);
}
public SimpleDateFormat getFrom() {
return from;
}
public SimpleDateFormat getTo() {
return to;
}
}
}