简体   繁体   English

在Tapestry 5 Web应用程序中编辑复杂的Java对象

[英]Editing a complex Java object in a Tapestry 5 web application

I am using Tapestry 5.3.6 for a web application and I want the user to edit an instance of a Java class (a "bean", or POJO) using a web form (which immediately suggests the use of beaneditform ) - however the Java class to be edited has a fairly complex structure. 我正在使用Tapestry 5.3.6作为Web应用程序,我希望用户使用Web表单(立即建议使用beaneditform )编辑Java类的实例(“bean”或POJO) - 但是Java要编辑的类具有相当复杂的结构。 I am looking for the simplest way of doing this in Tapestry 5. 我正在寻找Tapestry 5中最简单的方法。

Firstly, lets define some utility classes eg 首先,让我们定义一些实用程序类,例如

public class ModelObject {
  private URI uri;
  private boolean modified;
  // the usual constructors, getters and setters ...
}

public class Literal<T> extends ModelObject {
  private Class<?> valueClass;
  private T value;
  public Literal(Class<?> valueClass) {
    this.valueClass = valueClass;
  }
  public Literal(Class<?> valueClass, T value) {
    this.valueClass = valueClass;
    this.value = value;
  }
  // the usual getters and setters ...
}

public class Link<T extends ModelObject> extends ModelObject {
  private Class<?> targetClass;
  private T target;
  public Link(Class<?> targetClass) {
    this.targetClass = targetClass;
  }
  public Link(Class<?> targetClass, T target) {
    this.targetClass = targetClass;
    this.target = target;
  }
  // the usual getters and setters ...
}

Now you can create some fairly complex data structures, for example: 现在您可以创建一些相当复杂的数据结构,例如:

public class HumanBeing extends ModelObject {
  private Literal<String> name;
  // ... other stuff
  public HumanBeing() {
    name = new Literal<String>(String.class);
  }
  // the usual getters and setters ...
}

public class Project extends ModelObject {
  private Literal<String> projectName;
  private Literal<Date> startDate;
  private Literal<Date> endDate;
  private Literal<Integer> someCounter;
  private Link<HumanBeing> projectLeader;
  private Link<HumanBeing> projectManager;
  // ... other stuff, including lists of things, that may be Literals or
  // Links ... e.g. (ModelObjectList is an enhanced ArrayList that remembers
  // the type(s) of the objects it contains - to get around type erasure ...
  private ModelObjectList<Link<HumanBeing>> projectMembers;
  private ModelObjectList<Link<Project>> relatedProjects;
  private ModelObjectList<Literal<String>> projectAliases;
  // the usual constructors, getters and setters for all of the above ...
  public Project() {
    projectName = new Literal<String>(String.class);
    startDate = new Literal<Date>(Date.class);
    endDate = new Literal<Date>(Date.class);
    someCounter = new Literal<Integer>(Integer.class);
    projectLeader = new Link<HumanBeing>(HumanBeing.class);
    projectManager = new Link<HumanBeing>(HumanBeing.class);
    projectMembers = new ModelObjectList<Link<HumanBeing>>(Link.class, HumanBeing.class);
    // ... more ...
  }
}

If you point beaneditform at an instance of Project.class, you will not get very far before you have to supply a lot of custom coercers, translators, valueencoders, etc - and then you still run into the problem that you can't use generics when "contributing" said coercers, translators, valueencoders, etc. 如果你将beaneditform指向beaneditform的一个实例,那么在你必须提供很多自定义coercers,translators,valueencoders等之前你就不会走得太远 - 然后你仍然会遇到不能使用泛型的问题当“贡献”表示coercers,translators,valueencoders等。

I then started writing my own components to get around these problems (eg ModelObjectDisplay and ModelObjectEdit ) but this would require me to understand a lot more of the guts of Tapestry than I have time to learn ... it feels like I might be able to do what I want using the standard components and liberal use of "delegate" etc. Can anyone see a simple path for me to take with this? 然后我开始编写自己的组件来解决这些问题(例如ModelObjectDisplayModelObjectEdit ),但是这需要我理解Tapestry的更多内容而不是我有时间学习...感觉就像我可能能够使用标准组件和自由使用“委托”等来做我想做的事。任何人都可以看到一条简单的路径供我使用吗?

Thanks for reading this far. 感谢您阅读这篇文章。

PS: if you are wondering why I have done things like this, it is because the model represents linked data from an RDF graph database (aka triple-store) - I need to remember the URI of every bit of data and how it relates (links) to other bits of data (you are welcome to suggest better ways of doing this too :-) PS:如果你想知道为什么我做了这样的事情,那是因为模型代表来自RDF图数据库(又称三重存储)的链接数据 - 我需要记住每一位数据的URI及其相关性(链接到其他位数据(欢迎您提出更好的方法)---)

EDIT: 编辑:

@uklance suggested using display and edit blocks - here is what I had already tried: @uklance建议使用显示和编辑块 - 这是我已经尝试过的:

