/*
* Copyright 2014 Deutsche Nationalbibliothek
*
* 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 org.culturegraph.mf.metamorph.functions;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.culturegraph.mf.metamorph.api.MorphBuildException;
import org.culturegraph.mf.metamorph.api.helpers.AbstractSimpleStatelessFunction;
/**
* Format date/time strings in Metamorph. By default the input format is
* dd.MM.yyyy and the output format is {@link java.text.DateFormat.Field#LONG}.
* <br/>
* The Attribute removeLeadingZeros will remove all leading zeros from all
* numbers in the output date.
* <br/>
* The attribute era is used to specify if the date is BC or AD. Default
* value is AUTO. To understand that, three short examples:
* <ul><li>Input: 20.07.356 (era=BC)</li>
* <li>Output (German location): 20. Juli 0356 v. Chr.</li>
* <ul><li>Input: 20.07.356 (era=AD,removeLeadingZeros=true)</li>
* <li>Output (German location): 20. Juli 356</li>
* <li>Input: 20.07.-356 (era=AUTO)</li>
* <li>Output (German location): 20. Juli 0357 v. Chr. (there is NO year 0;
* see ISO 8601, Proleptic Gregorian Calendar)</li></ul>
* <p/>
* Examples of using this function in Metamorph:
* <ul><li>Default date format: <code><dateformat /></code></li>
* <li>Read ISO-dates and generate German style dates:
* <code><dateformat inputformat="yyyy-MM-dd" outputformat="dd.MM.yyyy" /></code></li>
* </ul>
*
* @author Michael Büchner
*/
public class DateFormat extends AbstractSimpleStatelessFunction {
public static final String DEFAULT_INPUT_FORMAT = "dd.MM.yyyy";
public static final DateFormats DEFAULT_OUTPUT_FORMAT = DateFormats.LONG;
public static final boolean DEFAULT_REMOVE_LEADING_ZEROS = false;
public static final Era DEFAULT_ERA = Era.AUTO;
private static final Set<String> SUPPORTED_LANGUAGES;
private String inputFormat = DEFAULT_INPUT_FORMAT;
private DateFormats outputFormat = DEFAULT_OUTPUT_FORMAT;
private Era era = DEFAULT_ERA;
private boolean removeLeadingZeros = DEFAULT_REMOVE_LEADING_ZEROS;
private Locale outputLocale = Locale.getDefault();
/**
* Supported date formats. Maps to the date formats in
* {@link java.text.DateFormat}.
*
* @author Christoph Böhme
*/
public enum DateFormats {
FULL(java.text.DateFormat.FULL),
LONG(java.text.DateFormat.LONG),
MEDIUM(java.text.DateFormat.MEDIUM),
SHORT(java.text.DateFormat.SHORT);
private final int formatId;
DateFormats(final int formatId) {
this.formatId = formatId;
}
int getFormatId() {
return formatId;
}
}
/**
* Supported eras (basically AUTO, AD and BC). Maps to
* {@link java.util.GregorianCalendar}.
*
* @author Michael Büchner
*/
public enum Era {
AD(GregorianCalendar.AD),
BC(GregorianCalendar.BC),
AUTO(-1);
private final int eraId;
Era(final int eraId) {
this.eraId = eraId;
}
int getEraId() {
return eraId;
}
}
static {
final Set<String> set = new HashSet<String>();
Collections.addAll(set, Locale.getISOLanguages());
SUPPORTED_LANGUAGES = Collections.unmodifiableSet(set);
}
@Override
public final String process(final String value) {
String result;
try {
final Calendar c = Calendar.getInstance();
final SimpleDateFormat sdf = new SimpleDateFormat(inputFormat);
c.setTime(sdf.parse(value));
if (era == Era.BC) {
c.set(Calendar.ERA, GregorianCalendar.BC);
} else if (era == Era.AD) {
c.set(Calendar.ERA, GregorianCalendar.AD);
}
final SimpleDateFormat sdfp = (SimpleDateFormat) java.text.DateFormat.getDateInstance(outputFormat.getFormatId(), outputLocale);
String p = sdfp.toPattern();
if (c.get(Calendar.ERA) == GregorianCalendar.BC) {
p = p.replace("yyyy", "yyyy G");
}
final SimpleDateFormat sdfo = new SimpleDateFormat(p, outputLocale);
result = sdfo.format(c.getTime());
if (removeLeadingZeros) {
result = result.replaceAll("([0]{1,})([0-9]{1,})", "$2");
}
} catch (final IllegalArgumentException e) {
throw new MorphBuildException("The date/time format is not supported.", e);
} catch (final Exception e) {
result = value;
}
return result;
}
public final void setInputFormat(final String inputFormat) {
this.inputFormat = inputFormat;
}
public final void setOutputFormat(final DateFormats outputFormat) {
this.outputFormat = outputFormat;
}
public final void setEra(final Era era) {
this.era = era;
}
public final void setRemoveLeadingZeros(final boolean removeLeadingZeros) {
this.removeLeadingZeros = removeLeadingZeros;
}
public final void setLanguage(final String language) {
if (!SUPPORTED_LANGUAGES.contains(language)) {
throw new MorphBuildException("Language '" + language + "' not supported.");
}
this.outputLocale = new Locale(language);
}
}