001: package net.sourceforge.pmd.util.designer;
002:
003: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
004: import net.sourceforge.pmd.ast.SimpleNode;
005: import net.sourceforge.pmd.dfa.IDataFlowNode;
006: import net.sourceforge.pmd.dfa.variableaccess.VariableAccess;
007: import net.sourceforge.pmd.util.LineGetter;
008: import net.sourceforge.pmd.util.StringUtil;
009:
010: import javax.swing.*;
011: import javax.swing.event.ListSelectionEvent;
012: import javax.swing.event.ListSelectionListener;
013: import java.awt.BorderLayout;
014: import java.awt.Color;
015: import java.awt.Dimension;
016: import java.awt.FontMetrics;
017: import java.awt.Graphics;
018: import java.util.List;
019:
020: public class DFAPanel extends JComponent implements
021: ListSelectionListener {
022:
023: public static class DFACanvas extends JPanel {
024:
025: private static final int NODE_RADIUS = 12;
026: private static final int NODE_DIAMETER = 2 * NODE_RADIUS;
027:
028: private SimpleNode node;
029:
030: private int x = 150;
031: private int y = 50;
032: private LineGetter lines;
033:
034: private void addAccessLabel(StringBuffer sb, VariableAccess va) {
035:
036: if (va.isDefinition()) {
037: sb.append("d(");
038: } else if (va.isReference()) {
039: sb.append("r(");
040: } else if (va.isUndefinition()) {
041: sb.append("u(");
042: //continue; // eo - the u() entries add a lot of clutter to the report
043: } else {
044: sb.append("?(");
045: }
046:
047: sb.append(va.getVariableName()).append(')');
048: }
049:
050: private String childIndicesOf(IDataFlowNode node,
051: String separator) {
052:
053: List kids = node.getChildren();
054: if (kids.isEmpty())
055: return "";
056:
057: StringBuffer sb = new StringBuffer();
058: sb.append(((IDataFlowNode) kids.get(0)).getIndex());
059:
060: for (int j = 1; j < node.getChildren().size(); j++) {
061: sb.append(separator);
062: sb.append(((IDataFlowNode) kids.get(j)).getIndex());
063: }
064: return sb.toString();
065: }
066:
067: private String[] deriveAccessLabels(List flow) {
068:
069: if (flow == null || flow.isEmpty())
070: return StringUtil.EMPTY_STRINGS;
071:
072: String[] labels = new String[flow.size()];
073:
074: for (int i = 0; i < labels.length; i++) {
075: List access = ((IDataFlowNode) flow.get(i))
076: .getVariableAccess();
077:
078: if (access == null || access.isEmpty()) {
079: continue; // leave a null at this slot
080: }
081:
082: StringBuffer exp = new StringBuffer();
083: addAccessLabel(exp, (VariableAccess) access.get(0));
084:
085: for (int k = 1; k < access.size(); k++) {
086: exp.append(", ");
087: addAccessLabel(exp, (VariableAccess) access.get(k));
088: }
089:
090: labels[i] = exp.toString();
091: }
092: return labels;
093: }
094:
095: private int maxWidthOf(String[] strings, FontMetrics fm) {
096:
097: int max = 0;
098: String str;
099:
100: for (int i = 0; i < strings.length; i++) {
101: str = strings[i];
102: if (str == null)
103: continue;
104: max = Math.max(max, SwingUtilities.computeStringWidth(
105: fm, str));
106: }
107: return max;
108: }
109:
110: public void paintComponent(Graphics g) {
111: super .paintComponent(g);
112:
113: if (node == null)
114: return;
115:
116: List flow = node.getDataFlowNode().getFlow();
117: FontMetrics fm = g.getFontMetrics();
118: int halfFontHeight = fm.getAscent() / 2;
119:
120: String[] accessLabels = deriveAccessLabels(flow);
121: int maxAccessLabelWidth = maxWidthOf(accessLabels, fm);
122:
123: for (int i = 0; i < flow.size(); i++) {
124: IDataFlowNode inode = (IDataFlowNode) flow.get(i);
125:
126: y = computeDrawPos(inode.getIndex());
127:
128: g.drawArc(x, y, NODE_DIAMETER, NODE_DIAMETER, 0, 360);
129: g.drawString(lines.getLine(inode.getLine()), x + 100
130: + maxAccessLabelWidth, y + 15);
131:
132: // draw index number centered inside of node
133: String idx = String.valueOf(inode.getIndex());
134: int halfWidth = SwingUtilities.computeStringWidth(fm,
135: idx) / 2;
136: g.drawString(idx, x + NODE_RADIUS - halfWidth, y
137: + NODE_RADIUS + halfFontHeight);
138:
139: String accessLabel = accessLabels[i];
140: if (accessLabel != null) {
141: g.drawString(accessLabel, x + 70, y + 15);
142: }
143:
144: for (int j = 0; j < inode.getChildren().size(); j++) {
145: IDataFlowNode n = inode.getChildren().get(j);
146: drawMyLine(inode.getIndex(), n.getIndex(), g);
147: }
148: String childIndices = childIndicesOf(inode, ", ");
149: g.drawString(childIndices, x - 3 * NODE_DIAMETER, y
150: + NODE_RADIUS - 2);
151: }
152: }
153:
154: public void setCode(LineGetter h) {
155: this .lines = h;
156: }
157:
158: public void setMethod(SimpleNode node) {
159: this .node = node;
160: }
161:
162: private int computeDrawPos(int index) {
163: int z = NODE_RADIUS * 4;
164: return z + index * z;
165: }
166:
167: private void drawArrow(Graphics g, int x, int y, int direction) {
168:
169: final int height = NODE_RADIUS * 2 / 3;
170: final int width = NODE_RADIUS * 2 / 3;
171:
172: switch (direction) {
173: case SwingConstants.NORTH:
174: g.drawLine(x, y, x - width / 2, y + height);
175: g.drawLine(x, y, x + width / 2, y + height);
176: break;
177: case SwingConstants.SOUTH:
178: g.drawLine(x, y, x - width / 2, y - height);
179: g.drawLine(x, y, x + width / 2, y - height);
180: break;
181: case SwingConstants.EAST:
182: g.drawLine(x, y, x - height, y - width / 2);
183: g.drawLine(x, y, x - height, y + width / 2);
184: break;
185: case SwingConstants.WEST:
186: g.drawLine(x, y, x + height, y - width / 2);
187: g.drawLine(x, y, x + height, y + width / 2);
188: }
189: }
190:
191: private void drawMyLine(int index1, int index2, Graphics g) {
192: int y1 = this .computeDrawPos(index1);
193: int y2 = this .computeDrawPos(index2);
194:
195: //int arrow = 6;
196:
197: if (index1 < index2) {
198: if (index2 - index1 == 1) {
199: x += NODE_RADIUS;
200: g.drawLine(x, y1 + NODE_DIAMETER, x, y2);
201: // g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
202: drawArrow(g, x, y2, SwingConstants.SOUTH);
203: x -= NODE_RADIUS;
204: } else if (index2 - index1 > 1) {
205: y1 = y1 + NODE_RADIUS;
206: y2 = y2 + NODE_RADIUS;
207: int n = ((index2 - index1 - 2) * 10) + 10;
208: g.drawLine(x, y1, x - n, y1);
209: g.drawLine(x - n, y1, x - n, y2);
210: g.drawLine(x - n, y2, x, y2);
211: // g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
212: drawArrow(g, x, y2, SwingConstants.EAST);
213: }
214:
215: } else {
216: if (index1 - index2 > 1) {
217: y1 = y1 + NODE_RADIUS;
218: y2 = y2 + NODE_RADIUS;
219: x = x + NODE_DIAMETER;
220: int n = ((index1 - index2 - 2) * 10) + 10;
221: g.drawLine(x, y1, x + n, y1);
222: g.drawLine(x + n, y1, x + n, y2);
223: g.drawLine(x + n, y2, x, y2);
224: // g.fillRect(x - arrow, y2 - arrow, arrow * 2, arrow * 2);
225: drawArrow(g, x, y2, SwingConstants.WEST);
226: x = x - NODE_DIAMETER;
227: } else if (index1 - index2 == 1) {
228: y2 = y2 + NODE_DIAMETER;
229: g
230: .drawLine(x + NODE_RADIUS, y2, x
231: + NODE_RADIUS, y1);
232: // g.fillRect(x + NODE_RADIUS - arrow, y2 - arrow, arrow * 2, arrow * 2);
233: drawArrow(g, x + NODE_RADIUS, y2,
234: SwingConstants.NORTH);
235: }
236: }
237: }
238: }
239:
240: private static class ElementWrapper {
241: private ASTMethodDeclaration node;
242:
243: public ElementWrapper(ASTMethodDeclaration node) {
244: this .node = node;
245: }
246:
247: public ASTMethodDeclaration getNode() {
248: return node;
249: }
250:
251: public String toString() {
252: return node.getMethodName();
253: }
254: }
255:
256: private DFACanvas dfaCanvas;
257: private JList nodeList;
258: private DefaultListModel nodes = new DefaultListModel();
259:
260: public DFAPanel() {
261: super ();
262:
263: setLayout(new BorderLayout());
264: JPanel leftPanel = new JPanel();
265:
266: nodeList = new JList(nodes);
267: nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
268: nodeList.setFixedCellWidth(150);
269: nodeList.setBorder(BorderFactory.createLineBorder(Color.black));
270: nodeList.addListSelectionListener(this );
271:
272: leftPanel.add(nodeList);
273: add(leftPanel, BorderLayout.WEST);
274:
275: dfaCanvas = new DFACanvas();
276: dfaCanvas.setBackground(Color.WHITE);
277: dfaCanvas.setPreferredSize(new Dimension(900, 1400));
278:
279: JScrollPane scrollPane = new JScrollPane(dfaCanvas);
280:
281: add(scrollPane, BorderLayout.CENTER);
282: }
283:
284: public void valueChanged(ListSelectionEvent event) {
285: ElementWrapper wrapper = null;
286: if (nodes.size() == 1) {
287: wrapper = (ElementWrapper) nodes.get(0);
288: } else if (nodes.isEmpty()) {
289: return;
290: } else if (nodeList.getSelectedValue() == null) {
291: wrapper = (ElementWrapper) nodes.get(0);
292: } else {
293: wrapper = (ElementWrapper) nodeList.getSelectedValue();
294: }
295: dfaCanvas.setMethod(wrapper.getNode());
296: dfaCanvas.repaint();
297: }
298:
299: public void resetTo(List<ASTMethodDeclaration> newNodes,
300: LineGetter lines) {
301: dfaCanvas.setCode(lines);
302: nodes.clear();
303: for (ASTMethodDeclaration md : newNodes) {
304: nodes.addElement(new ElementWrapper(md));
305: }
306: nodeList.setSelectedIndex(0);
307: dfaCanvas.setMethod(newNodes.get(0));
308: repaint();
309: }
310: }
|