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         public ElementWrapper(ASTMethodDeclaration node) {
139             this.node = node;
140         }
141         public ASTMethodDeclaration getNode() {
142             return node;
143         }
144         public String toString() {
145             return node.getMethodName();
146         }
147     }
148 
149     private DFACanvas dfaCanvas;
150     private JList nodeList;
151     private DefaultListModel nodes = new DefaultListModel();
152     private JPanel wrapperPanel;
153 
154     public DFAPanel() {
155         super();
156 
157         setLayout(new BorderLayout());
158         JPanel leftPanel = new JPanel();
159         nodeList = new JList(nodes);
160         nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
161         nodeList.setFixedCellWidth(150);
162         nodeList.setBorder(BorderFactory.createLineBorder(Color.black));
163         nodeList.addListSelectionListener(this);
164         leftPanel.add(nodeList);
165         add(leftPanel, BorderLayout.WEST);
166 
167         dfaCanvas = new DFACanvas();
168         JScrollPane scrollPane = new JScrollPane();
169         scrollPane.setPreferredSize(new Dimension(800, 450));  // eo - it would be better to calculate the size based on the containing object's size
170         dfaCanvas.setSize(2000,4000);  // eo - these seem to give a big enough canvas
171         scrollPane.add(dfaCanvas);
172         wrapperPanel = new JPanel();
173         wrapperPanel.add(scrollPane);
174         wrapperPanel.setBorder(BorderFactory.createLineBorder(Color.black));
175         add(wrapperPanel, BorderLayout.EAST);
176     }
177 
178     public void valueChanged(ListSelectionEvent event) {
179         ElementWrapper wrapper = null;
180         if (nodes.size() == 1) {
181             wrapper = (ElementWrapper) nodes.get(0);
182         } else if (nodes.isEmpty()) {
183             return;
184         } else if (nodeList.getSelectedValue() == null) {
185             wrapper = (ElementWrapper) nodes.get(0);
186         } else {
187             wrapper = (ElementWrapper) nodeList.getSelectedValue();
188         }
189         dfaCanvas.setMethod(wrapper.getNode());
190         dfaCanvas.repaint();
191     }
192 
193     public void resetTo(List newNodes, HasLines lines) {
194         dfaCanvas.setCode(lines);
195         nodes.clear();
196         for (Iterator i = newNodes.iterator(); i.hasNext();) {
197             nodes.addElement(new ElementWrapper((ASTMethodDeclaration) i.next()));
198         }
199         nodeList.setSelectedIndex(0);
200         dfaCanvas.setMethod((SimpleNode) newNodes.get(0));
201         repaint();
202     }
203 }
204