/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Tree_build.java * Copyright (C) 1999 University of Waikato, Hamilton, New Zealand * */ package weka.gui.treevisualizer; import java.util.*; import java.io.*; import java.awt.*; /** * This class will parse a dotty file and construct a tree structure from it * with Edge's and Node's * * @author Malcolm Ware (mfw4@cs.waikato.ac.nz) * @version $Revision: 1.4 $ */ public class TreeBuild { //this class will parse the tree into relevant strings //into info objects //from there it will create the nodes and edges from the info objects /** The name of the tree, Not in use. */ private String m_graphName; /** An array with all the nodes initially constructed into it. */ private Vector m_aNodes; /** An array with all the edges initially constructed into it. */ private Vector m_aEdges; /** An array containing a structure that describes the node without * actually creating it. */ private Vector m_nodes; /** An arry containing a structure that describes the edge without * actually creating it. */ private Vector m_edges; /** An object setup to take graph data. */ private InfoObject m_grObj; /** An object setup to take node data. */ private InfoObject m_noObj; /** An object setup to take edge data. */ private InfoObject m_edObj; /** true if it is a digraph. (note that this can't build digraphs). */ private boolean m_digraph; /** The stream to parse. */ private StreamTokenizer m_st; /** A table containing all the colors. */ private Hashtable m_colorTable; /** * Upon construction this will only setup the color table for quick * reference of a color. */ public TreeBuild() { m_colorTable = new Hashtable(); Colors ab = new Colors(); for (int noa = 0;noa < ab.m_cols.length;noa++) { m_colorTable.put(ab.m_cols[noa].m_name,ab.m_cols[noa].m_col); } } /** * This will build A node structure from the dotty format passed. Don't * send a dotty format with multiple parents * per node, and ensure that there is 1 and only 1 node with no parent. * * @param t The reader with the dotty string to be read. * @return The top node of the tree structure (the last node with no parent). */ public Node create(Reader t) { m_nodes = new Vector(50,50); m_edges = new Vector(50,50); m_grObj = new InfoObject("graph"); m_noObj = new InfoObject("node"); m_edObj = new InfoObject("edge"); m_digraph = false; m_st = new StreamTokenizer(new BufferedReader(t)); setSyntax(); graph(); Node top = generateStructures(); return top; } /** * This will go through all the found Nodes and Edges build instances of * these * and link them together. * * @return The node with no parent (the top of the tree). */ private Node generateStructures() { String id,label,source,target; Integer shape,style; int fontsize; Color fontcolor,color; InfoObject t; m_aNodes = new Vector(50,50); m_aEdges = new Vector(50,50); for (int noa = 0;noa < m_nodes.size();noa++) { t = (InfoObject)m_nodes.elementAt(noa); id = t.m_id; if (t.m_label == null) { if (m_noObj.m_label == null) { label = ""; } else { label = m_noObj.m_label; } } else { label = t.m_label; } if (t.m_shape == null) { if (m_noObj.m_shape == null) { shape = new Integer(2); } else { shape = getShape(m_noObj.m_shape); } } else { shape = getShape(t.m_shape); } if (shape == null) { shape = new Integer(2); } if (t.m_style == null) { if (m_noObj.m_style == null) { style = new Integer(1); } else { style = getStyle(m_noObj.m_style); } } else { style = getStyle(t.m_style); } if (style == null) { style = new Integer(1); } if (t.m_fontSize == null) { if (m_noObj.m_fontSize == null) { fontsize = 12; } else { fontsize = Integer.valueOf(m_noObj.m_fontSize).intValue(); } } else { fontsize = Integer.valueOf(t.m_fontSize).intValue(); } if (t.m_fontColor == null) { if (m_noObj.m_fontColor == null) { fontcolor = Color.black; } else { fontcolor = (Color)m_colorTable .get(m_noObj.m_fontColor.toLowerCase()); } } else { fontcolor = (Color)m_colorTable.get(t.m_fontColor.toLowerCase()); } if (fontcolor == null) { fontcolor = Color.black; } if (t.m_color == null) { if (m_noObj.m_color == null) { color = Color.gray; } else { color = (Color)m_colorTable.get(m_noObj.m_color.toLowerCase()); } } else { color = (Color)m_colorTable.get(t.m_color.toLowerCase()); } if (color == null) { color = Color.gray; } m_aNodes.addElement(new Node(label,id,style.intValue(),shape.intValue(), color,t.m_data)); } for (int noa = 0;noa < m_edges.size();noa++) { t = (InfoObject)m_edges.elementAt(noa); id = t.m_id; if (t.m_label == null) { if (m_noObj.m_label == null) { label = ""; } else { label = m_noObj.m_label; } } else { label = t.m_label; } if (t.m_shape == null) { if (m_noObj.m_shape == null) { shape = new Integer(2); } else { shape = getShape(m_noObj.m_shape); } } else { shape = getShape(t.m_shape); } if (shape == null) { shape = new Integer(2); } if (t.m_style == null) { if (m_noObj.m_style == null) { style = new Integer(1); } else { style = getStyle(m_noObj.m_style); } } else { style = getStyle(t.m_style); } if (style == null) { style = new Integer(1); } if (t.m_fontSize == null) { if (m_noObj.m_fontSize == null) { fontsize = 12; } else { fontsize = Integer.valueOf(m_noObj.m_fontSize).intValue(); } } else { fontsize = Integer.valueOf(t.m_fontSize).intValue(); } if (t.m_fontColor == null) { if (m_noObj.m_fontColor == null) { fontcolor = Color.black; } else { fontcolor = (Color)m_colorTable .get(m_noObj.m_fontColor.toLowerCase()); } } else { fontcolor = (Color)m_colorTable.get(t.m_fontColor.toLowerCase()); } if (fontcolor == null) { fontcolor = Color.black; } if (t.m_color == null) { if (m_noObj.m_color == null) { color = Color.white; } else { color = (Color)m_colorTable.get(m_noObj.m_color.toLowerCase()); } } else { color = (Color)m_colorTable.get(t.m_color.toLowerCase()); } if (color == null) { color = Color.white; } m_aEdges.addElement(new Edge(label,t.m_source,t.m_target)); } boolean f_set,s_set; Node x,sour = null,targ = null; Edge y; for (int noa = 0;noa < m_aEdges.size();noa++) { f_set = false; s_set = false; y = (Edge)m_aEdges.elementAt(noa); for (int nob = 0;nob < m_aNodes.size();nob++) { x = (Node)m_aNodes.elementAt(nob); if (x.getRefer().equals(y.getRtarget())) { f_set = true; targ = x; } if (x.getRefer().equals(y.getRsource())) { s_set = true; sour = x; } if (f_set == true && s_set == true) { break; } } if (targ != sour) { y.setTarget(targ); y.setSource(sour); } else { System.out.println("logic error"); } } for (int noa = 0;noa < m_aNodes.size();noa++) { if (((Node)m_aNodes.elementAt(noa)).getParent(0) == null) { sour = (Node)m_aNodes.elementAt(noa); } } return sour; } /** * This will convert the shape string to an int representing that shape. * * @param sh The name of the shape. * @return An Integer representing the shape. */ private Integer getShape(String sh) { if (sh.equalsIgnoreCase("box") || sh.equalsIgnoreCase("rectangle")) { return new Integer(1); } else if (sh.equalsIgnoreCase("oval")) { return new Integer(2); } else if (sh.equalsIgnoreCase("diamond")) { return new Integer(3); } else { return null; } } /** * Converts the string representing the fill style int oa number * representing it. * * @param sty The name of the style. * @return An Integer representing the shape. */ private Integer getStyle(String sty) { if (sty.equalsIgnoreCase("filled")) { return new Integer(1); } else { return null; } } /** * This will setup the syntax for the tokenizer so that it parses the * string properly. * */ private void setSyntax() { m_st.resetSyntax(); m_st.eolIsSignificant(false); m_st.slashStarComments(true); m_st.slashSlashComments(true); //System.out.println("slash"); m_st.whitespaceChars(0,' '); m_st.wordChars(' '+1,'\u00ff'); m_st.ordinaryChar('['); m_st.ordinaryChar(']'); m_st.ordinaryChar('{'); m_st.ordinaryChar('}'); m_st.ordinaryChar('-'); m_st.ordinaryChar('>'); m_st.ordinaryChar('/'); m_st.ordinaryChar('*'); m_st.quoteChar('"'); m_st.whitespaceChars(';',';'); m_st.ordinaryChar('='); } /** * This is the alternative syntax for the tokenizer. */ private void alterSyntax() { m_st.resetSyntax(); m_st.wordChars('\u0000', '\u00ff'); m_st.slashStarComments(false); m_st.slashSlashComments(false); m_st.ordinaryChar('\n'); m_st.ordinaryChar('\r'); } /** * This will parse the next token out of the stream and check for certain * conditions. * * @param r The error string to print out if something goes wrong. */ private void nextToken(String r) { int t = 0; try { t = m_st.nextToken(); } catch(IOException e) { } if (t == m_st.TT_EOF) { System.out.println("eof , " + r); } else if (t == m_st.TT_NUMBER) { System.out.println("got a number , " + r); } } /** * Parses the top of the dotty stream that has the graph information. * */ private void graph() { boolean flag = true; String s; //expect digraph int t; nextToken("expected 'digraph'"); if (m_st.sval.equalsIgnoreCase("digraph")) { m_digraph = true; } else { System.out.println("expected 'digraph'"); } nextToken("expected a Graph Name"); if (m_st.sval != null) { m_graphName = m_st.sval; } else { System.out.println("expected a Graph Name"); } nextToken("expected '{'"); if (m_st.ttype == '{') { stmtList(); } else { System.out.println("expected '{'"); } } /** * This is one of the states, this one is where new items can be defined * or the structure can end. * */ private void stmtList() { boolean flag = true; String s; int t; while(flag) { nextToken("expects a STMT_LIST item or '}'"); if (m_st.ttype == '}') { flag = false; } else if (m_st.sval.equalsIgnoreCase("graph") || m_st.sval.equalsIgnoreCase("node") || m_st.sval.equalsIgnoreCase("edge")) { m_st.pushBack(); attrStmt(); } else if (m_st.sval != null) { nodeId(m_st.sval,0); } else { System.out.println("expects a STMT_LIST item or '}'"); } } } /** * This will deal specifically with a new object such as graph , node , edge. * */ private void attrStmt() { nextToken("expected 'graph' or 'node' or 'edge'"); if (m_st.sval.equalsIgnoreCase("graph")) { nextToken("expected a '['"); if (m_st.ttype == '[') { attrList(m_grObj); } else { System.out.println("expected a '['"); } } else if (m_st.sval.equalsIgnoreCase("node")) { nextToken("expected a '['"); if (m_st.ttype == '[') { attrList(m_noObj); } else { System.out.println("expected a '['"); } } else if (m_st.sval.equalsIgnoreCase("edge")) { nextToken("expected a '['"); if (m_st.ttype == '[') { attrList(m_edObj); } else { System.out.println("expected a '['"); } } else { System.out.println("expected 'graph' or 'node' or 'edge'"); } } /** * Generates a new InfoObject with the specified name and either does * further processing if applicable * Otherwise it is an edge and will deal with that. * * @param s The ID string. * @param t Not sure!. */ private void nodeId(String s,int t) { nextToken("error occurred in node_id"); if (m_st.ttype == '}') { //creates a node if t is zero if (t == 0) { m_nodes.addElement(new InfoObject(s)); } m_st.pushBack(); } else if (m_st.ttype == '-') { nextToken("error occurred checking for an edge"); if (m_st.ttype == '>') { edgeStmt(s); } else { System.out.println("error occurred checking for an edge"); } } else if (m_st.ttype == '[') { //creates a node if t is zero and sends it to attr if (t == 0) { m_nodes.addElement(new InfoObject(s)); attrList((InfoObject)m_nodes.lastElement()); } else { attrList((InfoObject)m_edges.lastElement()); } } else if (m_st.sval != null) { //creates a node if t is zero if (t == 0) { m_nodes.addElement(new InfoObject(s)); } m_st.pushBack(); } else { System.out.println("error occurred in node_id"); } } /** * This will get the target of the edge. * * @param i The source of the edge. */ private void edgeStmt(String i) { nextToken("error getting target of edge"); if (m_st.sval != null) { m_edges.addElement(new InfoObject("an edge ,no id")); ((InfoObject)m_edges.lastElement()).m_source = i; ((InfoObject)m_edges.lastElement()).m_target = m_st.sval; nodeId(m_st.sval,1); } else { System.out.println("error getting target of edge"); } } /** * This will parse all the items in the attrib list for an object. * * @param a The object that the attribs apply to. */ private void attrList(InfoObject a) { boolean flag = true; while (flag) { nextToken("error in attr_list"); //System.out.println(st.sval); if (m_st.ttype == ']') { flag = false; } else if (m_st.sval.equalsIgnoreCase("color")) { nextToken("error getting color"); if (m_st.ttype == '=') { nextToken("error getting color"); if (m_st.sval != null) { a.m_color = m_st.sval; } else { System.out.println("error getting color"); } } else { System.out.println("error getting color"); } } else if (m_st.sval.equalsIgnoreCase("fontcolor")) { nextToken("error getting font color"); if (m_st.ttype == '=') { nextToken("error getting font color"); if (m_st.sval != null) { a.m_fontColor = m_st.sval; } else { System.out.println("error getting font color"); } } else { System.out.println("error getting font color"); } } else if (m_st.sval.equalsIgnoreCase("fontsize")) { nextToken("error getting font size"); if (m_st.ttype == '=') { nextToken("error getting font size"); if (m_st.sval != null) { a.m_fontSize = m_st.sval; } else { System.out.println("error getting font size"); } } else { System.out.println("error getting font size"); } } else if (m_st.sval.equalsIgnoreCase("label")) { nextToken("error getting label"); if (m_st.ttype == '=') { nextToken("error getting label"); if (m_st.sval != null) { a.m_label = m_st.sval; } else { System.out.println("error getting label"); } } else { System.out.println("error getting label"); } } else if (m_st.sval.equalsIgnoreCase("shape")) { nextToken("error getting shape"); if (m_st.ttype == '=') { nextToken("error getting shape"); if (m_st.sval != null) { a.m_shape = m_st.sval; } else { System.out.println("error getting shape"); } } else { System.out.println("error getting shape"); } } else if (m_st.sval.equalsIgnoreCase("style")) { nextToken("error getting style"); if (m_st.ttype == '=') { nextToken("error getting style"); if (m_st.sval != null) { a.m_style = m_st.sval; } else { System.out.println("error getting style"); } } else { System.out.println("error getting style"); } } else if (m_st.sval.equalsIgnoreCase("data")) { nextToken("error getting data"); if (m_st.ttype == '=') { //data has a special data string that can have anything //this is delimited by a single comma on an otherwise empty line alterSyntax(); a.m_data = new String(""); while (true) { nextToken("error getting data"); if (m_st.sval != null && a.m_data != null && m_st.sval.equals(",")) { break; } else if (m_st.sval != null) { a.m_data = a.m_data.concat(m_st.sval); } else if (m_st.ttype == '\r') { a.m_data = a.m_data.concat("\r"); } else if (m_st.ttype == '\n') { a.m_data = a.m_data.concat("\n"); } else { System.out.println("error getting data"); } } setSyntax(); } else { System.out.println("error getting data"); } } } } //special class for use in creating the tree /** * This is an internal class used to keep track of the info for the objects * before they are * actually created. */ private class InfoObject { /** The ID string for th object. */ public String m_id; /** The color name for the object. */ public String m_color; /** The font color for the object. not in use. */ public String m_fontColor; /** The fontsize for the object. not in use. */ public String m_fontSize; /** The label for the object. */ public String m_label; /** The shape name of for the object. */ public String m_shape; /** The backstyle name for the object. */ public String m_style; /** The source ID of the object. */ public String m_source; /** The target ID of the object. */ public String m_target; /** The data for this object. */ public String m_data; /** * This will construct a new InfoObject with the specified ID string. */ public InfoObject(String i) { m_id = i; m_color = null; m_fontColor = null; m_fontSize = null; m_label = null; m_shape = null; m_style = null; m_source = null; m_target = null; m_data = null; } } }