package org.pm4j.core.pm.impl.converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pm4j.core.exception.PmResourceRuntimeException;
import org.pm4j.core.exception.PmRuntimeException;
import org.pm4j.core.pm.PmAttr;
import org.pm4j.core.pm.PmConstants;
import org.pm4j.core.pm.impl.PmUtil;

public class PmConverterDate extends PmConverterSerializeableBase<Date> {

  /**
   * The format pattern that is used when no format pattern resource was found
   * for the current PM locale.
   */
  public static final String FALLBACK_FORMAT_PATTERN = "dd.MM.yyyy";

  public static final PmConverterDate INSTANCE = new PmConverterDate();

  private static final Log LOG = LogFactory.getLog(PmConverterDate.class);

  /** The separator string used in case of a multi-format resource string specification. */
  protected String formatSplitString = ";";

  @Override
  public Date stringToValue(PmAttr<?> pmAttr, String s) {
    if (StringUtils.isBlank(s)) {
      return null;
    }

    Locale locale = pmAttr.getPmConversation().getPmLocale();
    TimeZone timeZone = pmAttr.getPmConversation().getPmTimeZone();
    for (String format : getParseFormats(pmAttr)) {
      try {
        SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
        sdf.setTimeZone(timeZone);

        // we will not allow dates of the form 00.00.08 etc, hence lenient is set to false
        sdf.setLenient(false);

        return sdf.parse(s);
      } catch (ParseException e) {
        // ignore it and try the next format.
        if (LOG.isDebugEnabled()) {
          LOG.debug("Format '" + format + "' not applicable for value '" + s +
                    "'. Attribute context: " + PmUtil.getPmLogString(pmAttr));
        }
      }
    }

    // no format match
    throw new PmResourceRuntimeException(pmAttr, PmConstants.MSGKEY_VALIDATION_FORMAT_FAILURE,
                           pmAttr.getPmShortTitle(), getOutputFormat(pmAttr), s);
  }

  @Override
  public String valueToString(PmAttr<?> pmAttr, Date value) {
    String outputFormat = getOutputFormat(pmAttr);
    try {
      TimeZone timeZone = pmAttr.getPmConversation().getPmTimeZone();
      return FastDateFormat.getInstance(outputFormat, timeZone, pmAttr.getPmConversation().getPmLocale()).format(value);
    }
    catch (RuntimeException e) {
      throw new PmRuntimeException(pmAttr, "Unable to apply format '" +
                                         outputFormat + "' to value '" + value + "'.");
    }
  }

  /**
   * Translates the (optionally semicolon delimited) language specific format
   * value in an array set of formats that is used to parse date strings.
   * <p>
   * The last item will be used as string output format.
   * <p>
   * When the language specific format is not defined or empty, the
   * {@link PmConverterDate#FALLBACK_FORMAT_PATTERN} will be returned.
   *
   * @return The accepted input formats.
   */
  public String[] getParseFormats(PmAttr<?> pmAttr) {
    String formatString = StringUtils.defaultIfEmpty(pmAttr.getFormatString(), FALLBACK_FORMAT_PATTERN);
    String[] formats = formatString.split(formatSplitString);
    return formats;
  }

  /**
   * The last format definition returned by {@link #getParseFormats(PmAttr)}.
   * <p>
   * Is used for the method {@link PmAttr#getValueAsString()}.
   * <p>
   * It is also intended to be used by UI help constructs such as calendar
   * popups which provide their data as strings.
   *
   * @return The last item of the result of {@link #getParseFormats(PmAttr)}
   */
  public String getOutputFormat(PmAttr<?> pmAttr) {
    String[] formats = getParseFormats(pmAttr);
    return formats[formats.length-1];
  }

}
@@ -551,24 +551,65 @@
 Constants;%0D%0A
+import org.pm4j.core.pm.PmConversation;%0D%0A
 import org.p
@@ -634,16 +634,16 @@
 mUtil;%0D%0A
-
 %0D%0Apublic
@@ -1201,24 +1201,61 @@
 g = %22;%22;%0D%0A%0D%0A
+  boolean timeZoneAware = true;%0D%0A  %0D%0A
   @Override%0D
@@ -1469,40 +1469,11 @@
 e = 
-pmAttr.getPmConversation().getPm
+get
 Time
@@ -1469,32 +1469,38 @@
 e = getTimeZone(
+pmAttr
 );%0D%0A    for (Str
@@ -2505,40 +2505,11 @@
 e = 
-pmAttr.getPmConversation().getPm
+get
 Time
@@ -2513,16 +2513,22 @@
 imeZone(
+pmAttr
 );%0D%0A    
@@ -2854,32 +2854,193 @@
 %0D%0A    %7D%0D%0A  %7D%0D%0A%0D%0A
+  private TimeZone getTimeZone(PmAttr%3C?%3E pmAttr) %7B%0D%0A    return timeZoneAware ? pmAttr.getPmConversation().getPmTimeZone() : TimeZone.getTimeZone(%22UTC%22);%0D%0A  %7D%0D%0A%0D%0A
   /**%0D%0A   * Tran
@@ -4238,16 +4238,385 @@
 th-1%5D;%0D%0A
-
   %7D%0D%0A%0D%0A
+  /**%0D%0A   * @return true, if time zone conversion base on %7B@link PmConversation#getPmTimeZone()%7D is activated%0D%0A   */%0D%0A  public boolean isTimeZoneAware() %7B%0D%0A    return timeZoneAware;%0D%0A  %7D%0D%0A%0D%0A  /**%0D%0A   * @param timeZoneAware the timeZoneAware to set%0D%0A   */%0D%0A  public void setTimeZoneAware(boolean timeZoneAware) %7B%0D%0A    this.timeZoneAware = timeZoneAware;%0D%0A  %7D%0D%0A  %0D%0A  %0D%0A%0D%0A
 %7D

Add flag for time zone conversion