9

グラフが入ったクラスがあります。グラフを繰り返し、グラフを作成する文字列を作成してから、その文字列をJavaファイルに書き込みます。これを行うためのより良い方法はありますか、私はJDTとCodeModelについて読みましたが、実際にそれを使用する方法のヒントが必要です。

編集

私は正規表現コードジェネレーターを実行していますが、これまでのところ、正規表現を有向グラフで表されるDFAに変換しました(grailライブラリを使用)。DFAがある場合、次のステップは3つのメソッドを持つクラスを生成することです。最初のメソッドは同じグラフ(DFA)を作成し、2番目のメソッドは1つのノードから別のノードに移動し、3番目のメソッドは入力文字列が受け入れられる場合に一致します。正規表現の入力に応じて変更されるのは最初のメソッドのみで、他の2つは静的であり、生成されたJavaクラスごとに同じです。

私の文字列ベースのアプローチは次のようになります。

 import grail.interfaces.DirectedEdgeInterface;
 import grail.interfaces.DirectedGraphInterface;
 import grail.interfaces.DirectedNodeInterface;
 import grail.interfaces.EdgeInterface;
 import grail.iterators.EdgeIterator;
 import grail.iterators.NodeIterator;
 import grail.properties.GraphProperties;
 import grail.setbased.SetBasedDirectedGraph;

 public class ClassName {

private SetBasedDirectedGraph graph = new SetBasedDirectedGraph();
private static DirectedNodeInterface state;
private static DirectedNodeInterface currentState;
protected DirectedEdgeInterface edge;

public ClassName() {
    buildGraph();
}

protected void buildGraph() {

    // Creating Graph Nodes (Automaton States)

    state = graph.createNode(3);
    state.setProperty(GraphProperties.LABEL, "3");
    state.setProperty(GraphProperties.DESCRIPTION, "null");
    graph.addNode(state);
    state = graph.createNode(2);
    state.setProperty(GraphProperties.LABEL, "2");
    state.setProperty(GraphProperties.DESCRIPTION, "null");
    graph.addNode(state);
    state = graph.createNode(1);
    state.setProperty(GraphProperties.LABEL, "1");
    state.setProperty(GraphProperties.DESCRIPTION, "Accepted");
    graph.addNode(state);
    state = graph.createNode(0);
    state.setProperty(GraphProperties.LABEL, "0");
    state.setProperty(GraphProperties.DESCRIPTION, "Initial");
    graph.addNode(state);
            .....


    // Creating Graph Edges (Automaton Transitions)

    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(2),
            (DirectedNodeInterface) graph.getNode(1));
    edge.setProperty((GraphProperties.LABEL), "0");
    graph.addEdge(edge);
    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(2),
            (DirectedNodeInterface) graph.getNode(2));
    edge.setProperty((GraphProperties.LABEL), "1");
    graph.addEdge(edge);
    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(1),
            (DirectedNodeInterface) graph.getNode(1));
    edge.setProperty((GraphProperties.LABEL), "0");
    graph.addEdge(edge);
    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(1),
            (DirectedNodeInterface) graph.getNode(3));
    edge.setProperty((GraphProperties.LABEL), "1");
    graph.addEdge(edge);
    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(0),
            (DirectedNodeInterface) graph.getNode(1));
    edge.setProperty((GraphProperties.LABEL), "0");
    graph.addEdge(edge);
    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(0),
            (DirectedNodeInterface) graph.getNode(2));
    edge.setProperty((GraphProperties.LABEL), "1");
    graph.addEdge(edge);
}
}  
4

5 に答える 5

4

別の解決策は、現在のテクノロジーに固執するが、ビルダーパターンで小さなレイヤーを提供することです。ビルダーを実装するには、少しの時間の労力が必要ですが、はるかに読みやすいコードを取得します。

コードの最初の部分を実装しました。適切なビルダーを使用すると、次のように書くことができます。

graph = new GraphBuilder()
    .createNode(3).setLabel("3").setDescription("null").add()
    .createNode(2).setLabel("2").setDescription("null").add()
    .createNode(1).setLabel("1").setDescription("Accepted").add()
    .createNode(0).setLabel("0").setDescription("Initial").add()
    // unimplemented start
    .createEdge(2, 1).setLabel("0").add()
    .createEdge(2, 2).setLabel("1").add()
    .createEdge(1, 1).setLabel("0").add()
    .createEdge(1, 3).setLabel("1").add()
    .createEdge(0, 1).setLabel("0").add()
    .createEdge(0, 2).setLabel("1").add()
    // unimplemented end
    .build();

