View Javadoc

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