Firstly, I had the following in AppPropertyDisplayBlocks.tml ... 首先,我在AppPropertyDisplayBlocks.tml中有以下内容......

    <t:block id="literal">
        <t:delegate to="literalType" t:value="literalValue" />
    </t:block>

    <t:block id="link">
        <t:delegate to="linkType" t:value="linkValue" />
    </t:block>

and in AppPropertyDisplayBlocks.java ... 并在AppPropertyDisplayBlocks.java中...

    public Block getLiteralType() {
        Literal<?> literal = (Literal<?>) context.getPropertyValue();

        Class<?> valueClass = literal.getValueClass();
        if (!AppModule.modelTypes.containsKey(valueClass))
            return null;

        String blockId = AppModule.modelTypes.get(valueClass);
        return resources.getBlock(blockId);
    }

    public Object getLiteralValue() {
        Literal<?> literal = (Literal<?>) context.getPropertyValue();
        return literal.getValue();
    }

    public Block getLinkType() {
        Link<?> link = (Link<?>) context.getPropertyValue();

        Class<?> targetClass = link.getTargetClass();
        if (!AppModule.modelTypes.containsKey(targetClass))
            return null;

        String blockId = AppModule.modelTypes.get(targetClass);
        return resources.getBlock(blockId);
    }

    public Object getLinkValue() {
        Link<?> link = (Link<?>) context.getPropertyValue();
        return link.getTarget();
    }

AppModule.modelTypes is a map from java class to a String to be used by Tapestry eg Link.class -> "link" and Literal.class -> "literal" ... in AppModule I had the following code ... AppModule.modelTypes是从java类到Tapestry使用的String的映射,例如Link.class - >“link”和Literal.class - >“literal”...在AppModule中我有以下代码...

    public static void contributeDefaultDataTypeAnalyzer(
            MappedConfiguration<Class<?>, String> configuration) {
        for (Class<?> type : modelTypes.keySet()) {
            String name = modelTypes.get(type);
            configuration.add(type, name);
        }
    }

    public static void contributeBeanBlockSource(
            Configuration<BeanBlockContribution> configuration) {

        // using HashSet removes duplicates ...
        for (String name : new HashSet<String>(modelTypes.values())) {
            configuration.add(new DisplayBlockContribution(name,
                    "blocks/AppPropertyDisplayBlocks", name));
            configuration.add(new EditBlockContribution(name,
                    "blocks/AppPropertyEditBlocks", name));
        }
    }

I had similar code for the edit blocks ... however none of this seemed to work - I think because the original object was passed to the "delegate" rather than the de-referenced object which was either the value stored in the literal or the object the link pointed to (hmm... should be [Ll]inkTarget in the above, not [Ll]inkValue). 我有类似的编辑块代码...但是这些似乎都不起作用 - 我认为因为原始对象被传递给“委托”而不是被引用的对象,该对象是存储在文字中的值或者对象链接指向(hmm ...应该是上面的[Ll] inkTarget,而不是[Ll] inkValue)。 I also kept running into errors where Tapestry couldn't find a suitable "translator", "valueencoder" or "coercer" ... I am under some time pressure so it is difficult to follow these twisty passages through in order to get out of the maze :-) 我也一直遇到错误,Tapestry找不到合适的“翻译”,“价值编码器”或“coercer”...我在一段时间的压力下因此很难跟随这些曲折的段落以便离开迷宫 :-)

I would suggest to build a thin wrapper around the Objects you would like to edit though the BeanEditForm and pass those into it. 我建议在你想通过BeanEditForm编辑的对象周围构建一个瘦包装器并将它们传递给它。 So something like: 所以类似于:

public class TapestryProject {

   private Project project;

   public TapestryProject(Project proj){
      this.project = proj;
   }

   public String getName(){
      this.project.getProjectName().getValue();
   }

   public void setName(String name){
      this.project.getProjectName().setValue(name);
   }

   etc...
}

This way tapestry will deal with all the types it knows about leaving you free of having to create your own coersions (which is quite simple in itself by the way). 这样,挂毯将处理它所知道的所有类型,让您不必创建自己的版本(顺便说一句,这本身就很简单)。

You can contribute blocks to display and edit your "link" and "literal" datatypes. 您可以提供块来显示和编辑"link""literal"数据类型。

The beaneditform , beaneditor and beandisplay are backed by the BeanBlockSource service. beaneditformbeaneditorbeandisplayBeanBlockSource服务支持。 BeanBlockSource is responsible for providing display and edit blocks for various datatypes. BeanBlockSource负责为各种数据类型提供显示和编辑块。

If you download the tapestry source code and have a look at the following files: 如果您下载了tapestry源代码并查看以下文件:

  • tapestry-core\\src\\main\\java\\org\\apache\\tapestry5\\corelib\\pages\\PropertyEditBlocks.java 挂毯核心的\\ src \\主\\ java的\\组织\\阿帕奇\\ tapestry5 \\ corelib的\\网页\\ PropertyEditBlocks.java
  • tapestry-core\\src\\main\\resources\\org\\apache\\tapestry5\\corelib\\pages\\PropertyEditBlocks.tml 挂毯核心的\\ src \\主\\资源\\组织\\阿帕奇\\ tapestry5 \\ corelib的\\网页\\ PropertyEditBlocks.tml
  • tapestry-core\\src\\main\\java\\org\\apache\\tapestry5\\services\\TapestryModule.java 挂毯核心的\\ src \\主\\ java的\\组织\\阿帕奇\\ tapestry5 \\ SERVICES \\ TapestryModule.java

