All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.util.GraphvizDotBuilder Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 *******************************************************************************/
package org.eclipse.xtext.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;

/**
 * @author Moritz Eysholdt - Initial contribution and API
 */
public abstract class GraphvizDotBuilder {

	protected static final Logger log = Logger.getLogger(GraphvizDotBuilder.class);

	protected class Digraph extends Props {

		private static final long serialVersionUID = -8103262357417982098L;

		private List edges = new ArrayList();
		private List nodes = new ArrayList();

		public Digraph() {
			super();
		}

		public void add(Props child) {
			if (child instanceof Node)
				nodes.add((Node) child);
			else if (child instanceof Edge)
				edges.add((Edge) child);
			else
				throw new RuntimeException("Unknown child type");
		}

		@Override
		public void draw(PrintStream out) {
			out.println("digraph G {");
			for (Node n : nodes)
				n.draw(out);
			for (Edge e : edges)
				e.draw(out);
			out.println("}");
			out.println();
		}
	}

	protected class Edge extends Props {
		private static final long serialVersionUID = 1941409538047058551L;
		private Object dst;
		private Object src;

		public Edge(Object src, Object dst) {
			this.src = src;
			this.dst = dst;
		}

		@Override
		public void draw(PrintStream out) {
			out.println(id(src) + "->" + id(dst) + " [" + this + "];");
		}

		public void setNoConstraint() {
			put("constraint", "false");
		}
	}

	protected class Node extends Props {
		private static final long serialVersionUID = 758381202465081606L;
		private Object obj;

		public Node(Object obj) {
			this.obj = obj;
		}

		public Node(Object obj, String label) {
			this(obj);
			setLabel(label);
		}

		public Node(Object obj, String label, String shape) {
			this(obj, label);
			setShape(shape);
		}

		@Override
		public void draw(PrintStream out) {
			out.println(id(obj) + " [" + this + "];");
		}

		// record, diamond, etc.
		public void setShape(String shape) {
			put("shape", shape);
		}

	}

	protected abstract class Props extends HashMap {
		private static final long serialVersionUID = 414470084198132521L;

		public abstract void draw(PrintStream out);

		protected String esc(String s) {
			if (noEsc.matcher(s).matches())
				return s;
			return "\"" + s.replaceAll("\"|\\[|\\]|>|<|&", "\\\\$0") + "\"";
		}

		public void print(PrintStream out) {
			for (Map.Entry e : entrySet())
				out.println(e.getKey() + "=" + esc(e.getValue()) + ";");
		}

		public void setLabel(String label) {
			put("label", label);
		}

		public void setStyle(String style) {
			put("style", style);
		}

		@Override
		public String toString() {
			if (size() == 0)
				return "";
			StringBuffer b = new StringBuffer();
			Iterator> i = entrySet().iterator();
			Map.Entry e = i.next();
			b.append(e.getKey() + "=" + esc(e.getValue()));
			while (i.hasNext()) {
				e = i.next();
				b.append("," + e.getKey() + "=" + esc(e.getValue()));
			}
			return b.toString();
		}

	}

	private static final Pattern noEsc = Pattern.compile("[a-zA-Z0-9]+");

	public String draw(Object obj) {
		ByteArrayOutputStream ba = new ByteArrayOutputStream();
		draw(obj, new PrintStream(ba));
		return ba.toString();
	}

	public void draw(Object obj, PrintStream out) {
		out.println("## This is a Graphviz .dot file " + "(http://www.graphviz.org/)");
		out.println("## You can use the command " + "'dot -Tpdf this.dot > out.pdf' to render it.");
		drawObject(obj).draw(out);
	}

	// example options: "-v -T png"
	public void draw(Object obj, String outfile, String options) throws IOException {
		String cmd = getGraphvizBinary() + " -o " + outfile + " " + options;
		draw(obj, cmd);
	}

	public void draw(Object obj, String cmd) throws IOException {
		log.info("Running '" + cmd + "' in '" + new File(".").getCanonicalPath() + "'");
		StringTokenizer st = new StringTokenizer(cmd);
		String[] cmdarray = new String[st.countTokens()];
		int i = 0;
		while (st.hasMoreTokens()) {
			cmdarray[i] = st.nextToken();
			i++;
		 }
		Process p = Runtime.getRuntime().exec(cmdarray);
		PrintStream ps = new PrintStream(p.getOutputStream());
		BufferedInputStream in = new BufferedInputStream(p.getInputStream());
		BufferedInputStream err = new BufferedInputStream(p.getErrorStream());
		draw(obj, ps);
		ps.close();
		int b;
		ByteArrayOutputStream oerr = new ByteArrayOutputStream();
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		while ((b = err.read()) >= 0)
			oerr.write(b);
		while ((b = in.read()) >= 0)
			out.write(b);
		if (oerr.size() > 0)
			log.info("Graphviz output to stderr: \n" + oerr.toString());
		if (out.size() > 0)
			log.info("Graphviz output to stdout: \n" + out.toString());
	}

	protected abstract Props drawObject(Object obj);

	protected String getGraphvizBinary() {
		return "/opt/local/bin/dot";
	}

	protected String id(Object cls) {
		if (cls == null)
			return null;
		// Math.abs(Integer.MIN_VALUE) returns Integer.MIN_VALUE
		long safeAbsInt = Math.abs(Long.valueOf(cls.hashCode()));
		if (cls instanceof EPackage)
			return "cluster" + safeAbsInt;
		if (cls instanceof EObject)
			return ((EObject) cls).eClass().getName().toLowerCase() + safeAbsInt;
		return cls.getClass().getSimpleName().toLowerCase() + safeAbsInt;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy