谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

シーケンス図をJGraph X化してみた。

JGraphで描いていたシーケンス図を、JGraph Xで描き直してみた。
何よりもまず、できあがるシーケンスがかっこよくなったし、
ノードを動かした時のパフォーマンスも幾分か向上したみたい。


JGraphを使ってた時に感じていた、「これ、不要だろ」っていうAPI呼び出し
(例えばgraph.getGraphLayoutCache().insertとか)を使わなくて良くなったり、
ノードを作る時にnewを呼ばなくて良くなったりしたので、コードがすっきりした。


ただ、スタイルの扱いがちょっとイケてなくなった(APIがよく分からない)のが残念。
あと、長すぎるノードの文字列を自動的に省略をする方法も良く分からない。
この辺りはただの勉強不足かも知れないので、もうちょっと調べてみるけど。


そういえば、やっぱりGenericsとかは使えず、
むしろノードやエッジをjava.lang.Objectとして扱うことが逆に増えたんだけど
この辺りは「Swingでグラフを描きたい」っていう要望があるような現場は
そもそもJava1.4だろっていう事を考えれば、妥当なのかも。


とりあえず、ソースを貼っとく。

package cerot.blight.sequence;

import java.awt.Color;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cerot.blight.sequence.entity.Connector;
import cerot.blight.sequence.entity.ConnectorType;
import cerot.blight.sequence.entity.EndPoint;
import cerot.blight.sequence.entity.Node;

import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;

public class SequenceCreator {
	/** 呼び出し線のスタイル */
	private static final String STYLE_CALL = mxConstants.STYLE_ENDARROW + "="
			+ mxConstants.ARROW_OPEN + ";" + mxConstants.STYLE_VERTICAL_ALIGN
			+ "=" + mxConstants.ALIGN_BOTTOM + ";";

	/** 戻り線のスタイル */
	private static final String STYLE_RETURN = mxConstants.STYLE_DASHED + "=1;"
			+ mxConstants.STYLE_ENDARROW + "=" + mxConstants.ARROW_OPEN + ";"
			+ mxConstants.STYLE_VERTICAL_ALIGN + "=" + mxConstants.ALIGN_BOTTOM
			+ ";";

	/** 生存線のスタイル */
	private static final String STYLE_LIFELINE = mxConstants.STYLE_ENDARROW
			+ "=" + mxConstants.NONE;

	/** ノードの幅 */
	private int nodeWidth = 80;

	/** ノードの高さ */
	private int nodeHeight = 20;

	/** ノードの間隔 */
	private int nodeInterval = 10;

	/** 結線の間隔 */
	private int edgeInterval = 25;

	private mxGraph graph;

	/** ノードセルのマップ */
	private Map<Node, Object> nodeMap = new HashMap<Node, Object>();

	/** 結線の数 */
	private int edgeCount;

	/**
	 * コンストラクタ。モデルの変換を行います。
	 * @param connectorList 結線のリスト
	 */
	public SequenceCreator(List<Connector> connectorList) {
		graph = new mxGraph();

		for (Connector connector : connectorList) {
			createConnectorCell(connector);
		}

		// 生存線の作成
		Object parent = this.graph.getDefaultParent();
		for (Object nodeCell : this.nodeMap.values()) {
			Object endCell = createEndPointCell(nodeCell);
			graph.insertEdge(parent, null, null, nodeCell, endCell,
					STYLE_LIFELINE);
		}
	}

	/**
	 * グラフの描画を行います。
	 */
	public mxGraphComponent createGraphComponent() {
		mxGraphComponent component = new mxGraphComponent(this.graph);
		component.getViewport().setBackground(Color.WHITE);
		return component;
	}

	private void createConnectorCell(Connector connector) {
		EndPoint from = connector.getFrom();
		EndPoint to = connector.getTo();

		Object fromNodeCell = findParentNodeCell(from);
		Object fromCell = createEndPointCell(fromNodeCell);

		Object toNodeCell = findParentNodeCell(to);
		Object toCell = createEndPointCell(toNodeCell);

		Object parent = this.graph.getDefaultParent();

		if (connector.getConnectorType() == ConnectorType.METHOD_RETURN) {
			graph.insertEdge(parent, null, connector.getName(), fromCell,
					toCell, STYLE_RETURN);
		} else {
			graph.insertEdge(parent, null, connector.getName(), fromCell,
					toCell, STYLE_CALL);
		}

		this.edgeCount++;
	}

	private Object findParentNodeCell(EndPoint endPoint) {
		Node node = endPoint.getParentNode();
		Object result = findNodeCell(node);

		return result;
	}

	private Object createEndPointCell(Object nodeCell) {
		// X座標 = ノードの中央値
		mxRectangle rectangle = this.graph.getCellBounds(nodeCell);
		double x = rectangle.getCenterX();

		// Y座標 = 上端からの余白 + ノードの高さ + (結線の数 + 1) * 結線の間隔
		int y = this.nodeInterval + this.nodeHeight + (this.edgeCount + 1)
				* edgeInterval;

		Object parent = this.graph.getDefaultParent();
		Object result = this.graph.insertVertex(parent, null, null, x, y, 0, 0);

		return result;
	}

	private Object findNodeCell(Node node) {
		Object result = this.nodeMap.get(node);

		if (result == null) {
			Object parent = this.graph.getDefaultParent();

			// X座標 = 左端からの余白 + ノード数 * (ノード幅 + ノード間隔)
			int x = this.nodeInterval + this.nodeMap.size()
					* (this.nodeWidth + this.nodeInterval);

			// Y座標 = 上端からの余白
			int y = this.nodeInterval;

			result = this.graph.insertVertex(parent, null, node.getName(), x,
					y, nodeWidth, nodeHeight);

			this.nodeMap.put(node, result);
		}

		return result;
	}
}


興味がある人は、以前のエントリで書いたJGraphのソースと比べてみると良いかも。
JGraphでシーケンス図を描いてみた。 - 谷本 心 in せろ部屋


SourceForgeの方にもコミットしといたので、Diffで見たほうが見やすいかも。
http://svn.sourceforge.jp/view/b-light/trunk/B-Light/src/main/java/cerot/blight/sequence/SequenceCreator.java?root=b-light&r1=2&r2=9