4

私は Web アプリケーションに Tapestry 5.3.6 を使用しており、ユーザーが Web フォームを使用して Java クラス (「Bean」、または POJO) のインスタンスを編集することを望んでいます (すぐに の使用を提案しますbeaneditform) - ただし、Java クラスは編集対象はかなり複雑な構造をしています。Tapestry 5 でこれを行う最も簡単な方法を探しています。

まず、いくつかのユーティリティ クラスを定義しましょう。

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 ...
}

これで、次のようなかなり複雑なデータ構造を作成できます。

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 ...
  }
}

Project.class のインスタンスをbeaneditform指定すると、多くのカスタム コーサー、トランスレーター、バリューエンコーダーなどを提供する必要が生じる前に、それほど遠くまで到達することはありません。それでも、ジェネリックを使用できないという問題に遭遇します「貢献する」とは、強制者、翻訳者、バリューエンコーダーなどです。

次に、これらの問題を回避するために独自のコンポーネントを作成し始めました (例:ModelObjectDisplayおよびModelObjectEdit)。しかし、これには、学習する時間よりもはるかに多くのタペストリーの内臓を理解する必要があります。標準コンポーネントを使用し、「デリゲート」などを自由に使用したいのですが、これで私がたどる簡単な道を誰かが見ることができますか?

ここまで読んでくれてありがとう。

PS: なぜ私がこのようなことをしたのか疑問に思っているなら、それはモデルが RDF グラフ データベース (別名トリプル ストア) からのリンクされたデータを表しているためです。リンク)他のデータへのリンク(これを行うためのより良い方法を提案することも歓迎します:-)

編集:

@uklanceは、表示ブロックと編集ブロックの使用を提案しました-これは私がすでに試したことです:

まず、 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>

そして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 は Java クラスからタペストリーで使用される String へのマップです。

    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));
        }
    }

私は編集ブロックに同様のコードを持っていました...しかし、これはどれもうまくいかないようでした.リテラルに格納された値またはリンクが指しているオブジェクト (うーん... [Ll]inkValue ではなく、[Ll]inkTarget である必要があります)。また、Tapestry が適切な「トランスレーター」、「バリューエンコーダー」、または「コーサー」を見つけられないというエラーに遭遇し続けました...私はしばらくプレッシャーにさらされているので、抜け出すためにこれらの曲がりくねった通路をたどるのは困難です迷路 :-)

4

3 に答える 3

2

"link"ブロックを提供して、および"literal"データ型を表示および編集できます。

、、beaneditformおよびはサービスによってサポートされていbeaneditorます。さまざまなデータ型の表示および編集ブロックを提供する責任があります。beandisplayBeanBlockSourceBeanBlockSource

タペストリーのソースコードをダウンロードして、次のファイルを確認した場合:

  • Tapestry-core \ src \ main \ java \ org \ apache \ tapestry5 \ corelib \ pages \ PropertyEditBlocks.java
  • Tapestry-core \ src \ main \ resources \ org \ apache \ tapestry5 \ corelib \ pages \ PropertyEditBlocks.tml
  • Tapestry-core \ src \ main \ java \ org \ apache \ tapestry5 \ services \ TapestryModule.java

タペストリーがどのように貢献EditBlockContributionDisplayBlockContribution、デフォルトのブロック("date"データ型など)を提供するかがわかります。

に貢献する場合BeanBlockSourceは、カスタムデータ型の表示ブロックと編集ブロックを提供できます。これには、ページ内のIDでブロックを参照する必要があります。ページに。で注釈を付けることにより、ページをユーザーから非表示にすることができます@WhitelistAccessOnly

于 2013-03-05T10:37:03.773 に答える
2

BeanEditForm を介して編集したいオブジェクトの周りに薄いラッパーを構築し、それらをそれに渡すことをお勧めします。次のようなものです:

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...
}

このように、タペストリーは、独自の強制を作成する必要がないことを知っているすべてのタイプを処理します (ちなみに、それ自体は非常に簡単です)。

于 2013-03-04T09:16:21.530 に答える
0

インターフェイスとプロキシを使用して実装の詳細をモデルから隠す例を次に示します。プロキシが変更されたフラグの更新をどのように処理し、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));
    }
}
于 2013-03-06T17:54:12.547 に答える