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