|
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 |
| } |