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