ずっと読みやすいですね。これを取得するには、2つのビルダーが必要です。最初にGraphBuilderが登場します。

package at.corba.test.builder;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Builder for generating graphs.
 * @author ChrLipp
 */
public class GraphBuilder {
    /** List of StateBuilder, accesable via nodeNumber. */
    Map<Integer, StateBuilder> stateBuilderMap = new LinkedHashMap<Integer, StateBuilder>();

    /**
     * Delegates node-specific building to NodeBuilder.
     * @param nodeNumber Number of node to create
     * @return NodeBuilder for the node instance to create.
     */
    public StateBuilder createNode(final int nodeNumber) {
        StateBuilder builder = new StateBuilder(this);
        stateBuilderMap.put(nodeNumber, builder);
        return  builder;
    }

    /**
     * Builder function to initialise the graph.
     */
    public SetBasedDirectedGraph build() {
        SetBasedDirectedGraph graph = new SetBasedDirectedGraph();

        for (int key : stateBuilderMap.keySet()) {
            StateBuilder builder = stateBuilderMap.get(key);
            State state = graph.createNode(key);
            state = builder.build(state);
            graph.addNode(state);
        }

        return graph;
    }
}

そしてStateBuilderより:

package at.corba.test.builder;

import java.util.HashMap;
import java.util.Map;

/**
 * Builder for generating states.
 * @author ChrLipp
 */
public class StateBuilder {
    /** Parent builder */
    private final GraphBuilder graphBuilder;

    /** Properties for this node */
    Map<GraphProperties, String> propertyMap = new HashMap<GraphProperties, String>();

    /**
     * ctor.
     * @param graphBuilder  Link to parent builder
     * @param nodeNumber    Node to create
     */
    public StateBuilder(final GraphBuilder graphBuilder)  {
        this.graphBuilder = graphBuilder;
    }

    /**
     * Property setter for property Label.
     * @param label value for property label
     * @return current NodeBuilder instance for method chaining
     */
    public StateBuilder setLabel(final String label) {
        propertyMap.put(GraphProperties.LABEL, label);
        return this;
    }

    /**
     * Property setter for description Label.
     * @param description value for description label
     * @return current NodeBuilder instance for method chaining
     */
    public StateBuilder setDescription(final String description) {
        propertyMap.put(GraphProperties.DESCRIPTION, description);
        return this;
    }

    /**
     * DSL function to close the node section and to return control to the parent builder.
     * @return
     */
    public GraphBuilder add() {
        return graphBuilder;
    }

    /**
     * Builder function to initialise the node.
     * @return newly generated node
     */
    public State build(final State state) {
        for (GraphProperties key : propertyMap.keySet()) {
            String value = propertyMap.get(key);
            state.setProperty(key, value);
        }

        return state;
    }
}

エッジについても同じことをしますが、私はこれを実装しませんでした:-)。Groovyでは、ビルダーの作成がさらに簡単になります(私の実装はJavaで記述されたビルダーです)。たとえば、ビルダーの作成を参照してください

于 2012-05-02T12:29:30.967 に答える
2

Javaでのコードジェネレーターのより良い方法...コード生成をサポートするレクサー/パーサーを実装するために特別に作成された最新のツールであるANTLRのようなツールはどうですか。2冊の本を含む素晴らしいドキュメントがあります:

最後のものは、ANTLRを使用していない場合でも役立ちます。

于 2012-05-01T19:12:34.187 に答える
2

非常に簡単な例が次のブログにあります。

http://namanmehta.blogspot.in/2010/01/use-codemodel-to-generate-java-source.html

あなたはそれを見たいと思うかもしれません。

jcodemodelの問題は、JAX-Bなどの一般的なコードジェネレーターによって内部的に使用されており、十分に文書化されていないことです。また、チュートリアルもありません。ただし、このライブラリを使用する場合は、ユーザーが経験/問題の説明と解決策を文書化したさまざまなブログを見ることができます。

幸運を祈ります

于 2012-05-02T12:10:17.490 に答える
1

質問についてはまだ少しあいまいですが、ここにいくつかの提案があります:

  • 静的関数を含む基本クラスを作成し、生成されたクラスにそれを拡張させます。そうすれば、静的関数を書き直し続ける必要はありません。
  • グラフごとに本当に1つのクラスが必要ですか?通常、コンストラクターへのパラメーターとしてグラフを取得する1つのクラスがあり、同じクラスの異なるオブジェクトインスタンスがあります。
  • 有向グラフをシリアル化できますか?もしそうなら、それはそれを保存して復活させるためのより良い方法です。
于 2012-04-25T18:27:16.443 に答える
1