You will see how tapestry contributes EditBlockContribution and DisplayBlockContribution to provide default blocks (eg for a "date" datatype). 您将看到tapestry如何贡献EditBlockContributionDisplayBlockContribution来提供默认块(例如,对于"date"数据类型)。

If you contribute to BeanBlockSource , you could provide display and edit blocks for your custom datatypes. 如果您参与BeanBlockSource ,则可以为自定义数据类型提供显示和编辑块。 This will require you reference blocks by id in a page. 这将要求您在页面中按ID引用块。 The page can be hidden from your users by annotating it with @WhitelistAccessOnly . 通过@WhitelistAccessOnly对用户进行注释,可以隐藏用户的@WhitelistAccessOnly

Here's an example of using an interface and a proxy to hide the implementation details from your model. 这是使用接口和代理隐藏模型中的实现细节的示例。 Note how the proxy takes care of updating the modified flag and is able to map URI's from the Literal array to properties in the HumanBeing interface. 请注意代理如何处理更新已修改的标志,并能够将来自Literal数组的URI映射到HumanBeing接口中的属性。

package com.github.uklance.triplestore;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

public class TripleStoreOrmTest {
    public static class Literal<T> {
        public String uri;
        public boolean modified;
        public Class<T> type;
        public T value;

        public Literal(String uri, Class<T> type, T value) {
            super();
            this.uri = uri;
            this.type = type;
            this.value = value;
        }

        @Override
        public String toString() {
            return "Literal [uri=" + uri + ", type=" + type + ", value=" + value + ", modified=" + modified + "]";
        }
    }

    public interface HumanBeing {
        public String getName();
        public void setName(String name);

        public int getAge();
        public void setAge();
    }

    public interface TripleStoreProxy {
        public Map<String, Literal<?>> getLiteralMap();
    }

    @Test
    public void testMockTripleStore() {
        Literal<?>[] literals = {
            new Literal<String>("http://humanBeing/1/Name", String.class, "Henry"),
            new Literal<Integer>("http://humanBeing/1/Age", Integer.class, 21)
        };

        System.out.println("Before " + Arrays.asList(literals));

        HumanBeing humanBeingProxy = createProxy(literals, HumanBeing.class);

        System.out.println("Before Name: " + humanBeingProxy.getName());
        System.out.println("Before Age: " + humanBeingProxy.getAge());

        humanBeingProxy.setName("Adam");

        System.out.println("After Name: " + humanBeingProxy.getName());
        System.out.println("After Age: " + humanBeingProxy.getAge());

        Map<String, Literal<?>> literalMap = ((TripleStoreProxy) humanBeingProxy).getLiteralMap();
        System.out.println("After " + literalMap);
    }

    protected <T> T createProxy(Literal<?>[] literals, Class<T> type) {
        Class<?>[] proxyInterfaces = { type, TripleStoreProxy.class };

        final Map<String, Literal> literalMap = new HashMap<String, Literal>();
        for (Literal<?> literal : literals) {
            String name = literal.uri.substring(literal.uri.lastIndexOf("/") + 1);
            literalMap.put(name,  literal);
        }

        InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getDeclaringClass().equals(TripleStoreProxy.class)) {
                    return literalMap;
                }
                if (method.getName().startsWith("get")) {
                    String name = method.getName().substring(3);
                    return literalMap.get(name).value;
                } else if (method.getName().startsWith("set")) {
                    String name = method.getName().substring(3);
                    Literal<Object> literal = literalMap.get(name);
                    literal.value = args[0];
                    literal.modified = true;
                }    
                return null;
            }
        };

        return type.cast(Proxy.newProxyInstance(getClass().getClassLoader(), proxyInterfaces, handler));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 编辑Java Web应用程序 - Editing Java Web Application 将API公开给Tapestry Web应用程序 - Exposing an API to a Tapestry web application 如何使用Tapestry在Web应用程序中返回文件 - How to return a file in web application using Tapestry Java Tapestry Web应用程序中的基本图像编辑器(打开文件,裁剪,保存到新文件) - Basic image editor (open file, crop, save to new file) in a Java Tapestry web application 复杂对象作为java rest应用程序中的查询参数 - Complex object as a queryparameter in in java rest application Java Web应用程序MVC2如果有人在编辑数据,则“锁定”数据 - Java web application MVC2 “lock” data if someone is editing it Java Web应用程序对象分派 - Java web application object dispatching 复杂的Web应用程序上的ConversionInputException - ConversionInputException on a complex web application 在Tapestry Web应用程序中处理域异常的最佳方法是什么? - What is the best way to handle domain exceptions in Tapestry web application? 使用Tapestry和Hibernate的Web应用程序在调用persist()时引发异常 - Web application using Tapestry and Hibernate throws Exception when calling persist()
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM