package com.anem.green.web.convert;
import java.util.Map;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.joda.time.ReadableInstant;
import org.joda.time.ReadablePartial;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import com.anem.green.web.SessionPreferences;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
/**
*/
public class DateTimeConverter implements Converter {
private ValueExpression timeZoneExpression;
/**
* used to temporarily store the facesContext during the duration of a getAsString or getAsObject
*/
transient FacesContext facesContext;
@Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) {
this.facesContext = facesContext;
Class<?> type = ValueExpressionHelper.getValueType(facesContext, uiComponent,
Lists.<Class<?>> newArrayList(DateTime.class, LocalDate.class, LocalTime.class));
Preconditions.checkArgument(type != null, "DateTimeConverter is not attached to a component bound to either a"
+ " DateTime, LocalDate, or LocalTime.");
Object valueAsObject;
if (type.isAssignableFrom(DateTime.class)) {
valueAsObject = (DateTime) getAsObjectInstance(facesContext, uiComponent, value);
}
else if (type.isAssignableFrom(LocalDate.class)) {
Object dateTime = getAsObjectInstance(facesContext, uiComponent, value);
valueAsObject = dateTime == null ? null : new LocalDate(dateTime);
}
else if (type.isAssignableFrom(LocalTime.class)) {
Object dateTime = getAsObjectInstance(facesContext, uiComponent, value);
valueAsObject = dateTime == null ? null : new LocalTime(dateTime);
}
else {
throw new AssertionError("ValueExpressionHelper.getValueType() broke its contract");
}
this.facesContext = null;
return valueAsObject;
}
@Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) {
this.facesContext = facesContext;
Class<?> type = ValueExpressionHelper.getValueType(facesContext, uiComponent,
Lists.<Class<?>> newArrayList(DateTime.class, LocalDate.class, LocalTime.class));
Preconditions.checkArgument(type != null, "DateTimeConverter is not attached to a component bound to either a"
+ " DateTime, LocalDate, or LocalTime.");
if (value instanceof LocalDate) {
LocalDate localDate = (LocalDate) value;
return getAsStringValue(facesContext, uiComponent, localDate.toDateTimeAtStartOfDay(getTimeZone()));
}
if (value instanceof LocalTime) {
LocalTime localTime = (LocalTime) value;
return getAsStringValue(facesContext, uiComponent, localTime.toDateTimeToday(getTimeZone()));
}
if (value instanceof ReadablePartial) {
ReadablePartial readablePartial = (ReadablePartial) value;
return getAsStringValue(facesContext, uiComponent, readablePartial.toDateTime(new DateTime()));
}
this.facesContext = null;
return getAsStringValue(facesContext, uiComponent, value);
}
private Object getAsObjectInstance(FacesContext facesContext, UIComponent uiComponent, String value) {
if (facesContext == null) {
throw new NullPointerException("facesContext");
}
if (uiComponent == null) {
throw new NullPointerException("uiComponent");
}
if (value != null) {
value = value.trim();
if (value.length() > 0) {
DateTimeFormatter format = getDateFormat(uiComponent);
try {
return format.parseDateTime(value);
}
catch (IllegalArgumentException e) {
throw new ConverterException(e);
}
}
}
return null;
}
private String getAsStringValue(FacesContext facesContext, UIComponent uiComponent, Object value) {
if (facesContext == null) {
throw new NullPointerException("facesContext");
}
if (uiComponent == null) {
throw new NullPointerException("uiComponent");
}
if (value == null) {
return "";
}
if (value instanceof String) {
return (String) value;
}
DateTimeFormatter format = getDateFormat(uiComponent);
try {
return format.print((ReadableInstant) value);
}
catch (Exception e) {
throw new ConverterException("Cannot convert value '" + value + "'");
}
}
private DateTimeFormatter getDateFormat(UIComponent uiComponent) {
DateTimeFormatter format = DateTimeFormat.forPattern(getPattern(uiComponent));
format = format.withLocale(SessionPreferences.getCurrentLocale());
format = format.withZone(getTimeZone());
return format;
}
public DateTimeZone getTimeZone() {
if (timeZoneExpression == null) {
return DateTimeZone.getDefault();
}
else {
ELContext context;// TODO: Need to know how to test this.
if (facesContext == null) {
context = FacesContext.getCurrentInstance().getELContext();
}
else {
context = facesContext.getELContext();
}
return (DateTimeZone) timeZoneExpression.getValue(context);
}
}
public static String getPattern(UIComponent component) {
Map<String, Object> properties = component.getAttributes();
String pattern = (String) properties.get("dateFormat");
if (null == pattern) {
pattern = (String) properties.get("pattern");
}
if (null == pattern) {
pattern = "MM/dd/yyyy";
}
return pattern;
}
}