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 }