1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.util.designer;
5
6 import net.sourceforge.pmd.PMD;
7 import net.sourceforge.pmd.RuleContext;
8 import net.sourceforge.pmd.RuleSet;
9 import net.sourceforge.pmd.TargetJDK1_4;
10 import net.sourceforge.pmd.TargetJDK1_5;
11 import net.sourceforge.pmd.TargetJDKVersion;
12 import net.sourceforge.pmd.ast.ASTCompilationUnit;
13 import net.sourceforge.pmd.ast.JavaParser;
14 import net.sourceforge.pmd.ast.ParseException;
15 import net.sourceforge.pmd.ast.SimpleNode;
16 import net.sourceforge.pmd.jaxen.DocumentNavigator;
17 import org.apache.xml.serialize.OutputFormat;
18 import org.apache.xml.serialize.XMLSerializer;
19 import org.jaxen.BaseXPath;
20 import org.jaxen.JaxenException;
21 import org.jaxen.XPath;
22
23 import javax.swing.*;
24 import java.awt.BorderLayout;
25 import java.awt.Color;
26 import java.awt.FlowLayout;
27 import java.awt.Toolkit;
28 import java.awt.datatransfer.Clipboard;
29 import java.awt.datatransfer.ClipboardOwner;
30 import java.awt.datatransfer.StringSelection;
31 import java.awt.datatransfer.Transferable;
32 import java.awt.event.ActionEvent;
33 import java.awt.event.ActionListener;
34 import java.awt.event.KeyEvent;
35 import java.io.IOException;
36 import java.io.StringReader;
37 import java.io.StringWriter;
38 import java.util.Iterator;
39 import java.util.List;
40
41 public class Designer implements ClipboardOwner {
42
43 private JavaParser createParser() {
44 return getJDKVersion().createParser(new StringReader(codeEditorPane.getText()));
45 }
46
47 private TargetJDKVersion getJDKVersion() {
48 if (jdk14MenuItem.isSelected()) {
49 return new TargetJDK1_4();
50 }
51 return new TargetJDK1_5();
52 }
53
54 private class ShowListener implements ActionListener {
55 public void actionPerformed(ActionEvent ae) {
56 MyPrintStream ps = new MyPrintStream();
57 System.setOut(ps);
58 try {
59 ASTCompilationUnit lastCompilationUnit = createParser().CompilationUnit();
60 lastCompilationUnit.dump("");
61 astArea.setText(ps.getString());
62 } catch (ParseException pe) {
63 astArea.setText(pe.fillInStackTrace().getMessage());
64 }
65 }
66 }
67
68 private class DFAListener implements ActionListener {
69 public void actionPerformed(ActionEvent ae) {
70 try {
71 DFAGraphRule dfaGraphRule = new DFAGraphRule();
72 RuleSet rs = new RuleSet();
73 rs.addRule(dfaGraphRule);
74 RuleContext ctx = new RuleContext();
75 ctx.setSourceCodeFilename("[no filename]");
76 StringReader reader = new StringReader(codeEditorPane.getText());
77 new PMD(getJDKVersion()).processFile(reader, rs, ctx);
78 List methods = dfaGraphRule.getMethods();
79 if (!methods.isEmpty()) {
80 dfaPanel.resetTo(methods, codeEditorPane);
81 dfaPanel.repaint();
82 }
83 } catch (Exception e) {
84 e.printStackTrace();
85 }
86 }
87
88 }
89
90 private class XPathListener implements ActionListener {
91 public void actionPerformed(ActionEvent ae) {
92 xpathResults.clear();
93 if (xpathQueryArea.getText().length() == 0) {
94 xpathResults.addElement("XPath query field is empty");
95 xpathResultList.repaint();
96 codeEditorPane.requestFocus();
97 return;
98 }
99 JavaParser parser = createParser();
100 try {
101 XPath xpath = new BaseXPath(xpathQueryArea.getText(), new DocumentNavigator());
102 ASTCompilationUnit c = parser.CompilationUnit();
103 for (Iterator iter = xpath.selectNodes(c).iterator(); iter.hasNext();) {
104 StringBuffer sb = new StringBuffer();
105 SimpleNode node = (SimpleNode) iter.next();
106 String name = node.getClass().getName().substring(node.getClass().getName().lastIndexOf('.') + 1);
107 String line = " at line " + String.valueOf(node.getBeginLine());
108 sb.append(name).append(line).append(System.getProperty("line.separator"));
109 xpathResults.addElement(sb.toString().trim());
110 }
111 if (xpathResults.isEmpty()) {
112 xpathResults.addElement("No matching nodes " + System.currentTimeMillis());
113 }
114 } catch (ParseException pe) {
115 xpathResults.addElement(pe.fillInStackTrace().getMessage());
116 } catch (JaxenException je) {
117 xpathResults.addElement(je.fillInStackTrace().getMessage());
118 }
119 xpathResultList.repaint();
120 xpathQueryArea.requestFocus();
121 }
122 }
123
124
125 private final CodeEditorTextPane codeEditorPane = new CodeEditorTextPane();
126 private final JTextArea astArea = new JTextArea();
127 private DefaultListModel xpathResults = new DefaultListModel();
128 private final JList xpathResultList = new JList(xpathResults);
129 private final JTextArea xpathQueryArea = new JTextArea(15, 30);
130 private final JFrame frame = new JFrame("PMD Rule Designer");
131 private final DFAPanel dfaPanel = new DFAPanel();
132 private JRadioButtonMenuItem jdk14MenuItem;
133 private JRadioButtonMenuItem jdk15MenuItem;
134
135 public Designer() {
136 JSplitPane controlSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(codeEditorPane), createXPathQueryPanel());
137 JSplitPane resultsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createASTPanel(), createXPathResultPanel());
138
139 JTabbedPane tabbed = new JTabbedPane();
140 tabbed.addTab("Abstract Syntax Tree / XPath", resultsSplitPane);
141 tabbed.addTab("Data Flow Analysis", dfaPanel);
142 try {
143
144 if (JTabbedPane.class.getMethod("setMnemonicAt", new Class[]{Integer.TYPE, Integer.TYPE}) != null) {
145
146 tabbed.setMnemonicAt(0, KeyEvent.VK_A);
147 tabbed.setMnemonicAt(1, KeyEvent.VK_D);
148 }
149 } catch (NoSuchMethodException nsme) {}
150
151 JSplitPane containerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlSplitPane, tabbed);
152 containerSplitPane.setContinuousLayout(true);
153
154 JMenuBar menuBar = createMenuBar();
155 frame.setJMenuBar(menuBar);
156 frame.getContentPane().add(containerSplitPane);
157 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
158
159 int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
160 int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
161 frame.setSize(screenHeight - (screenHeight/4), screenHeight - (screenHeight/4));
162 frame.setLocation((screenWidth / 2) - frame.getWidth() / 2, (screenHeight / 2) - frame.getHeight() / 2);
163 frame.setVisible(true);
164 frame.pack();
165 frame.show();
166 resultsSplitPane.setDividerLocation(resultsSplitPane.getMaximumDividerLocation() - (resultsSplitPane.getMaximumDividerLocation() / 2));
167
168 }
169
170 private JMenuBar createMenuBar() {
171 JMenuBar menuBar = new JMenuBar();
172 JMenu menu = new JMenu("JDK");
173 ButtonGroup group = new ButtonGroup();
174 jdk14MenuItem = new JRadioButtonMenuItem("JDK 1.4");
175 jdk14MenuItem.setSelected(true);
176 group.add(jdk14MenuItem);
177 menu.add(jdk14MenuItem);
178 jdk15MenuItem = new JRadioButtonMenuItem("JDK 1.5");
179 jdk15MenuItem.setSelected(true);
180 group.add(jdk15MenuItem);
181 menu.add(jdk15MenuItem);
182 menuBar.add(menu);
183
184 JMenu actionsMenu = new JMenu("Actions");
185 JMenuItem copyXMLItem = new JMenuItem("Copy xml to clipboard");
186 copyXMLItem.addActionListener(new ActionListener() {
187 public void actionPerformed(ActionEvent e) {
188 copyXmlToClipboard();
189 }
190 });
191 actionsMenu.add(copyXMLItem);
192 JMenuItem createRuleXMLItem = new JMenuItem("Create rule XML");
193 createRuleXMLItem.addActionListener(new ActionListener() {
194 public void actionPerformed(ActionEvent e) {
195 createRuleXML();
196 }
197 });
198 actionsMenu.add(createRuleXMLItem);
199 menuBar.add(actionsMenu);
200 return menuBar;
201 }
202
203
204
205 private void createRuleXML() {
206 JPanel rulenamePanel = new JPanel();
207 rulenamePanel.setLayout(new FlowLayout());
208 rulenamePanel.add(new JLabel("Rule name"));
209 final JTextField rulenameField = new JTextField(30);
210 rulenamePanel.add(rulenameField);
211 JPanel rulemsgPanel = new JPanel();
212 rulemsgPanel.setLayout(new FlowLayout());
213 rulemsgPanel.add(new JLabel("Rule msg"));
214 final JTextField rulemsgField = new JTextField(60);
215 rulemsgPanel.add(rulemsgField);
216 JPanel ruledescPanel = new JPanel();
217 ruledescPanel.setLayout(new FlowLayout());
218 ruledescPanel.add(new JLabel("Rule desc"));
219 final JTextField ruledescField = new JTextField(60);
220 ruledescPanel.add(ruledescField);
221 JPanel ruleXMLPanel = new JPanel();
222 final JTextArea ruleXMLArea = new JTextArea(30, 50);
223 ruleXMLPanel.add(ruleXMLArea);
224 JButton go = new JButton("Create rule XML");
225 go.addActionListener(new ActionListener() {
226 public void actionPerformed(ActionEvent e) {
227 StringBuffer sb = new StringBuffer();
228 sb.append("<rule name=\"" + rulenameField.getText() + "\"" + PMD.EOL);
229 sb.append(" message=\"" + rulemsgField.getText() + "\"" + PMD.EOL);
230 sb.append(" class=\"" + (xpathQueryArea.getText().length() == 0 ? "" : "net.sourceforge.pmd.rules.XPathRule") + "\">" + PMD.EOL);
231 sb.append(" <description>" + PMD.EOL);
232 sb.append(" " + ruledescField.getText() + PMD.EOL);
233 sb.append(" </description>" + PMD.EOL);
234 if (xpathQueryArea.getText().length()!=0) {
235 sb.append(" <properties>" + PMD.EOL);
236 sb.append(" <property name=\"xpath\">" + PMD.EOL);
237 sb.append(" <value>" + PMD.EOL);
238 sb.append("<![CDATA[" + PMD.EOL);
239 sb.append(xpathQueryArea.getText() + PMD.EOL);
240 sb.append("]]>" + PMD.EOL);
241 sb.append(" </value>" + PMD.EOL);
242 sb.append(" </property>" + PMD.EOL);
243 sb.append(" </properties>" + PMD.EOL);
244 }
245 sb.append(" <priority>3</priority>" + PMD.EOL);
246 sb.append(" <example>" + PMD.EOL);
247 sb.append("<![CDATA[" + PMD.EOL);
248 sb.append(codeEditorPane.getText());
249 sb.append("]]>" + PMD.EOL);
250 sb.append(" </example>" + PMD.EOL);
251 sb.append("</rule>" + PMD.EOL);
252
253 ruleXMLArea.setText(sb.toString());
254 }
255 });
256
257 JPanel fieldsPanel = new JPanel();
258 fieldsPanel.setLayout(new BorderLayout());
259 fieldsPanel.add(rulenamePanel, BorderLayout.NORTH);
260 fieldsPanel.add(rulemsgPanel, BorderLayout.CENTER);
261 fieldsPanel.add(ruledescPanel, BorderLayout.SOUTH);
262
263 JPanel fieldBtnPanel = new JPanel();
264 fieldBtnPanel.setLayout(new BorderLayout());
265 fieldBtnPanel.add(fieldsPanel, BorderLayout.NORTH);
266 fieldBtnPanel.add(go, BorderLayout.SOUTH);
267
268 JPanel outer = new JPanel(new BorderLayout());
269 outer.add(fieldBtnPanel, BorderLayout.NORTH);
270 outer.add(ruleXMLPanel, BorderLayout.SOUTH);
271
272 JDialog d = new JDialog(frame);
273 d.setSize(200,300);
274 d.getContentPane().add(outer);
275 int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
276 int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
277 d.setLocation((screenWidth / 2) - frame.getWidth() / 2, (screenHeight / 2) - frame.getHeight() / 2);
278 d.setVisible(true);
279 d.pack();
280 d.show();
281 }
282
283 private JComponent createASTPanel() {
284 astArea.setRows(10);
285 astArea.setColumns(20);
286 JScrollPane astScrollPane = new JScrollPane(astArea);
287 return astScrollPane;
288 }
289
290 private JComponent createXPathResultPanel() {
291 xpathResults.addElement("No results yet");
292 xpathResultList.setBorder(BorderFactory.createLineBorder(Color.black));
293 xpathResultList.setFixedCellWidth(300);
294 JScrollPane scrollPane = new JScrollPane();
295 scrollPane.getViewport().setView(xpathResultList);
296 return scrollPane;
297 }
298
299 private JPanel createXPathQueryPanel() {
300 JPanel p = new JPanel();
301 p.setLayout(new BorderLayout());
302 xpathQueryArea.setBorder(BorderFactory.createLineBorder(Color.black));
303 JScrollPane scrollPane = new JScrollPane(xpathQueryArea);
304 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
305 scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
306 final JButton b = createGoButton();
307
308 p.add(new JLabel("XPath Query (if any)"), BorderLayout.NORTH);
309 p.add(scrollPane, BorderLayout.CENTER);
310 p.add(b, BorderLayout.SOUTH);
311
312 return p;
313 }
314
315 private JButton createGoButton() {
316 JButton b = new JButton("Go");
317 b.setMnemonic('g');
318 b.addActionListener(new ShowListener());
319 b.addActionListener(codeEditorPane);
320 b.addActionListener(new XPathListener());
321 b.addActionListener(new DFAListener());
322 return b;
323 }
324
325 public static void main(String[] args) {
326 new Designer();
327 }
328
329 private final void copyXmlToClipboard() {
330 if (codeEditorPane.getText()!=null && codeEditorPane.getText().trim().length()>0) {
331 ASTCompilationUnit cu = createParser().CompilationUnit();
332 String xml = "";
333 if (cu!=null) {
334 try {
335 xml = getXmlString( cu );
336 } catch (IOException e) {
337 e.printStackTrace();
338 xml = "Error trying to construct XML representation";
339 }
340 }
341 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(xml), this);
342 }
343 }
344
345 /***
346 * Returns an unformatted xml string (without the declaration)
347 *
348 * @param node
349 * @return
350 * @throws java.io.IOException
351 */
352 private String getXmlString(SimpleNode node) throws IOException {
353 StringWriter writer = new StringWriter();
354 XMLSerializer xmlSerializer = new XMLSerializer(writer, new OutputFormat("XML", "UTF-8", true));
355 xmlSerializer.asDOMSerializer();
356 xmlSerializer.serialize(node.asXml());
357 return writer.toString();
358 }
359
360 public void lostOwnership(Clipboard clipboard, Transferable contents) {}
361 }