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