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