【问题标题】:java.time.LocalDate and Wicket 7 DateTextFieldjava.time.LocalDate 和 Wicket 7 DateTextField
【发布时间】:2017-03-20 18:53:47
【问题描述】:

我正在尝试将 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);
    }
}

类 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())))

这些是错误消息:

最后一个原因:无法解析类的属性:类 example.ui.customer.CustomerUI 表达式:对话框 WicketMessage: 无法使用构造函数“public”实例化页面 example.ui.customer.MyCustomerOverviewPage()'。有一个例外 施工时被扔了!

堆栈跟踪:

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)

【问题讨论】:

    标签: date java-8 wicket


    【解决方案1】:

    更新,更简洁的答案

    我最初的答案远远超出了您的实际问题,但直到后来我才完全意识到。这是一个更简洁的版本,但我会在底部保留原始版本。

    您遇到的问题是因为我建议的幼稚扩展存在一个严重缺陷。

    CompoundPropertyModel 将尝试为 any 没有模型的子组件实例化模型。当您使用常规 CompoundPropertyModel 时,这不是问题;如果模型不相关,则永远不会尝试获取其对象。

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

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

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

    这样简单的东西
    @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 的工作原理。

    我将使用您的稍作修改的代码 sn-p 来说明这一点

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

    假设我们要向detailsDialog 添加一个标签,以显示CustomerUI 的“activeDate”属性。如果我们使用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 的默认行为,它仍将使用组件的检票口 ID 来确定内部模型中的属性名称是什么。所以发生的事情是这样的:

    1. 您向detailsDialog 添加了一个组件,其检票口 ID 为 “对话”
    2. 在线actualModel = super.wrapOnInheritance(component); 您的扩展程序会创建一个 PropertyModel 来引用您的 CustomerUI 模型及其属性“对话框”(因为那是 提供的组件)
    3. 在线if (actualModel.getObject() instanceof LocalDate) 你试图获得这个模型的价值。 但是,CustomerUI 对象上不存在属性“对话框”,并且 所以抛出错误

    所以最后你做对了一切。唯一的问题是您添加了一个子组件,其 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 模型。

    【讨论】:

    • 这个答案确实比它需要的要长得多。我会试着修剪一下。
    • 非常感谢 :-) 我明天会提供反馈。
    • 是否可以避免与类型参数相关的警告?
    • 警告是什么?将 actualModel 传递给包装构造函数?
    • 我已在 MyLocalDateCompoundPropertymodel 类的我自己的答案(基于你的答案)中将警告作为 cmets 插入
    【解决方案2】:

    根据 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;
            }
    
        }
    }
    

    【讨论】:

    • // warning: type Parameter 'T' hides type parameter 'T' 是指你的类的泛型变量和你的方法的泛型变量都称为T,因此你的方法的泛型隐藏了类的泛型。至于另一个警告,没有办法不强制转换actualModel,但你有 if 语句保护它,所以它应该没问题。而且我认为你也无法绕过最后一个演员表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多