2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
7
* This program is distributed in the hope that it will be useful,
8
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
* GNU General Public License for more details.
12
* You should have received a copy of the GNU General Public License
13
* along with this program; if not, write to the Free Software
14
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
* Copyright (C) 2003 University of Waikato, Hamilton, New Zealand
22
package weka.gui.graphvisualizer;
24
import java.io.Reader;
25
import java.io.StreamTokenizer;
26
import java.io.BufferedReader;
27
import java.io.IOException;
28
import java.io.FileWriter;
30
import weka.core.FastVector;
31
import weka.gui.graphvisualizer.GraphNode;
32
import weka.gui.graphvisualizer.GraphEdge;
37
* This class parses input in DOT format, and
38
* builds the datastructures that are passed to it.
39
* It is NOT 100% compatible with the DOT format. The
40
* GraphNode and GraphEdge classes do not have any provision
41
* for dealing with different colours or shapes of nodes,
42
* there can however, be a different label and ID for a
43
* node. It also does not do anything for labels for
44
* edges. However, this class won't crash or throw an
45
* exception if it encounters any of the above
46
* attributes of an edge or a node. This class however,
47
* won't be able to deal with things like subgraphs and
50
* @author Ashraf M. Kibriya (amk14@cs.waikato.ac.nz)
51
* @version $Revision: 1.4 $ - 23 Apr 2003 - Initial version (Ashraf M. Kibriya)
53
public class DotParser implements GraphConstants {
55
/** These holds the nodes and edges of the graph */
56
protected FastVector m_nodes, m_edges;
57
/** This is the input containing DOT stream to be parsed */
58
protected Reader m_input;
59
/** This holds the name of the graph if there is any otherwise it is null */
60
protected String m_graphName;
64
* Dot parser Constructor
66
* @param input - The input, if passing in a string then
67
* encapsulate that in String reader object
68
* @param nodes - Vector to put in GraphNode objects,
69
* corresponding to the nodes parsed in from
71
* @param edges - Vector to put in GraphEdge objects,
72
* corresponding to the edges parsed in from
75
public DotParser(Reader input, FastVector nodes, FastVector edges) {
76
m_nodes = nodes; m_edges = edges;
82
* This method parses the string or the InputStream that we
83
* passed in through the constructor and builds up the
84
* m_nodes and m_edges vectors
86
* @return - returns the name of the graph
88
public String parse() {
89
StreamTokenizer tk = new StreamTokenizer(new BufferedReader(m_input));
99
* This method sets the syntax of the StreamTokenizer.
100
* i.e. set the whitespace, comment and delimit chars.
103
protected void setSyntax(StreamTokenizer tk) {
105
tk.eolIsSignificant(false);
106
tk.slashStarComments(true);
107
tk.slashSlashComments(true);
108
tk.whitespaceChars(0,' ');
109
tk.wordChars(' '+1,'\u00ff');
110
tk.ordinaryChar('[');
111
tk.ordinaryChar(']');
112
tk.ordinaryChar('{');
113
tk.ordinaryChar('}');
114
tk.ordinaryChar('-');
115
tk.ordinaryChar('>');
116
tk.ordinaryChar('/');
117
tk.ordinaryChar('*');
119
tk.whitespaceChars(';',';');
120
tk.ordinaryChar('=');
123
/*************************************************************
125
* Following methods parse the DOT input and mimic the DOT
126
* language's grammar in their structure
128
*************************************************************
130
protected void graph(StreamTokenizer tk) {
134
if(tk.ttype==tk.TT_WORD) {
135
if(tk.sval.equalsIgnoreCase("digraph")) {
137
if(tk.ttype==tk.TT_WORD) {
138
m_graphName = tk.sval;
142
while(tk.ttype!='{') {
143
System.err.println("Error at line "+tk.lineno()+" ignoring token "+
146
if(tk.ttype==tk.TT_EOF)
151
else if(tk.sval.equalsIgnoreCase("graph"))
152
System.err.println("Error. Undirected graphs cannot be used");
154
System.err.println("Error. Expected graph or digraph at line "+
158
catch(Exception ex) { ex.printStackTrace(); }
161
//int tmpMatrix[][] = new int[m_nodes.size()][m_nodes.size()];
162
//for(int i=0; i<m_edges.size(); i++)
163
// tmpMatrix[((GraphEdge)m_edges.elementAt(i)).src]
164
// [((GraphEdge)m_edges.elementAt(i)).dest] =
165
// ((GraphEdge)m_edges.elementAt(i)).type;
166
//for(int i=0; i<m_nodes.size(); i++) {
167
// GraphNode n = (GraphNode)m_nodes.elementAt(i);
168
// n.edges = tmpMatrix[i];
171
//Adding parents, and those edges to a node which are coming out from it
172
int tmpEdges[], noOfEdgesOfNode[]=new int[m_nodes.size()];
173
int noOfPrntsOfNode[]=new int[m_nodes.size()];
174
for(int i=0; i<m_edges.size(); i++) {
175
GraphEdge e = (GraphEdge)m_edges.elementAt(i);
176
noOfEdgesOfNode[e.src]++;
177
noOfPrntsOfNode[e.dest]++;
179
for(int i=0; i<m_edges.size(); i++) {
180
GraphEdge e = (GraphEdge)m_edges.elementAt(i);
181
GraphNode n = (GraphNode)m_nodes.elementAt(e.src);
182
GraphNode n2 = (GraphNode)m_nodes.elementAt(e.dest);
184
n.edges = new int[noOfEdgesOfNode[e.src]][2];
185
for(int k=0; k<n.edges.length; k++)
189
n2.prnts = new int[noOfPrntsOfNode[e.dest]];
190
for(int k=0; k<n2.prnts.length; k++)
194
while(n.edges[k][1]!=0) k++;
195
n.edges[k][0] = e.dest;
196
n.edges[k][1] = e.type;
199
while(n2.prnts[k]!=-1) k++;
205
protected void stmtList(StreamTokenizer tk) throws Exception{
207
if(tk.ttype=='}' || tk.ttype==tk.TT_EOF)
216
protected void stmt(StreamTokenizer tk) {
219
if(tk.sval.equalsIgnoreCase("graph") || tk.sval.equalsIgnoreCase("node") ||
220
tk.sval.equalsIgnoreCase("edge") )
225
int nodeindex= m_nodes.indexOf(new GraphNode(tk.sval, null));
229
nodeStmt(tk, nodeindex);
230
else if(tk.ttype == '-')
231
edgeStmt(tk, nodeindex);
233
System.err.println("error at lineno "+tk.lineno()+" in stmt");
235
catch(Exception ex) {
236
System.err.println("error at lineno "+tk.lineno()+" in stmtException");
237
ex.printStackTrace();
243
protected void nodeID(StreamTokenizer tk) throws Exception{
245
if(tk.ttype=='"' || tk.ttype==tk.TT_WORD || (tk.ttype>='a' && tk.ttype<='z')
246
|| (tk.ttype>='A' && tk.ttype<='Z')) {
247
if(m_nodes!=null && !(m_nodes.contains( new GraphNode(tk.sval, null))) ) {
248
m_nodes.addElement( new GraphNode(tk.sval, tk.sval) );
249
//System.out.println("Added node >"+tk.sval+"<");
253
{ throw new Exception(); }
259
protected void nodeStmt(StreamTokenizer tk, final int nindex)
263
GraphNode temp = (GraphNode) m_nodes.elementAt(nindex);
265
if(tk.ttype==']' || tk.ttype==tk.TT_EOF)
267
else if(tk.ttype==tk.TT_WORD) {
269
if(tk.sval.equalsIgnoreCase("label")) {
274
if(tk.ttype==tk.TT_WORD || tk.ttype=='"')
277
System.err.println("couldn't find label at line "+tk.lineno());
282
System.err.println("couldn't find label at line "+tk.lineno());
287
else if(tk.sval.equalsIgnoreCase("color")){
292
if(tk.ttype==tk.TT_WORD || tk.ttype=='"')
295
System.err.println("couldn't find color at line "+tk.lineno());
300
System.err.println("couldn't find color at line "+tk.lineno());
305
else if(tk.sval.equalsIgnoreCase("style")) {
310
if(tk.ttype==tk.TT_WORD || tk.ttype=='"')
313
System.err.println("couldn't find style at line "+tk.lineno());
318
System.err.println("couldn't find style at line "+tk.lineno());
323
nodeStmt(tk, nindex);
327
protected void edgeStmt(StreamTokenizer tk, final int nindex)
341
e = new GraphEdge(nindex,
342
m_nodes.indexOf( new GraphNode(tk.sval, null) ),
344
if( m_edges!=null && !(m_edges.contains(e)) ) {
345
m_edges.addElement( e );
346
//System.out.println("Added edge from "+
347
// ((GraphNode)(m_nodes.elementAt(nindex))).ID+
349
// ((GraphNode)(m_nodes.elementAt(e.dest))).ID);
356
e = new GraphEdge(nindex,
357
m_nodes.indexOf( new GraphNode(tk.sval, null) ),
359
if( m_edges!=null && !(m_edges.contains(e)) ) {
360
m_edges.addElement( e );
361
//System.out.println("Added edge from "+
362
// ((GraphNode)(m_nodes.elementAt(nindex))).ID+" to "+
363
// ((GraphNode)(m_nodes.elementAt(e.dest))).ID);
367
else if(tk.ttype=='-') {
368
System.err.println("Error at line "+tk.lineno()+
369
". Cannot deal with undirected edges");
370
if(tk.ttype==tk.TT_WORD)
375
System.err.println("Error at line "+tk.lineno()+" in edgeStmt");
376
if(tk.ttype==tk.TT_WORD)
390
protected void edgeAttrib(StreamTokenizer tk, final GraphEdge e)
394
if(tk.ttype==']' || tk.ttype==tk.TT_EOF)
396
else if(tk.ttype==tk.TT_WORD) {
398
if(tk.sval.equalsIgnoreCase("label")) {
403
if(tk.ttype==tk.TT_WORD || tk.ttype=='"')
404
System.err.println("found label "+tk.sval);//e.lbl = tk.sval;
406
System.err.println("couldn't find label at line "+tk.lineno());
411
System.err.println("couldn't find label at line "+tk.lineno());
415
else if(tk.sval.equalsIgnoreCase("color")) {
420
if(tk.ttype==tk.TT_WORD || tk.ttype=='"')
423
System.err.println("couldn't find color at line "+tk.lineno());
428
System.err.println("couldn't find color at line "+tk.lineno());
433
else if(tk.sval.equalsIgnoreCase("style")) {
438
if(tk.ttype==tk.TT_WORD || tk.ttype=='"')
441
System.err.println("couldn't find style at line "+tk.lineno());
446
System.err.println("couldn't find style at line "+tk.lineno());
457
* This method saves a graph in a file in DOT format.
458
* However, if reloaded in GraphVisualizer we would need
459
* to layout the graph again.
461
* @param filename - The name of the file to write in. (will overwrite)
462
* @param graphName - The name of the graph
463
* @param nodes - Vector containing all the nodes
464
* @param edges - Vector containing all the edges
466
public static void writeDOT(String filename, String graphName,
467
FastVector nodes, FastVector edges) {
469
FileWriter os = new FileWriter(filename);
470
os.write("digraph ", 0, ("digraph ").length());
472
os.write(graphName+" ", 0, graphName.length()+1);
473
os.write("{\n", 0, ("{\n").length());
476
for(int i=0; i<edges.size(); i++) {
477
e = (GraphEdge) edges.elementAt(i);
478
os.write(((GraphNode)nodes.elementAt(e.src)).ID, 0,
479
((GraphNode)nodes.elementAt(e.src)).ID.length());
480
os.write("->", 0, ("->").length() );
481
os.write(((GraphNode)nodes.elementAt(e.dest)).ID+"\n",
483
((GraphNode)nodes.elementAt(e.dest)).ID.length()+1);
485
os.write("}\n", 0, ("}\n").length());
488
catch(IOException ex) { ex.printStackTrace(); }