2012-04-23 5 views
9

그래프가있는 클래스가 있습니다. 그래프를 반복하고 그래프를 만드는 문자열을 만든 다음 해당 문자열을 Java 파일에 씁니다. 더 좋은 방법이 있나요, 나는 JDT와 CodeModel에 대해 읽었지만, 실제로 어떻게 사용하는지에 대한 힌트가 필요합니다.Java에서 코드 생성기의 더 좋은 방법은 무엇입니까?

편집

나는 정규 표현식 코드 생성기를하고있는 중이 야 지금까지 나는 DFA로 정규 표현식은 (성배 라이브러리를 사용하여) directedgraph 표현 변환했다. DFA를 사용할 때 다음 단계는 세 가지 방법이있는 클래스를 생성하는 것입니다. 첫 번째 방법은 동일한 그래프 (DFA)를 작성하고 두 번째 방법은 한 노드에서 다른 노드로 이동하며 세 번째 방법은 입력 문자열이 받아 들여지면 일치시킵니다. 첫 번째 메소드 만이 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); 
} 
} 
+0

더 자세한 정보가 필요합니다. '그래프 반복'의 의미는 무엇입니까? – dfb

+0

이 그래프 유형에 대해 이야기하고 있습니까? http://en.wikipedia.org/wiki/Graph_%28mathematics%29 – eabraham

+0

수정 된 기사 읽기[email protected] 예 그것은 지시 된 것을 제외하고는 그러한 그래프입니다 – sm13294

답변

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

훨씬 더 읽기 쉽지 않습니까? 이것을 얻으려면 두 명의 건축업자가 필요합니다.

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보다 : 먼저 GraphBuilder 온다

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을 참조하십시오.

+0

나는 유망 해 보인다. 고맙습니다. 나는 한 번 시험해보고 나중에 알려 줄 것이다. – sm13294

+0

고맙습니다. 그래프 객체가 내가 전에 알아 채지 못했던 ctor에서 생성되었다는 것을 당신의 코드에 더 가깝게 보았습니다. 생성물을 GraphBuilder.build()로 옮기면 인스턴스 var/function 매개 변수는 필요 없지만 반환 값은 필요하지 않습니다. 당신은'graph = new GraphBuilder() ..'로 사용할 것입니다. – ChrLipp

+0

DSL은 setLabel, setDescriptionProperty를 setDescription, 노드를 createNode로 이름을 바꾸면 더 읽기 쉽습니다. – ChrLipp

1

여기에 약간의 질문에 퍼지 만 : 같은

내 문자열 기반의 접근 방식은 보이는

  • 정적 기능을 포함하는 기본 클래스를 생성하고 확인하여 생성 된 클래스가이를 확장합니다. 그렇게하면 정적 함수를 다시 작성하지 않아도됩니다.
  • 정말 그래프 당 하나의 클래스가 필요합니까? 일반적으로 생성자에 대한 매개 변수로 그래프를 가져 와서 같은 클래스의 다른 객체 인스턴스를 갖는 클래스가 하나 있습니다.
  • serialize 직접 그래프입니까? 그렇다면 그것을 저장하고 되살리는 더 좋은 방법입니다.
+0

예, 좋은 생각입니다. 그러나 실제로 CodeModel과 같은 코드 생성기 라이브러리를 사용하여 어떻게 할 것인지에 대한 힌트를 찾고 있습니다. – sm13294

+0

@ sm13294, 나는 ASM이 잘 문서화되어 있고 전투 테스트를 마쳤으므로 CodeModel 대신 [ASM] (http://forge.ow2.org/projects/asm)을 사용할 것을 제안합니다. 나는 여전히 같은 일반적인 패턴을 따를 것이다. 세 가지 함수를 모두 갖고 기본 그래프 개체 인 전용 멤버가있는 기본 클래스를 만듭니다. Directed Graph를 개인 정적 클래스 멤버로 사용하고 파생 클래스를 생성자를 통해 부모 클래스에 공급합니다. –

+0

약간의 ASM을 사용했지만 Java 코드를 생성 할 수 있다는 것을 알지 못했습니다. 다음 코드를 사용하여 코드를 생성하는 방법에 대한 간단한 예제를 제공해 줄 수 있습니까? $ Thanks in advance – sm13294

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

귀하의 데이터 모델은 매우 간단해야한다 - 값의지도가 하나의 키를 포함하는이 노드 목록입니다. 나중에 템플릿에 추가 정보가 필요하면 언제든지 데이터 모델을 변경할 수 있습니다. 모든 Java 객체는 필수 필드가 public이거나 public getter가있는 한 데이터 모델 내에서 작동해야합니다.

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

지도를 사용하여 데이터 모델에 대한 좋은 예를 들어 프리 마커 설명서에 this 페이지를 참조하십시오.

다음 단계는 FreeMarker API를 사용하여 템플릿 + 데이터 모델을 결합하여 클래스를 만드는 것입니다. 여기에 내가 당신의 사건에 대한 수정 한 프리 마커 설명서에서 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(); 
    } 
} 

프리 마커 설명서는 매우 유용하고 많은 유용한 예제가 포함되어 있습니다. 이 방법에 관심이 있으시면 Getting Started 가이드를 참조하십시오.

+0

간단한 예제를 제공 할 수 있습니까? – sm13294

1

Java에서 코드 생성기의 더 좋은 방법은 ... ANTLR과 같은 도구는 코드 생성을 지원하는 렉서/파서를 구현하기 위해 특별히 만든 최신 도구입니다. 이 두 권의 책을 포함하여, 좋은 문서가 있습니다

ANTLR을 사용하지 않는 경우에도 유용한 경우 마지막 하나.

2

아주 간단한 예는 다음 블로그에 기재되어 있습니다 :

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

당신은 그것을 좀보고 할 수 있습니다.

jcodemodel의 문제점은 JAX-B와 같이 많이 사용되는 코드 생성기에서 내부적으로 사용되며 잘 문서화되어 있지 않다는 점입니다. 또한 자습서가 없습니다. 그러나이 라이브러리를 사용하려는 경우 사용자가 경험/문제 설명과 해결 방법을 문서화 한 다른 블로그를 살펴볼 수 있습니다. 행운

관련 문제