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