9

我有一堂课,里面有一个图表。我迭代图形并创建一个构建图形的字符串,然后我将该字符串写入 Java 文件。有没有更好的方法来做到这一点,我读到了 JDT 和 CodeModel,但我真的需要一些关于如何使用它的提示。

编辑

我正在做一个正则表达式代码生成器,到目前为止,我已经将正则表达式转换为有向图中表示的 DFA(使用 grail 库)。当我拥有 DFA 时,下一步是生成一个具有三种方法的类,第一种方法构建相同的图 (DFA),第二种方法从一个节点移动到另一个节点,如果输入字符串被接受,则第三种方法匹配一个。只有第一种方法根据正则表达式输入而变化,其他两种方法是静态的,并且对于每个生成的 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();

更具可读性,不是吗?要做到这一点,您需要两个构建器。首先是 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 编写的构建器),例如Make a builder

于 2012-05-02T12:29:30.967 回答
2

一种更好的 Java 代码生成器方法... 像ANTLR这样的工具怎么样,它是一种专门为实现具有代码生成支持的词法分析器/解析器而创建的现代工具。它有很好的文档,包括两本书:

即使不使用 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

这个问题仍然有点模糊,但这里有一些建议:

  • 创建一个包含静态函数的基类,并使您生成的类扩展它。这样您就不必继续重写静态函数。
  • 你真的需要每个图一个类吗?通常你会有一个类,它将图形作为构造函数的参数,并且只有同一个类的不同对象实例
  • 你能序列化有向图吗?如果是这样,这是存储和恢复它的更好方法。
于 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>
}
}

您的数据模型应该相当简单——一个包含一个键的 Map,其值是一个节点列表。如果您以后发现您的模板需要更多信息,您可以随时更改您的数据模型。只要所需字段是公共的或具有公共 getter,任何 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);

有关使用 Maps 的数据模型的一个很好的示例,请参阅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 回答