java.time.LocalDate和Wicket 7 DateTextField

时间:2017-03-15 08:04:12

标签: date java-8 wicket

我尝试将Wicket组件DateTextFieldjava.time.LocalDate类型的属性相关联。

根据this answer中的提示,我们尝试实施包含CompoundPropertyModel属性的自定义java.time.Localdate。我使用的是Wicket 7.6和JDK 8。

我是Wicket和Java泛型的新手。

这是我的代码:

类LocaldateModel

import java.time.*;
import java.util.Date;
import org.apache.wicket.model.IModel;

public class LocalDateModel implements IModel<Date>
{
    private static final long serialVersionUID = 7262517323706786573L;
    private IModel<LocalDate> localDateModel;

    public LocalDateModel(IModel<LocalDate> localDateModel)
    {
        this.localDateModel = localDateModel;
    }

    @Override
    public Date getObject()
    {
        return Date.from(localDateModel.getObject().atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    @Override
    public void setObject(Date object)
    {
        localDateModel.setObject(
                Instant.ofEpochMilli(object.getTime()).atZone(ZoneId.systemDefault()).toLocalDate());
    }

    @Override
    public void detach()
    {
        localDateModel.detach();
    }
}

类LocalDateModelButAlsoWrapping

import java.time.Localdate;
import java.util.Date;

public class LocalDateModelButAlsoWrapping<T> extends LocalDateModel implements IWrapModel<Date>
{
    private static final long serialVersionUID = -8539316259078354206L;

    private IModel<Date> dateModel;

    public LocalDateModelButAlsoWrapping(IModel<LocalDate> localDateModel)
    {
        super(localDateModel);
        LocalDate localDate=localDateModel.getObject();
        dateModel.setObject(Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
    }

    @Override
    public IModel<Date> getWrappedModel()
    {
        return dateModel;
    }

    @Override
    public void setObject(Date object)
    {
       dateModel.setObject(object);
    }
}

Class MyLocalDateCompoundPropertyModel

import java.time.Localdate;
import org.apache.wicket.Component;
import org.apache.wicket.model.*;

public class MyLocalDateCompoundPropertyModel<T> extends CompoundPropertyModel<T>
{
    private static final long serialVersionUID = 7112056989820105247L;

    public MyLocalDateCompoundPropertyModel(IModel<T> model)
    {
        super(model);
    }

    public MyLocalDateCompoundPropertyModel(T object)
    {
        super(object);
    }

    @Override
    public <T> IWrapModel<T> wrapOnInheritance(Component component)
    {
        IWrapModel<T> actualModel;
        actualModel = super.wrapOnInheritance(component);

        if (actualModel.getObject() instanceof LocalDate)
        {
            return new LocalDateModelButAlsoWrapping(actualModel);
        }
        else
        {
            return actualModel;
        }
    }
}

具有LocalDate属性的类

import java.time.Localdate;

public class CustomerUI implements Serializable
{
    private static final long serialVersionUID = -4071467669346190367L;

    private CustomerDTO customer;

    public LocalDate getActiveUntil()
    {
        return customer.getActiveUntil();
    }

    public void setActiveUntil(LocalDate activeUntil)
    {
        customer.setActiveUntil(activeUntil);
    }

    // more delegating getters and setters
}    

创建组件

    detailsDialog = new MyCustomerDetailsDialog("createDialog",
            new MyLocalDateCompoundPropertyModel<CustomerUI>(Model.of(new CustomerUI())))

以下是错误消息:

  

最后一个原因:无法解决类:class的属性   example.ui.customer.CustomerUI表达式:对话框WicketMessage:   无法使用构造函数&public;&#39; public实例化页面   example.ui.customer.MyCustomerOverviewPage()&#39 ;.一个例外   施工期间被扔了!

Stacktrace:

org.apache.wicket.WicketRuntimeException: Property could not be resolved for class: class example.ui.customer.CustomerUI expression: dialog
     at org.apache.wicket.core.util.lang.PropertyResolver.getGetAndSet(PropertyResolver.java:393)
     at org.apache.wicket.core.util.lang.PropertyResolver.getObjectWithGetAndSet(PropertyResolver.java:355)
     at org.apache.wicket.core.util.lang.PropertyResolver.getObjectWithGetAndSet(PropertyResolver.java:261)
     at org.apache.wicket.core.util.lang.PropertyResolver.getValue(PropertyResolver.java:111)
     at org.apache.wicket.model.AbstractPropertyModel.getObject(AbstractPropertyModel.java:86)
     at example.ui.models.MyLocalDateCompoundPropertyModel.wrapOnInheritance(MyLocalDateCompoundPropertyModel.java:35)
     at org.apache.wicket.Component.initModel(Component.java:3841)
     at org.apache.wicket.Component.getDefaultModel(Component.java:1625)
     at org.apache.wicket.MarkupContainer$2.component(MarkupContainer.java:876)
     at org.apache.wicket.MarkupContainer$2.component(MarkupContainer.java:872)
     at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:144)
     at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:123)
     at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:192)
     at org.apache.wicket.MarkupContainer.visitChildren(MarkupContainer.java:983)
     at org.apache.wicket.MarkupContainer.setDefaultModel(MarkupContainer.java:871)
     at example.ui.customer.MyCustomerDetailsDialog.<init>(MyCustomerDetailsDialog.java:44)
     at example.ui.customer.MyCustomerOverviewPage$2.<init>(MyCustomerOverviewPage.java:95)

