View Javadoc

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                         // if it's a Boolean and it's 'false', what does that mean?
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             // Remove when minimal runtime support is >= JDK 1.4
191             Method setMnemonicAt = JTabbedPane.class.getMethod("setMnemonicAt", new Class[]{Integer.TYPE, Integer.TYPE});
192             if (setMnemonicAt != null) {
193                 //        // Compatible with >= JDK 1.4
194                 //        tabbed.setMnemonicAt(0, KeyEvent.VK_A);
195                 //        tabbed.setMnemonicAt(1, KeyEvent.VK_D);
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) { // Runtime is < JDK 1.4
200         } catch (IllegalAccessException e) { // Runtime is >= JDK 1.4 but there was an error accessing the function
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) { // Runtime is >= JDK 1.4 but there was an error accessing the function
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         //containerSplitPane.setDividerLocation(containerSplitPane.getMaximumDividerLocation() / 2);
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