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.HasLines;
    8
    9   import javax.swing.*;
    10  import javax.swing.event.ListSelectionEvent;
    11  import javax.swing.event.ListSelectionListener;
    12  import java.awt.BorderLayout;
    13  import java.awt.Canvas;
    14  import java.awt.Color;
    15  import java.awt.Dimension;
    16  import java.awt.Graphics;
    17  import java.util.Iterator;
    18  import java.util.List;
    19
    20  public class DFAPanel extends JComponent implements ListSelectionListener {
    21
    22      public static class DFACanvas extends Canvas {
    23
    24          private static final int NODE_RADIUS = 10;
    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 HasLines lines;
    32
    33          public void paint(Graphics g) {
    34              super.paint(g);
    35              if (node == null) {
    36                  return;
    37              }
    38              List flow = node.getDataFlowNode().getFlow();
    39              for (int i = 0; i < flow.size(); i++) {
    40                  IDataFlowNode inode = (IDataFlowNode) flow.get(i);
    41
    42                  y = computeDrawPos(inode.getIndex());
    43
    44                  g.drawArc(x, y, NODE_DIAMETER, NODE_DIAMETER, 0, 360);
    45                  g.drawString(lines.getLine(inode.getLine()), x + 200, y + 15);
    46
    47                  // draw index number centered inside of node
    48                  String idx = String.valueOf(inode.getIndex());
    49                  int hack = 4 * (idx.length() / 2); // eo - hack to get one and two digit numbers centered
    50                  g.drawString(idx, x + NODE_RADIUS - 2 - hack, y + NODE_RADIUS + 4);
    51
    52                  List access = inode.getVariableAccess();
    53                  if (access != null) {
    54                      StringBuffer exp = new StringBuffer();
    55                      for (int k = 0; k < access.size(); k++) {
    56                          VariableAccess va = (VariableAccess) access.get(k);
    57                          if (va.isDefinition()) {
    58                              exp.append("d(");
    59                          } else if (va.isReference()) {
    60                              exp.append("r(");
    61                          } else if (va.isUndefinition()) {
    62                              exp.append("u(");
    63                              //continue;  // eo - the u() entries add a lot of clutter to the report
    64                          } else {
    65                              exp.append("?(");
    66                          }
    67                          exp.append(va.getVariableName() + "), ");
    68                      }
    69                      g.drawString(exp.toString(), x + 70, y + 15);
    70                  }
    71
    72                  for (int j = 0; j < inode.getChildren().size(); j++) {
    73                      IDataFlowNode n = (IDataFlowNode) inode.getChildren().get(j);
    74                      this.drawMyLine(inode.getIndex(), n.getIndex(), g);
    75                      String output = (j == 0 ? "" : ",") + String.valueOf(n.getIndex());
    76                      g.drawString(output, x - 3 * NODE_DIAMETER + (j * 20), y + NODE_RADIUS - 2);
    77                  }
    78              }
    79          }
    80
    81          public void setCode(HasLines h) {
    82              this.lines = h;
    83          }
    84
    85          public void setMethod(SimpleNode node) {
    86              this.node = node;
    87          }
    88
    89          private int computeDrawPos(int index) {
    90              int z = NODE_RADIUS * 4;
    91              return z + index * z;
    92          }
    93
    94          private void drawMyLine(int index1, int index2, Graphics g) {
    95              int y1 = this.computeDrawPos(index1);
    96              int y2 = this.computeDrawPos(index2);
    97
    98              int arrow = 3;
    99
    100             if (index1 < index2) {
    101                 if (index2 - index1 == 1) {
    102                     x += NODE_RADIUS;
    103                     g.drawLine(x, y1 + NODE_DIAMETER, x, y2);
    104                     g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
    105                     x -= NODE_RADIUS;
    106                 } else if (index2 - index1 > 1) {
    107                     y1 = y1 + NODE_RADIUS;
    108                     y2 = y2 + NODE_RADIUS;
    109                     int n = ((index2 - index1 - 2) * 10) + 10;
    110                     g.drawLine(x, y1, x - n, y1);
    111                     g.drawLine(x - n, y1, x - n, y2);
    112                     g.drawLine(x - n, y2, x, y2);
    113                     g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
    114                 }
    115
    116             } else {
    117                 if (index1 - index2 > 1) {
    118                     y1 = y1 + NODE_RADIUS;
    119                     y2 = y2 + NODE_RADIUS;
    120                     x = x + NODE_DIAMETER;
    121                     int n = ((index1 - index2 - 2) * 10) + 10;
    122                     g.drawLine(x, y1, x + n, y1);
    123                     g.drawLine(x + n, y1, x + n, y2);
    124                     g.drawLine(x + n, y2, x, y2);
    125                     g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
    126                     x = x - NODE_DIAMETER;
    127                 } else if (index1 - index2 == 1) {
    128                     y2 = y2 + NODE_DIAMETER;
    129                     g.drawLine(x + NODE_RADIUS, y2, x + NODE_RADIUS, y1);
    130                     g.fillRect(x + NODE_RADIUS - arrow, y2 - arrow, arrow * 2, arrow * 2);
    131                 }
    132             }
    133         }
    134     }
    135
    136     private static class ElementWrapper {
    137         private ASTMethodDeclaration node;
    138
    139         public ElementWrapper(ASTMethodDeclaration node) {
    140             this.node = node;
    141         }
    142
    143         public ASTMethodDeclaration getNode() {
    144             return node;
    145         }
    146
    147         public String toString() {
    148             return node.getMethodName();
    149         }
    150     }
    151
    152     private DFACanvas dfaCanvas;
    153     private JList nodeList;
    154     private DefaultListModel nodes = new DefaultListModel();
    155     private JPanel wrapperPanel;
    156
    157     public DFAPanel() {
    158         super();
    159
    160         setLayout(new BorderLayout());
    161         JPanel leftPanel = new JPanel();
    162         nodeList = new JList(nodes);
    163         nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    164         nodeList.setFixedCellWidth(150);
    165         nodeList.setBorder(BorderFactory.createLineBorder(Color.black));
    166         nodeList.addListSelectionListener(this);
    167         leftPanel.add(nodeList);
    168         add(leftPanel, BorderLayout.WEST);
    169
    170         dfaCanvas = new DFACanvas();
    171         JScrollPane scrollPane = new JScrollPane();
    172         scrollPane.setPreferredSize(new Dimension(800, 450));  // eo - it would be better to calculate the size based on the containing object's size
    173         dfaCanvas.setSize(2000, 4000);  // eo - these seem to give a big enough canvas
    174         scrollPane.add(dfaCanvas);
    175         wrapperPanel = new JPanel();
    176         wrapperPanel.add(scrollPane);
    177         wrapperPanel.setBorder(BorderFactory.createLineBorder(Color.black));
    178         add(wrapperPanel, BorderLayout.EAST);
    179     }
    180
    181     public void valueChanged(ListSelectionEvent event) {
    182         ElementWrapper wrapper = null;
    183         if (nodes.size() == 1) {
    184             wrapper = (ElementWrapper) nodes.get(0);
    185         } else if (nodes.isEmpty()) {
    186             return;
    187         } else if (nodeList.getSelectedValue() == null) {
    188             wrapper = (ElementWrapper) nodes.get(0);
    189         } else {
    190             wrapper = (ElementWrapper) nodeList.getSelectedValue();
    191         }
    192         dfaCanvas.setMethod(wrapper.getNode());
    193         dfaCanvas.repaint();
    194     }
    195
    196     public void resetTo(List newNodes, HasLines lines) {
    197         dfaCanvas.setCode(lines);
    198         nodes.clear();
    199         for (Iterator i = newNodes.iterator(); i.hasNext();) {
    200             nodes.addElement(new ElementWrapper((ASTMethodDeclaration) i.next()));
    201         }
    202         nodeList.setSelectedIndex(0);
    203         dfaCanvas.setMethod((SimpleNode) newNodes.get(0));
    204         repaint();
    205     }
    206 }
    207