package humanize.time.joda;
import humanize.spi.FormatProvider;
import humanize.text.FormatFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.PeriodFormatter;
import com.google.common.base.Preconditions;
/**
* {@link FormatProvider} for Joda time.
*
*/
public class JodaTimeFormatProvider implements FormatProvider
{
/**
* Creates a factory for the specified format.
*
* @return FormatFactory instance
*/
public static FormatFactory factory()
{
return new FormatFactory()
{
@Override
public Format getFormat(String name, String args, Locale locale)
{
Map<String, Format> mt = FormatTables.get(name);
Preconditions.checkArgument(mt != null, "There's no format instance for [%s]", name);
Format m = mt.get((args == null || args.length() < 1) ? FormatNames.DEFAULT : args);
Preconditions.checkArgument(m != null, "There's no signature in [%s] for the given args [%s]", name,
args);
return ((ConfigurableFormat) m).withLocale(locale);
}
};
}
@Override
public FormatFactory getFactory()
{
return factory();
}
@Override
public String getFormatName()
{
return String.format("%s|%s|%s", FormatNames.FORMAT_JODA_TIME,
FormatNames.FORMAT_JODA_ISO_TIME, FormatNames.FORMAT_JODA_ISO_PERIOD);
}
public interface ConfigurableFormat
{
Format withLocale(Locale locale);
}
/**
* Base class for Joda formats.
*
* @param <T>
* Formatter type
*/
public abstract static class JodaBaseFormat<T> extends Format implements ConfigurableFormat
{
private static final long serialVersionUID = 9053371900269483473L;
protected final Method method;
protected T format;
public JodaBaseFormat(Method method)
{
this.method = method;
}
public synchronized T get()
{
if (format == null)
{
try
{
format = invoke();
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
return format;
}
@Override
public Object parseObject(String source, ParsePosition pos)
{
try
{
int begin = pos.getIndex();
pos.setIndex(source.length());
String text = source.substring(begin);
return (text == null || text.length() < 1) ? text : parse(text);
} catch (ParseException e)
{
pos.setIndex(0);
pos.setErrorIndex(e.getErrorOffset());
}
return null;
}
@SuppressWarnings("unchecked")
protected T invoke() throws IllegalAccessException, InvocationTargetException
{
return (T) method.invoke(null);
}
abstract protected Object parse(String text) throws ParseException;
}
/**
* {@link Format} for Joda {@link DateTime}.
*
*/
public static class JodaDateTimeFormat extends JodaBaseFormat<DateTimeFormatter>
{
private static final long serialVersionUID = -7080564879531103796L;
public JodaDateTimeFormat(Method method)
{
super(method);
}
@Override
public StringBuffer format(Object param, StringBuffer appendTo, FieldPosition pos)
{
return appendTo.append(((DateTime) param).toString(get()));
}
public Object parse(String source) throws ParseException
{
return format.parseDateTime(source);
}
public Format withLocale(Locale locale)
{
format = get().withLocale(locale);
return this;
}
}
/**
* {@link Format} for Joda {@link Period}.
*
*/
public static class JodaPeriodFormat extends JodaBaseFormat<PeriodFormatter>
{
private static final long serialVersionUID = 7075580918316147610L;
private Locale locale;
public JodaPeriodFormat(Method method)
{
super(method);
}
@Override
public StringBuffer format(Object param, StringBuffer appendTo, FieldPosition pos)
{
return appendTo.append(((Period) param).toString(get()));
}
public Object parse(String source) throws ParseException
{
return format.parsePeriod(source);
}
public synchronized Format withLocale(Locale locale)
{
this.locale = locale;
this.format = get();
return this;
}
@Override
protected PeriodFormatter invoke() throws IllegalAccessException, InvocationTargetException
{
return (PeriodFormatter) method.invoke(null, locale);
}
}
}