<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like
We use a "strict" DTD to make IE follow the alignment rules. -->
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
<body class="container">
<h3>Type Coercers</h3>
This example demonstrates adding your own type coercers.<br/><br/>
Type coercion allows you to pass a value of the wrong type to a component's parameter. Tapestry will see it and ask its TypeCoercer
service to "coerce" it to the right type.<br/><br/>
This enables passing a Long to an Integer parameter, a String to a Boolean parameter, a BigDecimal to a Double parameter, and so on.
<a href="http://tapestry.apache.org/typecoercer-service.html">The full list is here.</a><br/><br/>
Tapestry's DateField works with java.util.Date. Let's say you also wanted it to work with Joda Time's DateMidnight and LocalDate.<br/>
To achieve this, we have contributed 4 type coercers. You can see the contribution in AppModule, below.<br/>
<div class="eg">
<t:form t:id="dates" class="form-horizontal" validate="datesExample">
<div class="form-group">
<t:label for="aDateMidnight" class="col-sm-4"/>
<div class="col-sm-4">
<t:datefield t:id="aDateMidnight" value="datesExample.aDateMidnight" format="prop:dateFieldFormat"/>
</div>
</div>
<div class="form-group">
<t:label for="aLocalDate" class="col-sm-4"/>
<div class="col-sm-4">
<t:datefield t:id="aLocalDate" value="datesExample.aLocalDate" format="prop:dateFieldFormat"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-4">
<t:submit value="Save"/>
<t:eventlink event="refresh" class="btn btn-default">Refresh</t:eventlink>
</div>
</div>
</t:form>
</div>
To help you debug coercions, you can ask the TypeCoercer service to
<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/TypeCoercer.html#explain(java.lang.Class, java.lang.Class)">explain</a>
how it will coerce one type to another.
<div class="eg">
Explain Long to Integer: ${explainLongToInteger}<br/>
Explain String to Integer: ${explainStringToInteger}<br/>
Explain Date to DateMidnight: ${explainDateToDateMidnight}
</div>
References:
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a>,
<a href="http://tapestry.apache.org/type-coercion.html">Type Coercion</a>,
<a href="http://tapestry.apache.org/typecoercer-service.html">TypeCoercer Service</a>,
<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/TypeCoercer.html">TypeCoercer</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/DateField.html">DateField</a>,
<a href="http://joda-time.sourceforge.net/">Joda Time</a>.<br/><br/>
<t:pagelink page="Index">Home</t:pagelink><br/><br/>
The source for IPersonFinderServiceLocal and @EJB is shown in the Session Beans and @EJB examples.<br/><br/>
<t:tabgroup>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/lang/TypeCoercers.tml"/>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/lang/TypeCoercers.properties"/>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/lang/TypeCoercers.java"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/plain.css"/>
<t:sourcecodetab src="/business/src/main/java/jumpstart/business/domain/datestuff/DateStuffService.java"/>
<t:sourcecodetab src="/business/src/main/java/jumpstart/business/domain/datestuff/DatesExample.java"/>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/services/AppModule.java"/>
<t:sourcecodetab src="/business/src/main/java/jumpstart/util/JodaTimeUtil.java"/>
</t:tabgroup>
</body>
</html>
aDateMidnight-label=A field of type org.joda.time.DateMidnight
aLocalDate-label=A field of type org.joda.time.LocalDate
package jumpstart.web.pages.examples.lang;
import java.util.Date;
import javax.ejb.EJB;
import jumpstart.business.domain.datestuff.DatesExample;
import jumpstart.business.domain.datestuff.iface.IDateStuffServiceLocal;
import jumpstart.util.ExceptionUtil;
import org.apache.tapestry5.PersistenceConstants;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.joda.time.DateMidnight;
@Import(stylesheet = "css/examples/plain.css")
public class TypeCoercers {
// Screen fields
@Property
private DatesExample datesExample;
// Work fields
// This carries version through the redirect that follows a server-side validation failure.
@Persist(PersistenceConstants.FLASH)
private Integer versionFlash;
// Generally useful bits and pieces
@EJB
private IDateStuffServiceLocal dateStuffService;
@InjectComponent("dates")
private Form form;
@Inject
private TypeCoercer typeCoercer;
// The code
// Form bubbles up the PREPARE_FOR_RENDER event during form render.
void onPrepareForRender() throws Exception {
datesExample = findDatesExample(1L);
// If the form has errors then we're redisplaying after a redirect.
// Form will restore your input values but it's up to us to restore Hidden values.
if (form.getHasErrors()) {
datesExample.setVersion(versionFlash);
}
}
// Form bubbles up the PREPARE_FOR_SUBMIT event during form submission.
void onPrepareForSubmit() throws Exception {
datesExample = findDatesExample(1L);
}
void onValidateFromDates() {
try {
dateStuffService.changeDatesExample(datesExample);
}
catch (Exception e) {
// Display the cause. In a real system we would try harder to get a user-friendly message.
form.recordError(ExceptionUtil.getRootCauseMessage(e));
}
}
void onFailure() {
versionFlash = datesExample.getVersion();
}
void onRefresh() {
// By doing nothing the page will be displayed afresh.
}
private DatesExample findDatesExample(Long id) throws Exception {
// Ask business service to find DatesExample
DatesExample datesExample = dateStuffService.findDatesExample(id);
if (datesExample == null) {
throw new IllegalStateException("Database data has not been set up!");
}
return datesExample;
}
public String getDateFieldFormat() {
return "dd MMMM yyyy";
}
public String getExplainLongToInteger() {
try {
return typeCoercer.explain(Long.class, Integer.class);
}
catch (Exception e) {
return e.getMessage();
}
}
public String getExplainStringToInteger() {
try {
return typeCoercer.explain(String.class, Integer.class);
}
catch (Exception e) {
return e.getMessage();
}
}
public String getExplainDateToDateMidnight() {
try {
return typeCoercer.explain(Date.class, DateMidnight.class);
}
catch (Exception e) {
return e.getMessage();
}
}
}
.eg {
margin: 20px 0;
padding: 14px;
border: 1px solid #ddd;
border-radius: 6px;
-webkit-border-radius: 6px;
-mox-border-radius: 6px;
}
package jumpstart.business.domain.datestuff;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import jumpstart.business.domain.datestuff.iface.IDateStuffServiceLocal;
import jumpstart.business.domain.datestuff.iface.IDateStuffServiceRemote;
@Stateless
@Local(IDateStuffServiceLocal.class)
@Remote(IDateStuffServiceRemote.class)
public class DateStuffService implements IDateStuffServiceLocal, IDateStuffServiceRemote {
@PersistenceContext(unitName = "jumpstart")
private EntityManager em;
public DatesExample findDatesExample(Long id) {
return em.find(DatesExample.class, id);
}
public DatesExample changeDatesExample(DatesExample datesExample) {
return em.merge(datesExample);
}
}
package jumpstart.business.domain.datestuff;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import jumpstart.util.JodaTimeUtil;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
/**
* The DatesExample entity.
*/
@Entity
@SuppressWarnings("serial")
public class DatesExample implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(nullable = false)
private Long id;
@Version
@Column(nullable = false)
private Integer version;
// Traditional java-style Date fields
@Temporal(TemporalType.TIMESTAMP)
private java.util.Date aTimestamp;
@Temporal(TemporalType.DATE)
private java.util.Date aDate;
@Temporal(TemporalType.TIME)
private java.util.Date aTime;
// These fields are exposed as Joda Time types but persisted as standard Java types (Date, Timestamp, String,
// Integer).
private java.sql.Timestamp aDateTime;
private java.sql.Timestamp aDateTimeWithTZ;
private String aDateTimeTZ;
// This JSR-303 validation will be picked up by Form. For BeanEditForm, we also annotate the getter.
@NotNull
private java.sql.Date aDateMidnight;
private java.sql.Date aDateMidnightWithTZ;
private String aDateMidnightTZ;
private java.sql.Timestamp aLocalDateTime;
// This JSR-303 validation will be picked up by Form. For BeanEditForm, we also annotate the getter.
@NotNull
private java.sql.Date aLocalDate;
private java.sql.Time aLocalTimeAsTime;
private Integer aLocalTimeAsMillis;
private String aLocalTimeAsString;
public String toString() {
final String DIVIDER = ", ";
StringBuilder buf = new StringBuilder();
buf.append(this.getClass().getSimpleName() + ": ");
buf.append("[");
buf.append("id=" + id + DIVIDER);
buf.append("aTimestamp=" + aTimestamp + DIVIDER);
buf.append("aDate=" + aDate + DIVIDER);
buf.append("aTime=" + aTime + DIVIDER);
buf.append("aDateTime=" + aDateTime + DIVIDER);
buf.append("aDateTimeWithTZ=" + aDateTimeWithTZ + DIVIDER);
buf.append("aDateTimeTZ=" + aDateTimeTZ + DIVIDER);
buf.append("aDateMidnight=" + aDateMidnight + DIVIDER);
buf.append("aLocalDateTime=" + aLocalDateTime + DIVIDER);
buf.append("aLocalDate=" + aLocalDate + DIVIDER);
buf.append("aLocalTimeAsTime=" + aLocalTimeAsTime + DIVIDER);
buf.append("aLocalTimeAsMillis=" + aLocalTimeAsMillis + DIVIDER);
buf.append("aLocalTimeAsString=" + aLocalTimeAsString + DIVIDER);
buf.append("version=" + version);
buf.append("]");
return buf.toString();
}
// The need for an equals() method is discussed at http://www.hibernate.org/109.html
@Override
public boolean equals(Object obj) {
return (obj == this) || (obj instanceof DatesExample) && id != null && id.equals(((DatesExample) obj).getId());
}
// The need for a hashCode() method is discussed at http://www.hibernate.org/109.html
@Override
public int hashCode() {
return id == null ? super.hashCode() : id.hashCode();
}
public Long getId() {
return id;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Date getADate() {
return aDate;
}
public void setADate(Date aDate) {
this.aDate = aDate;
}
public Date getATime() {
return aTime;
}
public void setATime(Date aTime) {
this.aTime = aTime;
}
public Date getATimestamp() {
return aTimestamp;
}
public void setATimestamp(Date aTimestamp) {
this.aTimestamp = aTimestamp;
}
public DateTime getADateTime() {
return JodaTimeUtil.toDateTime(aDateTime);
}
public void setADateTime(DateTime dt) {
this.aDateTime = JodaTimeUtil.toSQLTimestamp(dt);
}
public DateTime getADateTimeWithTZ() {
return JodaTimeUtil.toDateTime(aDateTimeWithTZ, aDateTimeTZ);
}
public void setADateTimeWithTZ(DateTime dt) {
this.aDateTimeWithTZ = JodaTimeUtil.toSQLTimestamp(dt);
this.aDateTimeTZ = JodaTimeUtil.toTimeZoneID(dt);
}
// This JSR-303 validation will be picked up by BeanEditForm. For Form, we also annotate the field.
@NotNull
public DateMidnight getADateMidnight() {
return JodaTimeUtil.toDateMidnight(aDateMidnight);
}
public void setADateMidnight(DateMidnight dm) {
this.aDateMidnight = JodaTimeUtil.toSQLDate(dm);
}
public DateMidnight getADateMidnightWithTZ() {
return JodaTimeUtil.toDateMidnight(aDateMidnightWithTZ, aDateMidnightTZ);
}
public void setADateMidnightWithTZ(DateMidnight dm) {
this.aDateMidnightWithTZ = JodaTimeUtil.toSQLDate(dm);
this.aDateMidnightTZ = JodaTimeUtil.toTimeZoneID(dm);
}
public LocalDateTime getALocalDateTime() {
return JodaTimeUtil.toLocalDateTime(aLocalDateTime);
}
public void setALocalDateTime(LocalDateTime ldt) {
this.aLocalDateTime = JodaTimeUtil.toSQLTimestamp(ldt);
}
// This JSR-303 validation will be picked up by BeanEditForm. For Form, we also annotate the field.
@NotNull
public LocalDate getALocalDate() {
return JodaTimeUtil.toLocalDate(aLocalDate);
}
public void setALocalDate(LocalDate ld) {
this.aLocalDate = JodaTimeUtil.toSQLDate(ld);
}
public LocalTime getALocalTimeAsTime() {
return JodaTimeUtil.toLocalTime(aLocalTimeAsTime);
}
public void setALocalTimeAsTime(LocalTime lt) {
this.aLocalTimeAsTime = JodaTimeUtil.toSQLTime(lt);
}
public LocalTime getALocalTimeAsMillis() {
return JodaTimeUtil.toLocalTime(aLocalTimeAsMillis);
}
public void setALocalTimeAsMillis(LocalTime lt) {
this.aLocalTimeAsMillis = JodaTimeUtil.toIntegerMillis(lt);
}
public LocalTime getALocalTimeAsString() {
return JodaTimeUtil.toLocalTime(aLocalTimeAsString);
}
public void setALocalTimeAsString(LocalTime lt) {
this.aLocalTimeAsString = JodaTimeUtil.toString(lt);
}
}
package jumpstart.web.services;
import java.util.Map;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.Translator;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
import org.apache.tapestry5.internal.beanvalidator.BaseCCD;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.ServiceBinder;
import org.apache.tapestry5.ioc.annotations.EagerLoad;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Primary;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
import org.apache.tapestry5.ioc.services.Coercion;
import org.apache.tapestry5.ioc.services.CoercionTuple;
import org.apache.tapestry5.ioc.services.ThreadLocale;
import org.apache.tapestry5.services.BeanBlockContribution;
import org.apache.tapestry5.services.ComponentRequestFilter;
import org.apache.tapestry5.services.DisplayBlockContribution;
import org.apache.tapestry5.services.EditBlockContribution;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.DataConstants;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.apache.tapestry5.services.security.WhitelistAnalyzer;
import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
import org.apache.tapestry5.upload.services.UploadSymbols;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.slf4j.Logger;
import jumpstart.business.validation.constraints.Letters;
import jumpstart.util.JodaTimeUtil;
import jumpstart.web.translators.MoneyTranslator;
import jumpstart.web.translators.YesNoTranslator;
/**
* This module is automatically included as part of the Tapestry IoC Registry, it's a good place to configure and extend
* Tapestry, or to place your own service definitions. See http://tapestry.apache.org/5.3.4/tapestry-ioc/module.html
*/
public class AppModule {
private static final String UPLOADS_PATH = "jumpstart.upload-path";
@Inject
@Symbol(SymbolConstants.PRODUCTION_MODE)
@Property(write = false)
private static boolean productionMode;
// Add 2 services to those provided by Tapestry.
// - CountryNames, and SelectIdModelFactory are used by pages which ask Tapestry to @Inject them.
public static void bind(ServiceBinder binder) {
binder.bind(CountryNames.class);
binder.bind(SelectIdModelFactory.class, SelectIdModelFactoryImpl.class);
}
// Tell Tapestry about our custom translators and their message file.
// We do this by contributing configuration to Tapestry's TranslatorAlternatesSource service, FieldValidatorSource
// service, and ComponentMessagesSource service.
@SuppressWarnings("rawtypes")
public static void contributeTranslatorAlternatesSource(MappedConfiguration<String, Translator> configuration,
ThreadLocale threadLocale) {
configuration.add("yesno", new YesNoTranslator("yesno"));
configuration.add("money2", new MoneyTranslator("money2", 2, threadLocale));
}
public void contributeComponentMessagesSource(OrderedConfiguration<String> configuration) {
configuration.add("myTranslationMessages", "jumpstart/web/translators/TranslationMessages");
}
// Tell Tapestry about the client-side (javascript) validators that corresponds to each server-side Bean Validator.
public static void contributeClientConstraintDescriptorSource(final JavaScriptSupport javaScriptSupport,
final Configuration<ClientConstraintDescriptor> configuration) {
configuration.add(new BaseCCD(Letters.class) {
public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes) {
javaScriptSupport.require("beanvalidation/letters");
writer.attributes(DataConstants.VALIDATION_ATTRIBUTE, true, "data-validate-letters", true,
"data-letters-message", message);
}
});
}
// Tell Tapestry about our custom ValueEncoders.
// We do this by contributing configuration to Tapestry's ValueEncoderSource service.
// @SuppressWarnings("rawtypes")
// public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration) {
// configuration.addInstance(Person.class, PersonEncoder.class);
// }
// Tell Tapestry which locales we support, and tell Tapestry to use its jQuery implementation for its JavaScript.
// We do this by contributing configuration to Tapestry's ApplicationDefaults service.
public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration) {
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en_US,en_GB,fr");
configuration.add(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER, "jquery");
}
// Tell Tapestry how to detect and protect pages that require security.
// We do this by contributing a custom ComponentRequestFilter to Tapestry's ComponentRequestHandler service.
// - ComponentRequestHandler is shown in
// http://tapestry.apache.org/request-processing.html#RequestProcessing-Overview
// - Based on http://tapestryjava.blogspot.com/2009/12/securing-tapestry-pages-with.html
public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration) {
configuration.addInstance("PageProtectionFilter", PageProtectionFilter.class);
}
// Tell Tapestry how to handle WildFly 11's classpath URLs - WildFly uses a "virtual file system".
// We do this by overriding Tapestry's ClasspathURLConverter service.
// See "Running Tapestry on JBoss" (sic) in http://wiki.apache.org/tapestry/Tapestry5HowTos .
@SuppressWarnings("rawtypes")
public static void contributeServiceOverride(MappedConfiguration<Class, Object> configuration) {
// configuration.add(ClasspathURLConverter.class, new ClasspathURLConverterJBoss7());
configuration.add(ClasspathURLConverter.class, new ClasspathURLConverterWildFly11());
}
// Tell Tapestry how to handle @EJB in page and component classes.
// We do this by contributing configuration to Tapestry's ComponentClassTransformWorker service.
// - Based on http://wiki.apache.org/tapestry/JEE-Annotation.
@Primary
public static void contributeComponentClassTransformWorker(
OrderedConfiguration<ComponentClassTransformWorker2> configuration) {
configuration.addInstance("EJB", EJBAnnotationWorker.class, "before:Property");
}
// Tell Tapestry how to handle pages annotated with @WhitelistAccessOnly, eg. Tapestry's ServiceStatus and
// PageCatalog.
// The default WhitelistAnalyzer allows localhost only and only in non-production mode.
// Our aim is to make the servicestatus page available to ALL clients when not in production mode.
// We do this by contributing our own WhitelistAnalyzer to Tapestry's ClientWhitelist service.
public static void contributeClientWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration) {
if (!productionMode) {
configuration.add("NonProductionWhitelistAnalyzer", new WhitelistAnalyzer() {
@Override
public boolean isRequestOnWhitelist(Request request) {
if (request.getPath().startsWith("/core/servicestatus")) {
return true;
}
else {
// This is copied from org.apache.tapestry5.internal.services.security.LocalhostOnly
String remoteHost = request.getRemoteHost();
return remoteHost.equals("localhost") || remoteHost.equals("127.0.0.1")
|| remoteHost.equals("0:0:0:0:0:0:0:1%0") || remoteHost.equals("0:0:0:0:0:0:0:1");
}
}
}, "before:*");
}
}
// Tell Tapestry how to build our Filer service (used in the FileUpload example).
// Annotate it with EagerLoad to force resolution of symbols at startup rather than when it is first used.
@EagerLoad
public static IFiler buildFiler(Logger logger, @Inject @Symbol(UPLOADS_PATH) final String uploadsPath,
@Inject @Symbol(UploadSymbols.FILESIZE_MAX) final long fileSizeMax) {
return new Filer(logger, UPLOADS_PATH, uploadsPath, UploadSymbols.FILESIZE_MAX, fileSizeMax);
}
// Tell Tapestry how to coerce Joda Time types to and from Java Date types for the TypeCoercers example.
// We do this by contributing configuration to Tapestry's TypeCoercer service.
// - Based on http://tapestry.apache.org/typecoercer-service.html
@SuppressWarnings("rawtypes")
public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration) {
// From java.util.Date to DateMidnight
Coercion<java.util.Date, DateMidnight> toDateMidnight = new Coercion<java.util.Date, DateMidnight>() {
public DateMidnight coerce(java.util.Date input) {
// TODO - confirm this conversion always works, esp. across timezones
return JodaTimeUtil.toDateMidnight(input);
}
};
configuration.add(new CoercionTuple<>(java.util.Date.class, DateMidnight.class, toDateMidnight));
// From DateMidnight to java.util.Date
Coercion<DateMidnight, java.util.Date> fromDateMidnight = new Coercion<DateMidnight, java.util.Date>() {
public java.util.Date coerce(DateMidnight input) {
// TODO - confirm this conversion always works, esp. across timezones
return JodaTimeUtil.toJavaDate(input);
}
};
configuration.add(new CoercionTuple<>(DateMidnight.class, java.util.Date.class, fromDateMidnight));
// From java.util.Date to LocalDate
Coercion<java.util.Date, LocalDate> toLocalDate = new Coercion<java.util.Date, LocalDate>() {
public LocalDate coerce(java.util.Date input) {
// TODO - confirm this conversion always works, esp. across timezones
return JodaTimeUtil.toLocalDate(input);
}
};
configuration.add(new CoercionTuple<>(java.util.Date.class, LocalDate.class, toLocalDate));
// From LocalDate to java.util.Date
Coercion<LocalDate, java.util.Date> fromLocalDate = new Coercion<LocalDate, java.util.Date>() {
public java.util.Date coerce(LocalDate input) {
// TODO - confirm this conversion always works, esp. across timezones
return JodaTimeUtil.toJavaDate(input);
}
};
configuration.add(new CoercionTuple<>(LocalDate.class, java.util.Date.class, fromLocalDate));
}
// Tell Tapestry how its BeanDisplay and BeanEditor can handle the JodaTime types.
// We do this by contributing configuration to Tapestry's DefaultDataTypeAnalyzer and BeanBlockSource services.
// - Based on http://tapestry.apache.org/beaneditform-guide.html .
public static void contributeDefaultDataTypeAnalyzer(
@SuppressWarnings("rawtypes") MappedConfiguration<Class, String> configuration) {
configuration.add(DateTime.class, "dateTime");
configuration.add(DateMidnight.class, "dateMidnight");
configuration.add(LocalDateTime.class, "localDateTime");
configuration.add(LocalDate.class, "localDate");
configuration.add(LocalTime.class, "localTime");
}
public static void contributeBeanBlockSource(Configuration<BeanBlockContribution> configuration) {
configuration.add(new DisplayBlockContribution("dateTime", "infra/AppPropertyDisplayBlocks", "dateTime"));
configuration
.add(new DisplayBlockContribution("dateMidnight", "infra/AppPropertyDisplayBlocks", "dateMidnight"));
configuration
.add(new DisplayBlockContribution("localDateTime", "infra/AppPropertyDisplayBlocks", "localDateTime"));
configuration.add(new DisplayBlockContribution("localDate", "infra/AppPropertyDisplayBlocks", "localDate"));
configuration.add(new DisplayBlockContribution("localTime", "infra/AppPropertyDisplayBlocks", "localTime"));
configuration.add(new EditBlockContribution("dateMidnight", "infra/AppPropertyEditBlocks", "dateMidnight"));
configuration.add(new EditBlockContribution("localDate", "infra/AppPropertyEditBlocks", "localDate"));
}
}
package jumpstart.util;
import java.util.TimeZone;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class JodaTimeUtil {
public static final DateTimeFormatter localDateFormatter = DateTimeFormat.forPattern("YYYYMMDD");
// ///////////////////////////////////////////
// From JodaTime types to standard Java types
// ///////////////////////////////////////////
public static java.sql.Date toSQLDate(DateMidnight dm) {
// TODO - confirm this conversion always works, esp. across timezones
java.sql.Date d = (dm == null ? null : new java.sql.Date(dm.getMillis()));
return d;
}
public static java.util.Date toJavaDate(DateMidnight dm) {
// TODO - confirm this conversion always works, esp. across timezones
java.util.Date d = (dm == null ? null : new java.util.Date(dm.getMillis()));
return d;
}
public static String toTimeZoneID(DateMidnight dm) {
String s = (dm == null ? null : dm.getZone().getID());
return s;
}
public static java.sql.Timestamp toSQLTimestamp(DateTime dt) {
// TODO - confirm this conversion always works, esp. across timezones
java.sql.Timestamp ts = (dt == null ? null : new java.sql.Timestamp(dt.getMillis()));
return ts;
}
public static java.util.Date toJavaDate(DateTime dt) {
// TODO - confirm this conversion always works, esp. across timezones
java.util.Date ts = (dt == null ? null : new java.util.Date(dt.getMillis()));
return ts;
}
public static String toTimeZoneID(DateTime dt) {
String s = (dt == null ? null : dt.getZone().getID());
return s;
}
public static java.sql.Date toSQLDate(LocalDate ld) {
// TODO - confirm this conversion always works, esp. across timezones
java.sql.Date d = (ld == null ? null : new java.sql.Date(ld.toDateTimeAtStartOfDay().getMillis()));
return d;
}
public static java.util.Date toJavaDate(LocalDate ld) {
// TODO - confirm this conversion always works, esp. across timezones
java.util.Date d = (ld == null ? null : new java.util.Date(ld.toDateTimeAtStartOfDay().getMillis()));
return d;
}
public static String toString(LocalDate ld) {
// TODO - confirm this conversion always works, esp. across timezones
String s = (ld == null ? null : localDateFormatter.withZone(DateTimeZone.UTC).print(ld));
return s;
}
public static java.sql.Timestamp toSQLTimestamp(LocalDateTime ldt) {
// TODO - confirm this conversion always works, esp. across timezones
java.sql.Timestamp ts = (ldt == null ? null : new java.sql.Timestamp(ldt.toDateTime().getMillis()));
return ts;
}
public static java.util.Date toJavaDate(LocalDateTime ldt) {
// TODO - confirm this conversion always works, esp. across timezones
java.util.Date d = (ldt == null ? null : new java.util.Date(ldt.toDateTime().getMillis()));
return d;
}
public static java.sql.Time toSQLTime(LocalTime lt) {
// TODO - confirm this conversion always works, esp. across timezones
java.sql.Time t = (lt == null ? null : new java.sql.Time(lt.getMillisOfDay()));
return t;
}
public static Integer toIntegerMillis(LocalTime lt) {
Integer i = (lt == null ? null : new Integer(lt.getMillisOfDay()));
return i;
}
public static String toString(LocalTime lt) {
String s = (lt == null ? null : lt.toString());
return s;
}
// ///////////////////////////////////////////
// From standard Java types to JodaTime types
// ///////////////////////////////////////////
public static DateMidnight toDateMidnight(java.sql.Date d) {
// TODO - confirm this conversion always works, esp. across timezones
DateMidnight dm = (d == null ? null : new DateMidnight(d));
return dm;
}
public static DateMidnight toDateMidnight(java.util.Date d) {
// TODO - confirm this conversion always works, esp. across timezones
DateMidnight dm = (d == null ? null : new DateMidnight(d));
return dm;
}
public static DateMidnight toDateMidnight(java.sql.Date d, String timeZoneID) {
// TODO - confirm this conversion always works, esp. across timezones
DateMidnight dm = (d == null ? null : new DateMidnight(d, DateTimeZone.forID(timeZoneID)));
return dm;
}
public static DateMidnight toDateMidnight(java.util.Date d, String timeZoneID) {
// TODO - confirm this conversion always works, esp. across timezones
DateMidnight dm = (d == null ? null : new DateMidnight(d, DateTimeZone.forID(timeZoneID)));
return dm;
}
public static DateTime toDateTime(java.sql.Timestamp ts) {
// TODO - confirm this conversion always works, esp. across timezones
DateTime dt = (ts == null ? null : new DateTime(ts));
return dt;
}
public static DateTime toDateTime(java.util.Date d) {
// TODO - confirm this conversion always works, esp. across timezones
DateTime dt = (d == null ? null : new DateTime(d));
return dt;
}
public static DateTime toDateTime(java.sql.Timestamp ts, String timeZoneID) {
// TODO - confirm this conversion always works, esp. across timezones
DateTime dt = (ts == null ? null : new DateTime(ts, DateTimeZone.forID(timeZoneID)));
return dt;
}
public static DateTime toDateTime(java.util.Date d, String timeZoneID) {
// TODO - confirm this conversion always works, esp. across timezones
DateTime dt = (d == null ? null : new DateTime(d, DateTimeZone.forID(timeZoneID)));
return dt;
}
public static LocalDate toLocalDate(java.sql.Date d) {
// TODO - confirm this conversion always works, esp. across timezones
LocalDate ld = (d == null ? null : LocalDate.fromDateFields(d));
return ld;
}
public static LocalDate toLocalDate(java.util.Date d) {
// TODO - confirm this conversion always works, esp. across timezones
LocalDate ld = (d == null ? null : LocalDate.fromDateFields(d));
return ld;
}
public static LocalDate toLocalDate(String s) {
// TODO - confirm this conversion always works, esp. across timezones
LocalDate ld = (s == null ? null : localDateFormatter.withZone(DateTimeZone.UTC).parseDateTime(s).toLocalDate());
return ld;
}
public static LocalDateTime toLocalDateTime(java.sql.Timestamp ts) {
// TODO - confirm this conversion always works, esp. across timezones
LocalDateTime ldt = (ts == null ? null : LocalDateTime.fromDateFields(ts));
return ldt;
}
public static LocalDateTime toLocalDateTime(java.util.Date d) {
// TODO - confirm this conversion always works, esp. across timezones
LocalDateTime ldt = (d == null ? null : LocalDateTime.fromDateFields(d));
return ldt;
}
public static LocalTime toLocalTime(java.sql.Time t) {
// TODO - confirm this conversion always works, esp. across timezones
LocalTime lt = (t == null ? null : new LocalTime(t, DateTimeZone.UTC));
return lt;
}
public static LocalTime toLocalTime(Integer i) {
LocalTime lt = (i == null ? null : LocalTime.fromMillisOfDay(i));
return lt;
}
public static LocalTime toLocalTime(String s) {
LocalTime lt = (s == null ? null : new LocalTime(s));
return lt;
}
// ///////////////////////////////////////////
// Tests
// ///////////////////////////////////////////
public static void main(String[] args) {
test_dateMidnight();
System.out.println(" ");
test_dateMidnight_tz();
System.out.println(" ");
test_dateTime();
System.out.println(" ");
test_dateTime_tz();
System.out.println(" ");
test_localDate();
System.out.println(" ");
test_localDate_tz();
System.out.println(" ");
test_localDateTime();
System.out.println(" ");
test_localDate_shift_java_tz();
System.out.println(" ");
test_localDate_shift_joda_tz();
System.out.println(" ");
test_localTime_as_integer();
System.out.println(" ");
test_localTime_as_string();
System.out.println(" ");
}
public static DateTimeZone losAngeles = DateTimeZone.forID("America/Los_Angeles");
public static DateTimeZone brisbane = DateTimeZone.forID("Australia/Brisbane");
public static DateTimeZone perth = DateTimeZone.forID("Australia/Perth");
public static void test_dateMidnight() {
System.out.println("Test DateMidnight");
DateMidnight dm1 = new DateMidnight();
java.sql.Date d = toSQLDate(dm1);
DateMidnight dm2 = toDateMidnight(d);
System.out.println("DateMidnight 1 = " + dm1);
System.out.println("Date = " + d);
System.out.println("DateMidnight 2 = " + dm2);
if (!dm2.equals(dm1)) {
throw new IllegalStateException();
}
}
public static void test_dateMidnight_tz() {
System.out.println("Test DateMidnight with timezone");
DateMidnight dm1 = new DateMidnight(losAngeles);
java.sql.Date d = toSQLDate(dm1);
String tzID = toTimeZoneID(dm1);
DateMidnight dm2 = toDateMidnight(d, tzID);
System.out.println("DateMidnight 1 = " + dm1);
System.out.println("Date = " + d);
System.out.println("TimeZoneID = " + tzID);
System.out.println("DateMidnight 2 = " + dm2);
if (!dm2.equals(dm1)) {
throw new IllegalStateException();
}
}
public static void test_dateTime() {
System.out.println("Test DateTime");
DateTime dt1 = new DateTime();
java.sql.Timestamp ts = toSQLTimestamp(dt1);
DateTime dt2 = toDateTime(ts);
System.out.println("DateTime 1 = " + dt1);
System.out.println("Timestamp = " + ts);
System.out.println("DateTime 2 = " + dt2);
if (!dt2.equals(dt1)) {
throw new IllegalStateException();
}
}
public static void test_dateTime_tz() {
System.out.println("Test DateTime with timezone");
DateTime dt1 = new DateTime(losAngeles);
java.sql.Timestamp ts = toSQLTimestamp(dt1);
String tzID = toTimeZoneID(dt1);
DateTime dt2 = toDateTime(ts, tzID);
System.out.println("DateTime 1 = " + dt1);
System.out.println("Timestamp = " + ts);
System.out.println("TimeZoneID = " + tzID);
System.out.println("DateTime 2 = " + dt2);
if (!dt2.equals(dt1)) {
throw new IllegalStateException();
}
}
public static void test_localDate() {
System.out.println("Test LocalDate");
LocalDate ld1 = new LocalDate();
java.sql.Date d = toSQLDate(ld1);
LocalDate ld2 = toLocalDate(d);
System.out.println("LocalDate 1 = " + ld1);
System.out.println("Date = " + d);
System.out.println("LocalDate 2 = " + ld2);
if (!ld2.equals(ld1)) {
throw new IllegalStateException();
}
}
public static void test_localDate_tz() {
System.out.println("Test LocalDate with timezone");
DateMidnight dm1 = new DateMidnight(losAngeles);
LocalDate ld1 = new LocalDate(dm1);
java.sql.Date d = toSQLDate(ld1);
LocalDate ld2 = toLocalDate(d);
System.out.println("DateTime 1 = " + dm1);
System.out.println("LocalDate 1 = " + ld1);
System.out.println("Date = " + d);
System.out.println("LocalDate 2 = " + ld2);
if (!ld2.equals(ld1)) {
throw new IllegalStateException();
}
}
public static void test_localDate_shift_java_tz() {
System.out.println("Test LocalDate with shifted Java timezone");
TimeZone originalTZ = TimeZone.getDefault();
TimeZone losAngelesTZ = TimeZone.getTimeZone("America/Los_Angeles");
TimeZone.setDefault(losAngelesTZ);
LocalDate ld1 = new LocalDate();
System.out.println("ld1 LocalDate() = " + ld1 + " when default TZ = " + TimeZone.getDefault());
java.sql.Date d = toSQLDate(ld1);
System.out.println("d toSQLDate(ld1) = " + d + " when default TZ = " + TimeZone.getDefault());
TimeZone.setDefault(originalTZ);
System.out.println("d toSQLDate(ld1) = " + d + " when default TZ = " + TimeZone.getDefault());
LocalDate ld2 = toLocalDate(d);
System.out.println("ld2 toLocalDate(d) = " + ld2 + " when default TZ = " + TimeZone.getDefault());
TimeZone.setDefault(originalTZ);
if (!ld2.equals(ld1)) {
throw new IllegalStateException();
}
}
public static void test_localDate_shift_joda_tz() {
System.out.println("Test LocalDate with shifted JodaTime timezone");
DateTimeZone originalTZ = DateTimeZone.getDefault();
DateTimeZone losAngelesTZ = DateTimeZone.forID("America/Los_Angeles");
DateTimeZone.setDefault(losAngelesTZ);
LocalDate ld0 = new LocalDate(losAngelesTZ);
System.out.println("ld0 LocalDate(losAngelesTZ) = " + ld0 + " when default TZ = " + DateTimeZone.getDefault());
DateTimeZone.setDefault(losAngelesTZ);
LocalDate ld1 = new LocalDate();
System.out.println("ld1 LocalDate() = " + ld1 + " when default TZ = " + DateTimeZone.getDefault());
java.sql.Date d0 = toSQLDate(ld1);
System.out.println("d0 toSQLDate(ld0) = " + d0 + " when default TZ = " + DateTimeZone.getDefault());
java.sql.Date d1 = toSQLDate(ld1);
System.out.println("d1 toSQLDate(ld1) = " + d1 + " when default TZ = " + DateTimeZone.getDefault());
DateTimeZone.setDefault(originalTZ);
System.out.println("d1 toSQLDate(ld1) = " + d1 + " when default TZ = " + DateTimeZone.getDefault());
DateTimeZone.setDefault(originalTZ);
LocalDate ld2 = toLocalDate(d1);
System.out.println("ld2 toLocalDate(d1) = " + ld2 + " when default TZ = " + DateTimeZone.getDefault());
DateTimeZone.setDefault(originalTZ);
if (!ld2.equals(ld1)) {
throw new IllegalStateException();
}
}
// public static void test_localDate_tz() {
// System.out.println("Test LocalDate with different timezones");
// LocalDate ld1 = new LocalDate(losAngeles);
// java.sql.Date d1 = toSQLDate(ld1);
// LocalDate ld2 = new LocalDate(perth);
// java.sql.Date d2 = toSQLDate(ld2);
// LocalDate ld3 = new LocalDate();
// java.sql.Date d3 = toSQLDate(ld3);
// System.out.println("LocalDate 1 = " + ld1);
// System.out.println("Date = " + d);
// System.out.println("LocalDate 2 = " + ld2);
// if (!ld2.equals(ld1)) {
// throw new IllegalStateException();
// }
// }
public static void test_localDateTime() {
System.out.println("Test LocalDateTime");
LocalDateTime ldt1 = new LocalDateTime();
java.sql.Timestamp ts = toSQLTimestamp(ldt1);
LocalDateTime ldt2 = toLocalDateTime(ts);
System.out.println("LocalDateTime 1 = " + ldt1);
System.out.println("Timestamp = " + ts);
System.out.println("LocalDateTime 2 = " + ldt2);
if (!ldt2.equals(ldt1)) {
throw new IllegalStateException();
}
}
public static void test_localTime_as_integer() {
System.out.println("Test LocalTime as Integer");
LocalTime lt1 = new LocalTime();
Integer i = toIntegerMillis(lt1);
LocalTime lt2 = toLocalTime(i);
System.out.println("LocalTime 1 = " + lt1);
System.out.println("Integer = " + i);
System.out.println("LocalTime 2 = " + lt2);
if (!lt2.equals(lt1)) {
throw new IllegalStateException();
}
}
public static void test_localTime_as_string() {
System.out.println("Test LocalTime as String");
LocalTime lt1 = new LocalTime();
String t = toString(lt1);
LocalTime lt2 = toLocalTime(t);
System.out.println("LocalTime 1 = " + lt1);
System.out.println("String = " + t);
System.out.println("LocalTime 2 = " + lt2);
if (!lt2.equals(lt1)) {
throw new IllegalStateException();
}
}
}