Лучший способ генератора кода на Java?
У меня есть класс с диаграммой внутри. Я повторяю график и создаю строку, которая строит график, а затем просто записываю эту строку в файл Java. Есть ли лучший способ сделать это, я читал о JDT и CodeModel, но мне действительно нужен намек на то, как с ним привыкнуть.
редактировать
Я делаю генератор кода регулярного выражения, до сих пор я преобразовал регулярное выражение в DFA, представленное в directedgraph (используя grail библиотека.) Когда у меня есть DFA, следующий шаг-создать класс, который имеет три метода, 1-й строит тот же граф (DFA), 2-й метод перемещается с одного узла на другой, а третий метод соответствует, если входная строка принята одна. Только первый метод изменяется в зависимости от ввода regularexpression, два других являются статическими и одинаковыми для каждого сгенерированного класса 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);
}
}
5 ответов
другое решение было бы придерживаться текущей технологии, но обеспечить небольшой слой с шаблона. Для реализации построителя вам нужно небольшое одноразовое усилие, но получить гораздо лучше читаемый код.
я реализовал первую часть кода. С правильным строителем вы могли бы написать:
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), см. например сделать строителя.
очень простой пример приведен в следующем блоге:
http://namanmehta.blogspot.in/2010/01/use-codemodel-to-generate-java-source.html
возможно, вы захотите взглянуть на него.
проблема с jcodemodel заключается в том, что он внутренне используется популярными генераторами кода, такими как JAX-B, и не хорошо документирован. Также нет никаких учебников. Но если вы хотите использовать эту библиотеку, вы можете посмотреть на разных блогах, где пользователи задокументировали свой опыт / описание проблемы и разрешение .
удачи
все еще немного нечеткой на вопрос, но вот несколько советов:
- создайте базовый класс, включающий статические функции, и расширьте его сгенерированные классы. Таким образом, вам не нужно переписывать статические функции.
- вам действительно нужен один класс на график? Обычно у вас будет один класс, который принимает граф в качестве параметра конструктора и просто имеет разные экземпляры объектов одного класса
- вы можете сериализовать направленный граф? Если это так, то это лучший способ сохранить и оживить его.
я использовал менее известный продукт под названием FreeMarker для нескольких проектов, которые требовали генерации кода (например, классы кодирования/декодирования для сообщений). Это решение на основе Java, в котором вы создаете модель памяти и передаете ее шаблону. С их домашней страницы:
FreeMarker-это "механизм шаблонов"; универсальный инструмент для генерации текста выход (что-нибудь из HTML в автогенерируемые исходный код) на основе шаблоны. Это пакет Java, библиотека классов для Java-программистов. Это не приложение для конечных пользователей, но то, что программисты могут внедрять в свои продукты.
чтобы использовать FreeMarker, создайте модель данных и шаблон для создания кода для класса, который вы пытаетесь построить. Это решение имеет дополнительные затраты на обучение, но должно быть простым в освоении и невероятно полезно для будущих требований к генерации кода и других проектов в будущем.
Update: вот шаблон для класс, указанный в вопросе (Примечание: я его не тестировал):
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>
}
}
ваша модель данных должна быть довольно простой-карта, содержащая один ключ, значение которого является списком узлов. Если позже вы обнаружите, что ваш шаблон нуждается в дополнительной информации, вы можете изменить модель данных в любое время. Любой объект 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 для отличный пример для моделей данных с использованием карт.
следующим шагом будет использование API FreeMarker для объединения модели template + data для создания класса. Вот это пример из руководства 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 очень полезно и содержит много полезных примеров. Вижу Начало Работы руководство, если вы заинтересованы в этом подходе.
лучший способ генератора кода на Java... Как насчет таких инструментов, как ANTLR, который является современным инструментом, созданным специально для реализации лексеров/синтаксических анализаторов с поддержкой генерации кода. Он имеет большую документацию, в том числе две книги:
последний, если он полезен, даже если не использует ANTLR.