コード生成が必要ないくつかのプロジェクト(メッセージのエンコード/デコードクラスなど)には、あまり知られていないFreeMarkerという製品を使用しました。これは、メモリモデルを生成し、それをテンプレートにフィードするJavaベースのソリューションです。彼らのホームページから:

FreeMarkerは「テンプレートエンジン」です。テンプレートに基づいてテキスト出力(HTMLから自動生成されたソースコードまで)を生成するための汎用ツール。これはJavaパッケージであり、Javaプログラマー向けのクラスライブラリです。これは、それ自体がエンドユーザー向けのアプリケーションではなく、プログラマーが製品に組み込むことができるものです。

FreeMarkerを使用するには、データモデルとテンプレートを作成して、構築しようとしているクラスのコードを生成します。このソリューションには追加の学習オーバーヘッドがありますが、習得が容易である必要があり、将来のコード生成要件や将来の他のプロジェクトに非常に役立ちます。

更新:質問で指定されたクラスのテンプレートは次のとおりです(注:私はテストしていません):

import grail.interfaces.DirectedEdgeInterface;
import grail.interfaces.DirectedGraphInterface;
import grail.interfaces.DirectedNodeInterface;
import grail.interfaces.EdgeInterface;
import grail.iterators.EdgeIterator;
import grail.iterators.NodeIterator;
import grail.properties.GraphProperties;
import grail.setbased.SetBasedDirectedGraph;

public class ClassName {

private SetBasedDirectedGraph graph = new SetBasedDirectedGraph();
private static DirectedNodeInterface state;
private static DirectedNodeInterface currentState;
protected DirectedEdgeInterface edge;

public ClassName() {
    buildGraph();
}

protected void buildGraph() {

    // Creating Graph Nodes (Automaton States)
<#list nodes as node>
    state = graph.createNode(${node.id});
    state.setProperty(GraphProperties.LABEL, "${node.id}");
    state.setProperty(GraphProperties.DESCRIPTION, "null");
    graph.addNode(state);
</#list>

    // Creating Graph Edges (Automaton Transitions)
<#assign edgeCount = 0>
<#list nodes as node1>
<#list nodes as node2>
    edge = graph.createEdge(null, (DirectedNodeInterface) graph.getNode(${node1.id}),
            (DirectedNodeInterface) graph.getNode(${node2.id}));
    edge.setProperty((GraphProperties.LABEL), "${edgeCount}");
    graph.addEdge(edge);
<#assign edgeCount = edgeCount + 1>
</#list>
</#list>
}
}

データモデルはかなり単純である必要があります。値がノードのリストである1つのキーを含むマップです。後でテンプレートにさらに情報が必要になった場合は、いつでもデータモデルを変更できます。必須フィールドがパブリックであるか、パブリックゲッターを持っている限り、Javaオブジェクトはデータモデル内で機能する必要があります。

Map<String, Object> root = new HashMap<String, Object>();
List<Integer> nodes = new ArrayList<Integer>();
nodes.add(1);
nodes.add(2);
...
root.put("nodes", nodes);

マップを使用したデータモデルの優れた例については、FreeMarkerマニュアルのこのページを参照してください。

次のステップは、FreeMarker APIを使用して、テンプレートとデータモデルを組み合わせてクラスを作成することです。これは私があなたのケースのために修正したFreeMarkerマニュアルからの例です:

import freemarker.template.*;
import java.util.*;
import java.io.*;

public class Test {

    public static void main(String[] args) throws Exception {

        /* ------------------------------------------------------------------- */    
        /* You should do this ONLY ONCE in the whole application life-cycle:   */    

        /* Create and adjust the configuration */
        Configuration cfg = new Configuration();
        cfg.setDirectoryForTemplateLoading(
                new File("/where/you/store/templates"));
        cfg.setObjectWrapper(new DefaultObjectWrapper());

        /* ------------------------------------------------------------------- */    
        /* You usually do these for many times in the application life-cycle:  */    

        /* Get or create a template */
        Template temp = cfg.getTemplate("test.ftl");

        /* Create a data-model */
        Map<String, Object> root = new HashMap<String, Object>();
        List<Integer> nodes = new ArrayList<Integer>();
        nodes.add(1);
        nodes.add(2);
        ...
        root.put("nodes", nodes);    

        /* Merge data-model with template */
        Writer out = new OutputStreamWriter(System.out);
        temp.process(root, out);
        out.flush();
    }
}  

FreeMarkerのマニュアルは非常に役立ち、多くの役立つ例が含まれています。このアプローチに興味がある場合は、スタートガイドを参照してください。

于 2012-04-30T19:38:52.663 に答える