第35行是:

 if (actualModel.getObject() instanceof LocalDate)

2 个答案:

答案 0 :(得分:2)

更新,更多简洁回答

我的原始答案超出了你的实际问题,但直到事后我才意识到。这是一个更加简洁的版本,但我会把原版保留在底部。

你遇到的问题是因为我建议的天真扩展有一个严重的缺陷。

CompoundPropertyModel将尝试为没有模型的任何子组件实例化模型。当您使用常规CompoundPropertyModel时,这不是问题;在模型不相关的地方,从不试图获得它的对象。

这里发生的是模型的这个扩展不仅会为任何无模型的孩子创建一个模型,而且还会尝试评估它以便检查属性的类型。

问题在于,您可能有子组件(例如链接),其中模型不一定会被解析为任何东西。在这个特定的例子中,您似乎添加了一个ID为“dialog”的组件;即使它可能不一定需要模型,仍然会为它调用CompoundPropertyModel,并且它将尝试从底层CustomerUI对象获取名称为“dialog”的属性。

因此,为了不遇到此问题,您需要阻止扩展程序每次都评估模型。像

这样简单的东西
@Override
public <T> IWrapModel<T> wrapOnInheritance(Component component)
{
    IWrapModel<T> actualModel;
    actualModel = super.wrapOnInheritance(component);

    if (component instanceof DateTextField && actualModel.getObject() instanceof LocalDate)
    {
        return new LocalDateModelButAlsoWrapping(actualModel);
    }
    else
    {
        return actualModel;
    }
}

希望它有所帮助。

原始答案

要了解代码无效的原因,您需要了解CompoundPropertyModel的工作原理。

我将使用您的稍微修改过的代码段来说明这个

IModel<CustomerUI> customerUIModel = Model.of(new CustomerUI());
detailsDialog = new MyCustomerDetailsDialog("createDialog", new CompoundPropertyModel<CustomerUI>(customerUIModel));

假设我们想要向显示CustomerUI的“activeDate”属性的detailsDialog添加标签。如果我们使用CompoundPropertyModel,那么为了实现这一目标,我们必须执行以下操作:

IModel<LocalDate> activeDateModel = PropertyModel(customerUIModel, "activeUntil");
detailsDialog.add(new Label("dialog", activeDateModel));

CompoundPropertyModel所做的是让我们绕过为组件提供模型并简单地执行

detailsDialog.add(new Label("activeDate"))

然后,当呈现页面时,组件将检测到它没有模型,因此它为父模型提供了为其提供模型的机会。

这就是CompoundPropertyModel将要启动的地方。它会查看请求提供模型的组件,并完成相同的操作。但是,它是如何做到的?好吧,很简单就是

return new PropertyModel(innerModel, component.getId())

因此,由于这个简单的逻辑,您可以绕过在子组件上实例化您自己的模型,并让父模型CompoundPropertyModel为它们提供模型。 但子组件的ID必须引用存储在CompoundPropertyModel 中的模型对象的某些属性。否则你将得到你得到的确切错误:

Property could not be resolved for class: class example.ui.customer.CustomerUI expression: dialog

现在让我们看一下您对CompoundPropertyModel的扩展。当组件请求为自己获取模型时,您的扩展会执行以下操作:

@Override
public <T> IWrapModel<T> wrapOnInheritance(Component component)
{
    IWrapModel<T> actualModel;
    actualModel = super.wrapOnInheritance(component);

    if (actualModel.getObject() instanceof LocalDate)
    {
        return new LocalDateModelButAlsoWrapping(actualModel);
    }
    else
    {
        return actualModel;
    }
}

它的作用是调用CompoundPropertyModel的默认行为来实例化组件的模型,然后它检测此模型是否返回LocalDate的实例并包装该模型以便有一个返回Date的模型。

