/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.james.util;

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

/**
 * A thread-safe date formatting class to produce dates formatted in accord with the 
 * specifications of RFC 977.
 *
 * @author Peter M. Goldstein <farsight@alum.mit.edu>
 */
public class RFC977DateFormat implements SimplifiedDateFormat {
    private final SynchronizedDateFormat internalLongDateFormat;
    private final SynchronizedDateFormat internalShortDateFormat;

    public RFC977DateFormat() {
        internalLongDateFormat = new SynchronizedDateFormat("yyyyMMdd HHmmss", Locale.ENGLISH);
        internalShortDateFormat = new SynchronizedDateFormat("yyMMdd HHmmss", Locale.ENGLISH);
    }

    /**
     * This method returns the long form of the RFC977 Date 
     *
     * @return java.lang.String
     * @param d Date
     */
    public String format(Date d) {
        return internalLongDateFormat.format(d);
    }

    /**
     * Parses text from the beginning of the given string to produce a date.
     * The method may not use the entire text of the given string.
     * <p>
     * This method is designed to be thread safe, so we wrap our delegated
     * parse method in an appropriate synchronized block.
     *
     * @param source A <code>String</code> whose beginning should be parsed.
     * @return A <code>Date</code> parsed from the string.
     * @exception ParseException if the beginning of the specified string
     *            cannot be parsed.
     */
    public Date parse(String source) throws ParseException {
        source = source.trim();
        if (source.indexOf(' ') == 6) {
            return internalShortDateFormat.parse(source);
        } else {
            return internalLongDateFormat.parse(source);
        }
    }

    /**
     * Sets the time zone of this SynchronizedDateFormat object.
     * @param zone the given new time zone.
     */
    public void setTimeZone(TimeZone zone) {
        synchronized(this) {
            internalShortDateFormat.setTimeZone(zone);
            internalLongDateFormat.setTimeZone(zone);
        }
    }

    /**
     * Gets the time zone.
     * @return the time zone associated with this SynchronizedDateFormat.
     */
    public TimeZone getTimeZone() {
        synchronized(this) {
            return internalShortDateFormat.getTimeZone();
        }
    }

    /**
     * Specify whether or not date/time parsing is to be lenient.  With
     * lenient parsing, the parser may use heuristics to interpret inputs that
     * do not precisely match this object's format.  With strict parsing,
     * inputs must match this object's format.
     * @param lenient when true, parsing is lenient
     * @see java.util.Calendar#setLenient
     */
    public void setLenient(boolean lenient)
    {
        synchronized(this) {
            internalShortDateFormat.setLenient(lenient);
            internalLongDateFormat.setLenient(lenient);
        }
    }

    /**
     * Tell whether date/time parsing is to be lenient.
     * @return whether this SynchronizedDateFormat is lenient.
     */
    public boolean isLenient()
    {
        synchronized(this) {
            return internalShortDateFormat.isLenient();
        }
    }


    /**
     * Overrides equals
     */
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof RFC977DateFormat)) {
            return false;
        }
        RFC977DateFormat theOtherRFC977DateFormat = (RFC977DateFormat)obj;
        synchronized (this) {
            return ((internalShortDateFormat.equals(theOtherRFC977DateFormat.internalShortDateFormat)) &&
                    (internalLongDateFormat.equals(theOtherRFC977DateFormat.internalLongDateFormat)));
        }
    }

    /**
     * Overrides hashCode
     */
    public int hashCode() {
        return (int)(internalLongDateFormat.hashCode() & internalShortDateFormat.hashCode());
    }

}
