001    /****************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one   *
003     * or more contributor license agreements.  See the NOTICE file *
004     * distributed with this work for additional information        *
005     * regarding copyright ownership.  The ASF licenses this file   *
006     * to you under the Apache License, Version 2.0 (the            *
007     * "License"); you may not use this file except in compliance   *
008     * with the License.  You may obtain a copy of the License at   *
009     *                                                              *
010     *   http://www.apache.org/licenses/LICENSE-2.0                 *
011     *                                                              *
012     * Unless required by applicable law or agreed to in writing,   *
013     * software distributed under the License is distributed on an  *
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015     * KIND, either express or implied.  See the License for the    *
016     * specific language governing permissions and limitations      *
017     * under the License.                                           *
018     ****************************************************************/
019    
020    
021    package org.apache.mailet.base;
022    
023    import java.text.DateFormat;
024    import java.text.ParseException;
025    import java.text.SimpleDateFormat;
026    import java.util.Date;
027    import java.util.Locale;
028    import java.util.TimeZone;
029    
030    
031    /**
032     * This class is designed to be a synchronized wrapper for a
033     * <code>java.text.DateFormat</code> subclass.  In general,
034     * these subclasses (most notably the <code>java.text.SimpleDateFormat</code>
035     * classes are not thread safe, so we need to synchronize on the
036     * internal DateFormat for all delegated calls.
037     *
038     */
039    public class SynchronizedDateFormat implements SimplifiedDateFormat {
040        private final DateFormat internalDateFormat;
041    
042        /**
043         * Public constructor that mimics that of SimpleDateFormat.  See
044         * java.text.SimpleDateFormat for more details.
045         *
046         * @param pattern the pattern that defines this DateFormat
047         * @param locale the locale
048         */
049        public SynchronizedDateFormat(String pattern, Locale locale) {
050            internalDateFormat = new SimpleDateFormat(pattern, locale);
051        }
052    
053        /**
054         * <p>Wrapper method to allow child classes to synchronize a preexisting
055         * DateFormat.</p>
056         *
057         * <p>TODO: Investigate replacing this with a factory method.</p>
058         *
059         * @param theDateFormat the DateFormat to synchronize
060         */
061        protected SynchronizedDateFormat(DateFormat theDateFormat) {
062            internalDateFormat = theDateFormat;
063        }
064    
065        /**
066         * SimpleDateFormat will handle most of this for us, but we
067         * want to ensure thread safety, so we wrap the call in a
068         * synchronized block.
069         *
070         * @return java.lang.String
071         * @param d Date
072         */
073        public String format(Date d) {
074            synchronized (internalDateFormat) {
075               return internalDateFormat.format(d);
076            }
077        }
078    
079        /**
080         * Parses text from the beginning of the given string to produce a date.
081         * The method may not use the entire text of the given string.
082         * <p>
083         * This method is designed to be thread safe, so we wrap our delegated
084         * parse method in an appropriate synchronized block.
085         *
086         * @param source A <code>String</code> whose beginning should be parsed.
087         * @return A <code>Date</code> parsed from the string.
088         * @throws ParseException if the beginning of the specified string
089         *         cannot be parsed.
090         */
091        public Date parse(String source) throws ParseException {
092            synchronized (internalDateFormat) {
093                return internalDateFormat.parse(source);
094            }
095        }
096    
097        /**
098         * Sets the time zone of this SynchronizedDateFormat object.
099         * @param zone the given new time zone.
100         */
101        public void setTimeZone(TimeZone zone) {
102            synchronized(internalDateFormat) {
103                internalDateFormat.setTimeZone(zone);
104            }
105        }
106    
107        /**
108         * Gets the time zone.
109         * @return the time zone associated with this SynchronizedDateFormat.
110         */
111        public TimeZone getTimeZone() {
112            synchronized(internalDateFormat) {
113                return internalDateFormat.getTimeZone();
114            }
115        }
116    
117        /**
118         * Specify whether or not date/time parsing is to be lenient.  With
119         * lenient parsing, the parser may use heuristics to interpret inputs that
120         * do not precisely match this object's format.  With strict parsing,
121         * inputs must match this object's format.
122         * @param lenient when true, parsing is lenient
123         * @see java.util.Calendar#setLenient
124         */
125        public void setLenient(boolean lenient)
126        {
127            synchronized(internalDateFormat) {
128                internalDateFormat.setLenient(lenient);
129            }
130        }
131    
132        /**
133         * Tell whether date/time parsing is to be lenient.
134         * @return whether this SynchronizedDateFormat is lenient.
135         */
136        public boolean isLenient()
137        {
138            synchronized(internalDateFormat) {
139                return internalDateFormat.isLenient();
140            }
141        }
142    
143        /**
144         * Overrides hashCode
145         */
146        public int hashCode() {
147            synchronized(internalDateFormat) {
148                return internalDateFormat.hashCode();
149            }
150        }
151    
152        /**
153         * Overrides equals
154         */
155        public boolean equals(Object obj) {
156            if (this == obj) {
157                return true;
158            }
159            if (obj == null || getClass() != obj.getClass()) {
160                return false;
161            }
162            synchronized(internalDateFormat) {
163                return internalDateFormat.equals(obj);
164            }
165        }
166    
167    }