由于您的扩展程序仍依赖于CompoundPropertyModel的默认行为,因此它仍将使用组件的wicket ID来确定内部模型中属性的名称。所以发生了什么:

  1. 您为其门票ID为detailsDialog添加了一个组件 “对话”
  2. 在线actualModel = super.wrapOnInheritance(component); 您的扩展程序会创建一个PropertyModel来引用您的 CustomerUI模型及其属性“对话框”(因为它是ID的 提供的组件)
  3. 在线if (actualModel.getObject() instanceof LocalDate),您尝试获取此模型的值。 但是,CustomerUI对象上没有属性“对话框” 所以错误被抛出
  4. 所以最后你做的一切都是正确的。唯一的问题是你添加了一个子组件,其wicket ID没有引用有效的属性。

    这也表明了一个问题。我提供的实施是天真的。每当添加没有模型的组件时,此扩展模型将为其创建模型,但它将始终尝试获取其值。因此,如果你添加一个甚至不应该有模型的子组件,那么复合属性模型甚至会为这些组件提供支持。

    因此,更永久的解决方案将不涉及尝试获取模型的价值。例如。您可以检查该组件是否为DateTextField

    @Override
    public <T> IWrapModel<T> wrapOnInheritance(Component component)
    {
        IWrapModel<T> actualModel;
        actualModel = super.wrapOnInheritance(component);
    
        if (component instanceof DateTextField && actualModel.getObject() instanceof LocalDate)
        {
            return new LocalDateModelButAlsoWrapping(actualModel);
        }
        else
        {
            return actualModel;
        }
    }
    

    这将推迟查看模型,直到您确定您尝试实例化的模型确实需要LocalDate模型的组件。

答案 1 :(得分:0)

根据WiseTrees的回答,我修改了我的课程。虽然在类MyLocalDateCompoundPropertyModel中存在与类型转换相关的警告,但此解决方案仍然有效。

类MyLocalDateModel

package example.ui.models;

import java.time.*;
import java.util.Date;
import org.apache.wicket.model.IModel;
import example.misc.MyDateTimeUtil;

public class MyLocalDateModel implements IModel<Date>
{
    private static final long serialVersionUID = 7262517323706786573L;
    private IModel<LocalDate> localDateModel;

    public MyLocalDateModel(IModel<LocalDate> localDateModel)
    {
        this.localDateModel = localDateModel;
    }

    @Override
    public Date getObject()
    {
        return MyDateTimeUtil.createDateFromLocalDate(localDateModel.getObject());
    }

    @Override
    public void setObject(Date object)
    {
        localDateModel.setObject(MyDateTimeUtil.createLocalDateFromDate(object));
    }

    @Override
    public void detach()
    {
        localDateModel.detach();
    }
}

类MyWrappingLocalDateModel

package example.ui.models;

import java.time.LocalDate;
import java.util.Date;
import org.apache.wicket.model.*;

public class MyWrappingLocalDateModel extends MyLocalDateModel implements IWrapModel<Date>
{
    private static final long serialVersionUID = -8539316259078354206L;

    public MyWrappingLocalDateModel(IModel<LocalDate> localDateModel)
    {
        super(localDateModel);
    }

    @Override
    public IModel<Date> getWrappedModel()
    {
        return Model.of(super.getObject());
    }

    @Override
    public void setObject(Date object)
    {
       super.setObject(object);
    }
}

类MyLocalDateCompoundPropertyModel

package example.ui.models;

import java.time.LocalDate;
import org.apache.wicket.Component;
import org.apache.wicket.extensions.markup.html.form.DateTextField;
import org.apache.wicket.model.*;

public class MyLocalDateCompoundPropertyModel<T> extends CompoundPropertyModel<T>
{
    private static final long serialVersionUID = 7112056989820105247L;

    public MyLocalDateCompoundPropertyModel(IModel<T> model)
    {
        super(model);
    }

    public MyLocalDateCompoundPropertyModel(T object)
    {
        super(object);
    }

    @Override
    public <T> IWrapModel<T> wrapOnInheritance(Component component)
    // warning: type Parameter 'T' hides type parameter 'T'
    {
        IWrapModel<T> actualModel;
        actualModel = super.wrapOnInheritance(component);

        if ((component instanceof DateTextField) && (actualModel.getObject() instanceof LocalDate))
        {
            return (IWrapModel<T>) new MyWrappingLocalDateModel((IModel<LocalDate>) actualModel);
            // warning: unchecked cast
        }
        else
        {
            return actualModel;
        }

    }
}