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