我有一个里面有图表的课程。我迭代图形并创建一个构建图形的字符串,然后我将该字符串写入Java文件。 有没有更好的方法,我读到JDT和CodeModel,但我真的需要一些如何使用它的提示。
修改
我正在做一个正则表达式代码生成器,到目前为止,我已将正则表达式转换为有向图中表示的DFA(使用grail库)。当我有DFA时,下一步是生成一个有三个方法的类,第一个构建相同的图形(DFA),第二个方法从一个节点移动到另一个节点,第三个方法匹配输入字符串是否被接受。只有第一种方法根据regularrexpression输入而改变,另外两种方法是静态的,并且对于每个生成的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);
}
}
答案 0 :(得分:4)
另一种解决方案是坚持当前的技术,但提供一个builder pattern的小层。要实现构建器,您需要花费一小段时间,但可以获得更好的可读代码。
我实现了代码的第一部分。使用适当的构建器,您可以写:
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。
答案 1 :(得分:2)
以下博客给出了一个非常简单的例子:
http://namanmehta.blogspot.in/2010/01/use-codemodel-to-generate-java-source.html
你可能想看看它。
jcodemodel的问题在于它是由JAX-B之类的流行代码生成器内部使用的,并且没有很好的文档记录。它也没有任何教程。但是,如果您想使用此库,您可以查看用户记录其体验/问题描述和解决方案的不同博客。
祝你好运
答案 2 :(得分:1)
这个问题还有点模糊,但这里有一些建议:
答案 3 :(得分: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);
请参阅FreeMarker手册中的this页面,了解使用地图的数据模型的一个很好的示例。
下一步是使用FreeMarker API组合模板+数据模型来创建类。以下是我为您的案例修改的FreeMarker手册中的example:
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手册非常有用,包含许多有用的示例。如果您对此方法感兴趣,请参阅Getting Started指南。
答案 4 :(得分:1)
Java中代码生成器的更好方法... ANTLR等工具如何,这是一个专门为实现具有代码生成支持的词法分析器/解析器而创建的现代工具。它有很好的文档,包括两本书:
即使不使用ANTLR,最后一个如果有用。