/* ===================================================================
* JodaDateFormatEditor.java
*
* Created Aug 3, 2009 3:51:41 PM
*
* Copyright (c) 2009 Solarnetwork.net Dev Team.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ===================================================================
* $Id$
* ===================================================================
*/
package net.solarnetwork.util;
import java.beans.PropertyEditorSupport;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.ReadableInstant;
import org.joda.time.ReadablePartial;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* PropertyEditor using Joda Time's DateTimeFormatter for thread-safe date
* parsing and formatting.
*
* <p>This class has been designed with {@link CloningPropertyEditorRegistrar}
* in mind, so that one instance of a {@link DateTimeFormatter} can be shared
* between multiple threads to parse or format Joda date objects.</p>
*
* <p>The configurable properties of this class are:</p>
*
* <dl class="class-properties">
* <dt>datePattern</dt>
* <dd>The date pattern format string to use for parsing / formatting dates.
* See the {@link org.joda.time.format.DateTimeFormat} JavaDocs for
* information on how the patterns work. Defaults to {@code yyyy-MM-dd}.</dd>
*
* <dt>timeZone</dt>
* <dd>The TimeZone to use for parsing/formatting all dates. Defaults to
* the platform's default time zone.</dd>
*
* <dt>parseMode</dt>
* <dd>A mode flag for handling different Joda date instance types.</dd>
*
* </dl>
*
* @author matt
* @version $Revision$ $Date$
*/
public class JodaDateFormatEditor extends PropertyEditorSupport
implements Cloneable {
/**
* A mode flag to determine the type of dates to parse.
*/
public enum ParseMode {
/** Parse text into DateTime objects. */
DateTime,
/** Parse text into LocalDate objects. */
LocalDate,
/** Parse text into LocalTime objects. */
LocalTime,
}
private TimeZone timeZone = TimeZone.getDefault();
private String[] datePatterns = new String[] {"yyyy-MM-dd"};
private ParseMode parseMode = ParseMode.DateTime;
private DateTimeFormatter[] dateFormatters = null;
/**
* Default constructor.
*/
public JodaDateFormatEditor() {
super();
}
/**
* Construct from a single date pattern value in the default time zone.
*
* <p>This will also call the {@link #init()} method.</p>
*
* @param datePattern the date pattern
*/
public JodaDateFormatEditor(String datePattern) {
this(datePattern, ParseMode.DateTime);
}
/**
* Construct from a single date pattern value in the default time zone.
*
* <p>This will also call the {@link #init()} method.</p>
*
* @param datePattern the date pattern
* @param parseMode the specific parse mode to use
*/
public JodaDateFormatEditor(String datePattern, ParseMode parseMode) {
this(datePattern, parseMode == ParseMode.DateTime ? TimeZone.getDefault() : null);
this.parseMode = parseMode;
}
/**
* Construct from a single date pattern value.
*
* <p>This will also call the {@link #init()} method.</p>
*
* @param datePattern the date pattern
* @param timeZone the time zone
*/
public JodaDateFormatEditor(String datePattern, TimeZone timeZone) {
this(new String[] {datePattern}, timeZone);
}
/**
* Construct from multiple date pattern values.
*
* <p>This will also call the {@link #init()} method.</p>
*
* @param datePatterns the date patterns
* @param timeZone the time zone
*/
public JodaDateFormatEditor(String[] datePatterns, TimeZone timeZone) {
super();
this.datePatterns = datePatterns;
this.timeZone = timeZone;
init();
}
/**
* Initialize after properties set.
*/
public void init() {
this.dateFormatters = new DateTimeFormatter[this.datePatterns.length];
for ( int i = 0; i < this.dateFormatters.length; i++ ) {
this.dateFormatters[i] = DateTimeFormat.forPattern(datePatterns[i]);
if ( timeZone != null ) {
this.dateFormatters[i] = dateFormatters[i].withZone(
DateTimeZone.forTimeZone(timeZone));
}
}
}
@Override
public String getAsText() {
Object val = getValue();
if ( val == null ) {
return null;
}
DateTimeFormatter format = this.dateFormatters[0];
if ( val instanceof ReadableInstant ) {
return format.print((ReadableInstant)val);
} else if ( val instanceof ReadablePartial ) {
return format.print((ReadablePartial)val);
} else if ( val instanceof Date ) {
return format.print(((Date)val).getTime());
} else if ( val instanceof Calendar ) {
return format.print(((Calendar)val).getTimeInMillis());
}
throw new IllegalArgumentException("Unsupported date object ["
+val.getClass() +"]: " +val);
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
IllegalArgumentException iae = null;
// try patterns one at a time, if all fail to parse then throw exception
for ( DateTimeFormatter df : this.dateFormatters ) {
try {
DateTime dt = df.parseDateTime(text);
Object val = dt;
switch ( this.parseMode ) {
case LocalDate:
val = dt.toLocalDate();
break;
case LocalTime:
val = dt.toLocalTime();
break;
default:
// leave as DateTime
}
setValue(val);
iae = null;
break;
} catch ( IllegalArgumentException e ) {
iae = e;
}
}
if ( iae != null ) {
throw iae;
}
}
@Override
public Object clone() {
try {
return super.clone();
} catch ( CloneNotSupportedException e ) {
// should never get here
throw new RuntimeException(e);
}
}
/**
* Get the first date pattern.
*
* @return the datePattern
*/
public String getDatePattern() {
if ( datePatterns == null || datePatterns.length < 1 ) {
return null;
}
return datePatterns[0];
}
/**
* Set a single date pattern.
*
* @param datePattern the datePattern to set
*/
public void setDatePattern(String datePattern) {
this.datePatterns = new String[] {datePattern};
}
/**
* @return the timeZone
*/
public TimeZone getTimeZone() {
return timeZone;
}
/**
* @param timeZone the timeZone to set
*/
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
/**
* @return the datePatterns
*/
public String[] getDatePatterns() {
return datePatterns;
}
/**
* @param datePatterns the datePatterns to set
*/
public void setDatePatterns(String[] datePatterns) {
this.datePatterns = datePatterns;
}
/**
* @return the parseMode
*/
public ParseMode getParseMode() {
return parseMode;
}
/**
* @param parseMode the parseMode to set
*/
public void setParseMode(ParseMode parseMode) {
this.parseMode = parseMode;
}
}