package net.sourceforge.pmd.eclipse.ui.views.dataflow; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.dfa.DataFlowNode; import net.sourceforge.pmd.lang.dfa.VariableAccess; import net.sourceforge.pmd.eclipse.plugin.PMDPlugin; import net.sourceforge.pmd.eclipse.ui.nls.StringKeys; import net.sourceforge.pmd.eclipse.util.IOUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; /** * Viewer for the DataFlowGraph, contains the DataflowGraphTable * * @author SebastianRaffel ( 30.05.2005 ) */ public class DataflowGraphViewer extends Composite { private Node method; private String resourceString; private DataflowGraphTable table; private DataflowGraph graph; protected static final int NODE_RADIUS = 12; protected static final int LINE_LENGTH = 25; protected static final int ROW_HEIGHT = 2 * NODE_RADIUS + LINE_LENGTH; protected int[] colWidths; protected Color bgColor; protected Color lineColor; protected Color textColor; /** * Constructor * * @param parent the parent Composite * @param style the SWT Style */ public DataflowGraphViewer(Composite parent, int style) { super(parent, style); setLayoutData(new GridData(GridData.FILL_BOTH)); table = initTable(this, style); GridLayout mainLayout = new GridLayout(1, false); mainLayout.marginHeight = mainLayout.marginWidth = 0; mainLayout.horizontalSpacing = mainLayout.verticalSpacing = 0; setLayout(mainLayout); } @Override public void addMouseListener(MouseListener mouseListener) { if (graph != null) { graph.addMouseListener(mouseListener); } } /** * Inits the Table. * * @param parent * @param style * @return the DataflowGraphTable */ private DataflowGraphTable initTable(Composite parent, int style) { DataflowGraphTable dfaTable = new DataflowGraphTable(parent, style); // set Column-widths and header titles colWidths = new int[] { 50, 250, 70, 220, 300 }; String[] headerTitles = { getString(StringKeys.VIEW_DATAFLOW_GRAPH_COLUMN_LINE), getString(StringKeys.VIEW_DATAFLOW_GRAPH_COLUMN_GRAPH), getString(StringKeys.VIEW_DATAFLOW_GRAPH_COLUMN_NEXT), getString(StringKeys.VIEW_DATAFLOW_GRAPH_COLUMN_VALUES), getString(StringKeys.VIEW_DATAFLOW_GRAPH_COLUMN_CODE) }; dfaTable.setColumns(colWidths, headerTitles, 1); Display display = parent.getDisplay(); // set the Colors bgColor = display.getSystemColor(SWT.COLOR_WHITE); //new Color(null, 255, 255, 255); lineColor = new Color(null, 192, 192, 192); textColor = display.getSystemColor(SWT.COLOR_BLACK); //new Color(null, 0, 0, 0); dfaTable.setColors(textColor, bgColor, lineColor); return dfaTable; } /** * Sets the data for this Viewer, gives the Table Data to show. * * @param node * @param resString the Node's Resource as String */ public void setData(Node node, String resString) { if (method != null) { table.dispose(); table = initTable(this, SWT.NONE); layout(); } method = node; resourceString = resString; // set the Data for the Table table.setRows(node.getDataFlowNode().getFlow().size(), ROW_HEIGHT); table.setTableData(createDataFields(node)); // create the Graph graph = new DataflowGraph(table.getGraphArea(), node, NODE_RADIUS, LINE_LENGTH, ROW_HEIGHT); } /** * @return the DataflowGraph */ public DataflowGraph getGraph() { return graph; } private String nextNodeNumberStringFrom(DataFlowNode dfNode) { List<DataFlowNode> dfNodes = dfNode.getChildren(); if (dfNodes.isEmpty()) return ""; StringBuilder sb = new StringBuilder( Integer.toString(dfNodes.get(0).getIndex()) ); for (int j = 1; j < dfNodes.size(); j++) { sb.append(", ").append(dfNodes.get(j).getIndex()); } return sb.toString(); } private String referenceStringFrom(DataFlowNode dfNode) { List<VariableAccess> access = dfNode.getVariableAccess(); if (access == null) return null; StringBuilder exp = new StringBuilder(); for (int k = 0; k < access.size(); k++) { if (k > 0) { exp.append(", "); } VariableAccess va = access.get(k); switch (va.getAccessType()) { case VariableAccess.DEFINITION: exp.append("d("); break; case VariableAccess.REFERENCING: exp.append("r("); break; case VariableAccess.UNDEFINITION: exp.append("u("); break; default: exp.append("?("); } exp.append(va.getVariableName()).append(')'); } return exp.toString(); } /** * Creates an List (#Rows) of List (#Columns) with TableData in it, provides the Input for the Table * * @param node * @return the DataflowGraphTable's Input-List */ protected List<List<DataflowGraphTableData>> createDataFields(Node node) { List<DataFlowNode> flow = node.getDataFlowNode().getFlow(); // the whole TableData List<List<DataflowGraphTableData>> tableData = new ArrayList<List<DataflowGraphTableData>>(); for (DataFlowNode inode : flow) { // one Data-List for a row List<DataflowGraphTableData> rowData = new ArrayList<DataflowGraphTableData>(); // 1. The Nodes Line rowData.add(new DataflowGraphTableData(String.valueOf(inode.getLine()), SWT.CENTER)); // 2. empty, because the Graph is shown in this column rowData.add(null); // 3. the Numbers of the next Nodes String cellContent = nextNodeNumberStringFrom(inode); rowData.add(new DataflowGraphTableData(cellContent, SWT.LEFT | SWT.WRAP)); // 4. The Dataflow occurrences (definition, undefinition, reference) in this Line of Code cellContent = referenceStringFrom(inode); if (cellContent != null) { rowData.add(new DataflowGraphTableData(cellContent, SWT.LEFT | SWT.WRAP)); } else { rowData.add(null); } // 5. The Line of Code itself if (resourceString != null) { cellContent = getCodeLine(resourceString, inode.getLine()).trim(); rowData.add(new DataflowGraphTableData(cellContent, SWT.LEFT | SWT.WRAP)); } else { rowData.add(null); } tableData.add(rowData); } return tableData; } /** * Simply returns the given Line from the String * * @param code, in general a Text representing a Java-File * @param line, the Line of Code to return * @return the Line of Code or null, if not found */ protected String getCodeLine(String code, int line) { LineNumberReader reader = null; try { reader = new LineNumberReader(new StringReader(code)); String retString; // read the Code (File) line-wise while (reader.ready()) { retString = reader.readLine(); // when the line is reached // return the read String if (reader.getLineNumber() == line) { return retString; } } } catch (IOException ioe) { PMDPlugin.getDefault().logError(StringKeys.ERROR_IO_EXCEPTION + this.toString(), ioe); } finally { IOUtil.closeQuietly(reader); } return null; } /** * Helper method to return an NLS string from its key */ private String getString(String key) { return PMDPlugin.getDefault().getStringTable().getString(key); } }