View Javadoc

    1   package net.sourceforge.pmd.util.designer;
    2
    3   import java.awt.BorderLayout;
    4   import java.awt.Color;
    5   import java.awt.Dimension;
    6   import java.awt.FontMetrics;
    7   import java.awt.Graphics;
    8   import java.util.List;
    9
    10  import javax.swing.BorderFactory;
    11  import javax.swing.DefaultListModel;
    12  import javax.swing.JComponent;
    13  import javax.swing.JList;
    14  import javax.swing.JPanel;
    15  import javax.swing.JScrollPane;
    16  import javax.swing.ListSelectionModel;
    17  import javax.swing.SwingConstants;
    18  import javax.swing.SwingUtilities;
    19  import javax.swing.event.ListSelectionEvent;
    20  import javax.swing.event.ListSelectionListener;
    21
    22  import net.sourceforge.pmd.lang.ast.Node;
    23  import net.sourceforge.pmd.lang.dfa.DataFlowNode;
    24  import net.sourceforge.pmd.lang.dfa.VariableAccess;
    25  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
    26  import net.sourceforge.pmd.util.StringUtil;
    27
    28  public class DFAPanel extends JComponent implements ListSelectionListener {
    29
    30      public static class DFACanvas extends JPanel {
    31
    32  	private static final int NODE_RADIUS = 12;
    33  	private static final int NODE_DIAMETER = 2 * NODE_RADIUS;
    34
    35  	private Node node;
    36
    37  	private int x = 150;
    38  	private int y = 50;
    39  	private LineGetter lines;
    40
    41  	private void addAccessLabel(StringBuffer sb, VariableAccess va) {
    42
    43  	    if (va.isDefinition()) {
    44  		sb.append("d(");
    45  	    } else if (va.isReference()) {
    46  		sb.append("r(");
    47  	    } else if (va.isUndefinition()) {
    48  		sb.append("u(");
    49  		//continue;  // eo - the u() entries add a lot of clutter to the report
    50  	    } else {
    51  		sb.append("?(");
    52  	    }
    53
    54  	    sb.append(va.getVariableName()).append(')');
    55  	}
    56
    57  	private String childIndicesOf(DataFlowNode node, String separator) {
    58
    59  	    List<DataFlowNode> kids = node.getChildren();
    60  	    if (kids.isEmpty()) {
    61  		return "";
    62  	    }
    63
    64  	    StringBuffer sb = new StringBuffer();
    65  	    sb.append(kids.get(0).getIndex());
    66
    67  	    for (int j = 1; j < node.getChildren().size(); j++) {
    68  		sb.append(separator);
    69  		sb.append(kids.get(j).getIndex());
    70  	    }
    71  	    return sb.toString();
    72  	}
    73
    74  	private String[] deriveAccessLabels(List<DataFlowNode> flow) {
    75
    76  	    if (flow == null || flow.isEmpty()) {
    77  		return StringUtil.EMPTY_STRINGS;
    78  	    }
    79
    80  	    String[] labels = new String[flow.size()];
    81
    82  	    for (int i = 0; i < labels.length; i++) {
    83  		List<VariableAccess> access = flow.get(i).getVariableAccess();
    84
    85  		if (access == null || access.isEmpty()) {
    86  		    continue; // leave a null at this slot
    87  		}
    88
    89  		StringBuffer exp = new StringBuffer();
    90  		addAccessLabel(exp, access.get(0));
    91
    92  		for (int k = 1; k < access.size(); k++) {
    93  		    exp.append(", ");
    94  		    addAccessLabel(exp, access.get(k));
    95  		}
    96
    97  		labels[i] = exp.toString();
    98  	    }
    99  	    return labels;
    100 	}
    101
    102 	private int maxWidthOf(String[] strings, FontMetrics fm) {
    103
    104 	    int max = 0;
    105 	    String str;
    106
    107 	    for (String element : strings) {
    108 		str = element;
    109 		if (str == null) {
    110 		    continue;
    111 		}
    112 		max = Math.max(max, SwingUtilities.computeStringWidth(fm, str));
    113 	    }
    114 	    return max;
    115 	}
    116
    117 	@Override
    118 	public void paintComponent(Graphics g) {
    119 	    super.paintComponent(g);
    120
    121 	    if (node == null) {
    122 		return;
    123 	    }
    124
    125 	    List<DataFlowNode> flow = node.getDataFlowNode().getFlow();
    126 	    FontMetrics fm = g.getFontMetrics();
    127 	    int halfFontHeight = fm.getAscent() / 2;
    128
    129 	    String[] accessLabels = deriveAccessLabels(flow);
    130 	    int maxAccessLabelWidth = maxWidthOf(accessLabels, fm);
    131
    132 	    for (int i = 0; i < flow.size(); i++) {
    133 		DataFlowNode inode = flow.get(i);
    134
    135 		y = computeDrawPos(inode.getIndex());
    136
    137 		g.drawArc(x, y, NODE_DIAMETER, NODE_DIAMETER, 0, 360);
    138 		g.drawString(lines.getLine(inode.getLine()), x + 100 + maxAccessLabelWidth, y + 15);
    139
    140 		// draw index number centered inside of node
    141 		String idx = String.valueOf(inode.getIndex());
    142 		int halfWidth = SwingUtilities.computeStringWidth(fm, idx) / 2;
    143 		g.drawString(idx, x + NODE_RADIUS - halfWidth, y + NODE_RADIUS + halfFontHeight);
    144
    145 		String accessLabel = accessLabels[i];
    146 		if (accessLabel != null) {
    147 		    g.drawString(accessLabel, x + 70, y + 15);
    148 		}
    149
    150 		for (int j = 0; j < inode.getChildren().size(); j++) {
    151 		    DataFlowNode n = inode.getChildren().get(j);
    152 		    drawMyLine(inode.getIndex(), n.getIndex(), g);
    153 		}
    154 		String childIndices = childIndicesOf(inode, ", ");
    155 		g.drawString(childIndices, x - 3 * NODE_DIAMETER, y + NODE_RADIUS - 2);
    156 	    }
    157 	}
    158
    159 	public void setCode(LineGetter h) {
    160 	    this.lines = h;
    161 	}
    162
    163 	public void setMethod(Node node) {
    164 	    this.node = node;
    165 	}
    166
    167 	private int computeDrawPos(int index) {
    168 	    int z = NODE_RADIUS * 4;
    169 	    return z + index * z;
    170 	}
    171
    172 	private void drawArrow(Graphics g, int x, int y, int direction) {
    173
    174 	    final int height = NODE_RADIUS * 2 / 3;
    175 	    final int width = NODE_RADIUS * 2 / 3;
    176
    177 	    switch (direction) {
    178 	    case SwingConstants.NORTH:
    179 		g.drawLine(x, y, x - width / 2, y + height);
    180 		g.drawLine(x, y, x + width / 2, y + height);
    181 		break;
    182 	    case SwingConstants.SOUTH:
    183 		g.drawLine(x, y, x - width / 2, y - height);
    184 		g.drawLine(x, y, x + width / 2, y - height);
    185 		break;
    186 	    case SwingConstants.EAST:
    187 		g.drawLine(x, y, x - height, y - width / 2);
    188 		g.drawLine(x, y, x - height, y + width / 2);
    189 		break;
    190 	    case SwingConstants.WEST:
    191 		g.drawLine(x, y, x + height, y - width / 2);
    192 		g.drawLine(x, y, x + height, y + width / 2);
    193 		break;
    194 	    default:
    195 		// Do nothing
    196 		break;
    197 	    }
    198 	}
    199
    200 	private void drawMyLine(int index1, int index2, Graphics g) {
    201 	    int y1 = this.computeDrawPos(index1);
    202 	    int y2 = this.computeDrawPos(index2);
    203
    204 	    //int arrow = 6;
    205
    206 	    if (index1 < index2) {
    207 		if (index2 - index1 == 1) {
    208 		    x += NODE_RADIUS;
    209 		    g.drawLine(x, y1 + NODE_DIAMETER, x, y2);
    210 		    //  g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
    211 		    drawArrow(g, x, y2, SwingConstants.SOUTH);
    212 		    x -= NODE_RADIUS;
    213 		} else if (index2 - index1 > 1) {
    214 		    y1 = y1 + NODE_RADIUS;
    215 		    y2 = y2 + NODE_RADIUS;
    216 		    int n = (index2 - index1 - 2) * 10 + 10;
    217 		    g.drawLine(x, y1, x - n, y1);
    218 		    g.drawLine(x - n, y1, x - n, y2);
    219 		    g.drawLine(x - n, y2, x, y2);
    220 		    //   g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
    221 		    drawArrow(g, x, y2, SwingConstants.EAST);
    222 		}
    223
    224 	    } else {
    225 		if (index1 - index2 > 1) {
    226 		    y1 = y1 + NODE_RADIUS;
    227 		    y2 = y2 + NODE_RADIUS;
    228 		    x = x + NODE_DIAMETER;
    229 		    int n = (index1 - index2 - 2) * 10 + 10;
    230 		    g.drawLine(x, y1, x + n, y1);
    231 		    g.drawLine(x + n, y1, x + n, y2);
    232 		    g.drawLine(x + n, y2, x, y2);
    233 		    //      g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
    234 		    drawArrow(g, x, y2, SwingConstants.WEST);
    235 		    x = x - NODE_DIAMETER;
    236 		} else if (index1 - index2 == 1) {
    237 		    y2 = y2 + NODE_DIAMETER;
    238 		    g.drawLine(x + NODE_RADIUS, y2, x + NODE_RADIUS, y1);
    239 		    //   g.fillRect(x + NODE_RADIUS - arrow, y2 - arrow, arrow * 2, arrow * 2);
    240 		    drawArrow(g, x + NODE_RADIUS, y2, SwingConstants.NORTH);
    241 		}
    242 	    }
    243 	}
    244     }
    245
    246     private static class ElementWrapper {
    247 	private ASTMethodDeclaration node;
    248
    249 	public ElementWrapper(ASTMethodDeclaration node) {
    250 	    this.node = node;
    251 	}
    252
    253 	public ASTMethodDeclaration getNode() {
    254 	    return node;
    255 	}
    256
    257 	@Override
    258 	public String toString() {
    259 	    return node.getMethodName();
    260 	}
    261     }
    262
    263     private DFACanvas dfaCanvas;
    264     private JList nodeList;
    265     private DefaultListModel nodes = new DefaultListModel();
    266
    267     public DFAPanel() {
    268 	super();
    269
    270 	setLayout(new BorderLayout());
    271 	JPanel leftPanel = new JPanel();
    272
    273 	nodeList = new JList(nodes);
    274 	nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    275 	nodeList.setFixedCellWidth(150);
    276 	nodeList.setBorder(BorderFactory.createLineBorder(Color.black));
    277 	nodeList.addListSelectionListener(this);
    278
    279 	leftPanel.add(nodeList);
    280 	add(leftPanel, BorderLayout.WEST);
    281
    282 	dfaCanvas = new DFACanvas();
    283 	dfaCanvas.setBackground(Color.WHITE);
    284 	dfaCanvas.setPreferredSize(new Dimension(900, 1400));
    285
    286 	JScrollPane scrollPane = new JScrollPane(dfaCanvas);
    287
    288 	add(scrollPane, BorderLayout.CENTER);
    289     }
    290
    291     public void valueChanged(ListSelectionEvent event) {
    292 	ElementWrapper wrapper = null;
    293 	if (nodes.size() == 1) {
    294 	    wrapper = (ElementWrapper) nodes.get(0);
    295 	} else if (nodes.isEmpty()) {
    296 	    return;
    297 	} else if (nodeList.getSelectedValue() == null) {
    298 	    wrapper = (ElementWrapper) nodes.get(0);
    299 	} else {
    300 	    wrapper = (ElementWrapper) nodeList.getSelectedValue();
    301 	}
    302 	dfaCanvas.setMethod(wrapper.getNode());
    303 	dfaCanvas.repaint();
    304     }
    305
    306     public void resetTo(List<ASTMethodDeclaration> newNodes, LineGetter lines) {
    307 	dfaCanvas.setCode(lines);
    308 	nodes.clear();
    309 	for (ASTMethodDeclaration md : newNodes) {
    310 	    nodes.addElement(new ElementWrapper(md));
    311 	}
    312 	nodeList.setSelectedIndex(0);
    313 	dfaCanvas.setMethod(newNodes.get(0));
    314 	repaint();
    315     }
    316 }