Designer.java
0001 /**
0002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
0003  */
0004 package net.sourceforge.pmd.util.designer;
0005 
0006 import java.awt.BorderLayout;
0007 import java.awt.Color;
0008 import java.awt.Component;
0009 import java.awt.Dimension;
0010 import java.awt.Font;
0011 import java.awt.Toolkit;
0012 import java.awt.datatransfer.Clipboard;
0013 import java.awt.datatransfer.ClipboardOwner;
0014 import java.awt.datatransfer.StringSelection;
0015 import java.awt.datatransfer.Transferable;
0016 import java.awt.event.ActionEvent;
0017 import java.awt.event.ActionListener;
0018 import java.awt.event.ComponentEvent;
0019 import java.awt.event.KeyEvent;
0020 import java.awt.event.MouseEvent;
0021 import java.io.File;
0022 import java.io.FileInputStream;
0023 import java.io.FileWriter;
0024 import java.io.IOException;
0025 import java.io.StringReader;
0026 import java.io.StringWriter;
0027 import java.lang.reflect.Proxy;
0028 import java.util.ArrayList;
0029 import java.util.Arrays;
0030 import java.util.Collections;
0031 import java.util.Enumeration;
0032 import java.util.List;
0033 
0034 import javax.swing.AbstractAction;
0035 import javax.swing.AbstractButton;
0036 import javax.swing.ActionMap;
0037 import javax.swing.BorderFactory;
0038 import javax.swing.ButtonGroup;
0039 import javax.swing.DefaultListModel;
0040 import javax.swing.InputMap;
0041 import javax.swing.JButton;
0042 import javax.swing.JComponent;
0043 import javax.swing.JFrame;
0044 import javax.swing.JLabel;
0045 import javax.swing.JList;
0046 import javax.swing.JMenu;
0047 import javax.swing.JMenuBar;
0048 import javax.swing.JMenuItem;
0049 import javax.swing.JPanel;
0050 import javax.swing.JRadioButton;
0051 import javax.swing.JRadioButtonMenuItem;
0052 import javax.swing.JScrollPane;
0053 import javax.swing.JSplitPane;
0054 import javax.swing.JTabbedPane;
0055 import javax.swing.JTextArea;
0056 import javax.swing.JTree;
0057 import javax.swing.KeyStroke;
0058 import javax.swing.ListCellRenderer;
0059 import javax.swing.ListSelectionModel;
0060 import javax.swing.ScrollPaneConstants;
0061 import javax.swing.WindowConstants;
0062 import javax.swing.event.ListSelectionEvent;
0063 import javax.swing.event.ListSelectionListener;
0064 import javax.swing.event.TreeSelectionEvent;
0065 import javax.swing.event.TreeSelectionListener;
0066 import javax.swing.event.UndoableEditEvent;
0067 import javax.swing.event.UndoableEditListener;
0068 import javax.swing.text.JTextComponent;
0069 import javax.swing.tree.DefaultMutableTreeNode;
0070 import javax.swing.tree.DefaultTreeCellRenderer;
0071 import javax.swing.tree.DefaultTreeModel;
0072 import javax.swing.tree.TreeCellRenderer;
0073 import javax.swing.tree.TreeNode;
0074 import javax.swing.tree.TreePath;
0075 import javax.swing.tree.TreeSelectionModel;
0076 import javax.swing.undo.CannotRedoException;
0077 import javax.swing.undo.CannotUndoException;
0078 import javax.swing.undo.UndoManager;
0079 import javax.xml.parsers.DocumentBuilder;
0080 import javax.xml.parsers.DocumentBuilderFactory;
0081 import javax.xml.parsers.ParserConfigurationException;
0082 import javax.xml.transform.OutputKeys;
0083 import javax.xml.transform.Result;
0084 import javax.xml.transform.Source;
0085 import javax.xml.transform.Transformer;
0086 import javax.xml.transform.TransformerException;
0087 import javax.xml.transform.TransformerFactory;
0088 import javax.xml.transform.dom.DOMSource;
0089 import javax.xml.transform.stream.StreamResult;
0090 
0091 import net.sourceforge.pmd.PMD;
0092 import net.sourceforge.pmd.RuleContext;
0093 import net.sourceforge.pmd.RuleSet;
0094 import net.sourceforge.pmd.RuleSets;
0095 import net.sourceforge.pmd.lang.Language;
0096 import net.sourceforge.pmd.lang.LanguageVersion;
0097 import net.sourceforge.pmd.lang.LanguageVersionHandler;
0098 import net.sourceforge.pmd.lang.Parser;
0099 import net.sourceforge.pmd.lang.ast.Node;
0100 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
0101 import net.sourceforge.pmd.lang.java.ast.AccessNode;
0102 import net.sourceforge.pmd.lang.java.ast.JavaNode;
0103 import net.sourceforge.pmd.lang.java.ast.ParseException;
0104 import net.sourceforge.pmd.lang.java.symboltable.ClassNameDeclaration;
0105 import net.sourceforge.pmd.lang.java.symboltable.ClassScope;
0106 import net.sourceforge.pmd.lang.java.symboltable.LocalScope;
0107 import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
0108 import net.sourceforge.pmd.lang.java.symboltable.MethodScope;
0109 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
0110 import net.sourceforge.pmd.lang.java.symboltable.Scope;
0111 import net.sourceforge.pmd.lang.java.symboltable.SourceFileScope;
0112 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
0113 import net.sourceforge.pmd.lang.rule.XPathRule;
0114 import net.sourceforge.pmd.lang.xpath.Initializer;
0115 import net.sourceforge.pmd.util.StringUtil;
0116 
0117 import org.w3c.dom.Document;
0118 import org.w3c.dom.Element;
0119 import org.w3c.dom.Text;
0120 import org.xml.sax.SAXException;
0121 
0122 public class Designer implements ClipboardOwner {
0123 
0124     private static final int DEFAULT_LANGUAGE_VERSION_SELECTION_INDEX = Arrays.asList(getSupportedLanguageVersions())
0125       .indexOf(Language.JAVA.getDefaultVersion());
0126 
0127     private Node getCompilationUnit() {
0128   LanguageVersionHandler languageVersionHandler = getLanguageVersionHandler();
0129   Parser parser = languageVersionHandler.getParser();
0130   Node node = parser.parse(null, new StringReader(codeEditorPane.getText()));
0131   languageVersionHandler.getSymbolFacade().start(node);
0132   languageVersionHandler.getTypeResolutionFacade(null).start(node);
0133   return node;
0134     }
0135 
0136     private static LanguageVersion[] getSupportedLanguageVersions() {
0137   List<LanguageVersion> languageVersions = new ArrayList<LanguageVersion>();
0138   for (LanguageVersion languageVersion : LanguageVersion.values()) {
0139       if (languageVersion.getLanguageVersionHandler() != null) {
0140     Parser parser = languageVersion.getLanguageVersionHandler().getParser();
0141     if (parser != null && parser.canParse()) {
0142         languageVersions.add(languageVersion);
0143     }
0144       }
0145   }
0146   return languageVersions.toArray(new LanguageVersion[languageVersions.size()]);
0147     }
0148 
0149     private LanguageVersion getLanguageVersion() {
0150   return getSupportedLanguageVersions()[selectedLanguageVersionIndex()];
0151     }
0152 
0153     private void setLanguageVersion(LanguageVersion languageVersion) {
0154   if (languageVersion != null) {
0155       LanguageVersion[] versions = getSupportedLanguageVersions();
0156       for (int i = 0; i < versions.length; i++) {
0157     LanguageVersion version = versions[i];
0158     if (languageVersion.equals(version)) {
0159         languageVersionMenuItems[i].setSelected(true);
0160         break;
0161     }
0162       }
0163   }
0164     }
0165 
0166     private int selectedLanguageVersionIndex() {
0167   for (int i = 0; i < languageVersionMenuItems.length; i++) {
0168       if (languageVersionMenuItems[i].isSelected()) {
0169     return i;
0170       }
0171   }
0172   throw new RuntimeException("Initial default language version not specified");
0173     }
0174 
0175     private LanguageVersionHandler getLanguageVersionHandler() {
0176   LanguageVersion languageVersion = getLanguageVersion();
0177   return languageVersion.getLanguageVersionHandler();
0178     }
0179 
0180     private class ExceptionNode implements TreeNode {
0181 
0182   private Object item;
0183   private ExceptionNode[] kids;
0184 
0185   public ExceptionNode(Object theItem) {
0186       item = theItem;
0187 
0188       if (item instanceof ParseException) {
0189     createKids();
0190       }
0191   }
0192 
0193   // each line in the error message becomes a separate tree node
0194   private void createKids() {
0195 
0196       String message = ((ParseExceptionitem).getMessage();
0197       String[] lines = StringUtil.substringsOf(message, PMD.EOL);
0198 
0199       kids = new ExceptionNode[lines.length];
0200       for (int i = 0; i < lines.length; i++) {
0201     kids[inew ExceptionNode(lines[i]);
0202       }
0203   }
0204 
0205   public int getChildCount() {
0206       return kids == null : kids.length;
0207   }
0208 
0209   public boolean getAllowsChildren() {
0210       return false;
0211   }
0212 
0213   public boolean isLeaf() {
0214       return kids == null;
0215   }
0216 
0217   public TreeNode getParent() {
0218       return null;
0219   }
0220 
0221   public TreeNode getChildAt(int childIndex) {
0222       return kids[childIndex];
0223   }
0224 
0225   public String label() {
0226       return item.toString();
0227   }
0228 
0229   public Enumeration<ExceptionNode> children() {
0230       Enumeration<ExceptionNode> e = new Enumeration<ExceptionNode>() {
0231     int i = 0;
0232 
0233     public boolean hasMoreElements() {
0234         return kids != null && i < kids.length;
0235     }
0236 
0237     public ExceptionNode nextElement() {
0238         return kids[i++];
0239     }
0240       };
0241       return e;
0242   }
0243 
0244   public int getIndex(TreeNode node) {
0245       for (int i = 0; i < kids.length; i++) {
0246     if (kids[i== node) {
0247         return i;
0248     }
0249       }
0250       return -1;
0251   }
0252     }
0253 
0254     // Tree node that wraps the AST node for the tree widget and
0255     // any possible children they may have.
0256     private class ASTTreeNode implements TreeNode {
0257 
0258   private Node node;
0259   private ASTTreeNode parent;
0260   private ASTTreeNode[] kids;
0261 
0262   public ASTTreeNode(Node theNode) {
0263       node = theNode;
0264 
0265       Node parent = node.jjtGetParent();
0266       if (parent != null) {
0267     this.parent = new ASTTreeNode(parent);
0268       }
0269   }
0270 
0271   private ASTTreeNode(ASTTreeNode parent, Node theNode) {
0272       node = theNode;
0273       this.parent = parent;
0274   }
0275 
0276   public int getChildCount() {
0277       return node.jjtGetNumChildren();
0278   }
0279 
0280   public boolean getAllowsChildren() {
0281       return false;
0282   }
0283 
0284   public boolean isLeaf() {
0285       return node.jjtGetNumChildren() == 0;
0286   }
0287 
0288   public TreeNode getParent() {
0289       return parent;
0290   }
0291 
0292   public Scope getScope() {
0293       if (node instanceof JavaNode) {
0294     return ((JavaNodenode).getScope();
0295       }
0296       return null;
0297   }
0298 
0299   public Enumeration<ASTTreeNode> children() {
0300 
0301       if (getChildCount() 0) {
0302     getChildAt(0)// force it to build kids
0303       }
0304 
0305       Enumeration<ASTTreeNode> e = new Enumeration<ASTTreeNode>() {
0306     int i = 0;
0307 
0308     public boolean hasMoreElements() {
0309         return kids != null && i < kids.length;
0310     }
0311 
0312     public ASTTreeNode nextElement() {
0313         return kids[i++];
0314     }
0315       };
0316       return e;
0317   }
0318 
0319   public TreeNode getChildAt(int childIndex) {
0320 
0321       if (kids == null) {
0322     kids = new ASTTreeNode[node.jjtGetNumChildren()];
0323     for (int i = 0; i < kids.length; i++) {
0324         kids[inew ASTTreeNode(this.parent, node.jjtGetChild(i));
0325     }
0326       }
0327       return kids[childIndex];
0328   }
0329 
0330   public int getIndex(TreeNode node) {
0331 
0332       for (int i = 0; i < kids.length; i++) {
0333     if (kids[i== node) {
0334         return i;
0335     }
0336       }
0337       return -1;
0338   }
0339 
0340   public String label() {
0341       LanguageVersionHandler languageVersionHandler = getLanguageVersionHandler();
0342       StringWriter writer = new StringWriter();
0343       languageVersionHandler.getDumpFacade(writer, ""false).start(node);
0344       return writer.toString();
0345   }
0346 
0347   public String getToolTipText() {
0348       String tooltip = "Line: " + node.getBeginLine() " Column: " + node.getBeginColumn();
0349 
0350       if (node instanceof AccessNode) {
0351     AccessNode accessNode = (AccessNodenode;
0352     if (!"".equals(tooltip)) {
0353         tooltip += ",";
0354     }
0355     tooltip += accessNode.isAbstract() " Abstract" "";
0356     tooltip += accessNode.isStatic() " Static" "";
0357     tooltip += accessNode.isFinal() " Final" "";
0358     tooltip += accessNode.isNative() " Native" "";
0359     tooltip += accessNode.isPrivate() " Private" "";
0360     tooltip += accessNode.isSynchronized() " Synchronised" "";
0361     tooltip += accessNode.isTransient() " Transient" "";
0362     tooltip += accessNode.isVolatile() " Volatile" "";
0363     tooltip += accessNode.isStrictfp() " Strictfp" "";
0364       }
0365       return tooltip;
0366   }
0367     }
0368 
0369     private TreeCellRenderer createNoImageTreeCellRenderer() {
0370   DefaultTreeCellRenderer treeCellRenderer = new DefaultTreeCellRenderer();
0371   treeCellRenderer.setLeafIcon(null);
0372   treeCellRenderer.setOpenIcon(null);
0373   treeCellRenderer.setClosedIcon(null);
0374   return treeCellRenderer;
0375     }
0376 
0377     // Special tree variant that knows how to retrieve node labels and
0378     // provides the ability to expand all nodes at once.
0379     private class TreeWidget extends JTree {
0380 
0381   private static final long serialVersionUID = 1L;
0382 
0383   public TreeWidget(Object[] items) {
0384       super(items);
0385       setToolTipText("");
0386   }
0387 
0388   @Override
0389   public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row,
0390     boolean hasFocus) {
0391       if (value == null) {
0392     return "";
0393       }
0394       if (value instanceof ASTTreeNode) {
0395     return ((ASTTreeNodevalue).label();
0396       }
0397       if (value instanceof ExceptionNode) {
0398     return ((ExceptionNodevalue).label();
0399       }
0400       return value.toString();
0401   }
0402 
0403   @Override
0404   public String getToolTipText(MouseEvent e) {
0405       if (getRowForLocation(e.getX(), e.getY()) == -1) {
0406     return null;
0407       }
0408       TreePath curPath = getPathForLocation(e.getX(), e.getY());
0409       if (curPath.getLastPathComponent() instanceof ASTTreeNode) {
0410     return ((ASTTreeNodecurPath.getLastPathComponent()).getToolTipText();
0411       else {
0412     return super.getToolTipText(e);
0413       }
0414   }
0415 
0416   public void expandAll(boolean expand) {
0417       TreeNode root = (TreeNodegetModel().getRoot();
0418       expandAll(new TreePath(root), expand);
0419   }
0420 
0421   private void expandAll(TreePath parent, boolean expand) {
0422       // Traverse children
0423       TreeNode node = (TreeNodeparent.getLastPathComponent();
0424       if (node.getChildCount() >= 0) {
0425     for (Enumeration<TreeNode> e = node.children(); e.hasMoreElements();) {
0426         TreeNode n = e.nextElement();
0427         TreePath path = parent.pathByAddingChild(n);
0428         expandAll(path, expand);
0429     }
0430       }
0431 
0432       if (expand) {
0433     expandPath(parent);
0434       else {
0435     collapsePath(parent);
0436       }
0437   }
0438     }
0439 
0440     private void loadASTTreeData(TreeNode rootNode) {
0441   astTreeWidget.setModel(new DefaultTreeModel(rootNode));
0442   astTreeWidget.setRootVisible(true);
0443   astTreeWidget.expandAll(true);
0444     }
0445 
0446     private void loadSymbolTableTreeData(TreeNode rootNode) {
0447   if (rootNode != null) {
0448       symbolTableTreeWidget.setModel(new DefaultTreeModel(rootNode));
0449       symbolTableTreeWidget.expandAll(true);
0450   else {
0451       symbolTableTreeWidget.setModel(null);
0452   }
0453     }
0454 
0455     private class ShowListener implements ActionListener {
0456   public void actionPerformed(ActionEvent ae) {
0457       TreeNode tn;
0458       try {
0459     Node lastCompilationUnit = getCompilationUnit();
0460     tn = new ASTTreeNode(lastCompilationUnit);
0461       catch (ParseException pe) {
0462     tn = new ExceptionNode(pe);
0463       }
0464 
0465       loadASTTreeData(tn);
0466       loadSymbolTableTreeData(null);
0467   }
0468     }
0469 
0470     private class DFAListener implements ActionListener {
0471   public void actionPerformed(ActionEvent ae) {
0472 
0473       DFAGraphRule dfaGraphRule = new DFAGraphRule();
0474       RuleSet rs = new RuleSet();
0475       LanguageVersion languageVersion = getLanguageVersion();
0476       if (languageVersion.getLanguage().equals(Language.JAVA)) {
0477     rs.addRule(dfaGraphRule);
0478       }
0479       RuleContext ctx = new RuleContext();
0480       ctx.setSourceCodeFilename("[no filename]." + languageVersion.getLanguage().getExtensions().get(0));
0481       StringReader reader = new StringReader(codeEditorPane.getText());
0482       PMD pmd = new PMD();
0483       pmd.getConfiguration().setDefaultLanguageVersion(languageVersion);
0484 
0485       try {
0486     pmd.processFile(reader, new RuleSets(rs), ctx);
0487     //      } catch (PMDException pmde) {
0488     //    loadTreeData(new ExceptionNode(pmde));
0489       catch (Exception e) {
0490     e.printStackTrace();
0491       }
0492 
0493       List<ASTMethodDeclaration> methods = dfaGraphRule.getMethods();
0494       if (methods != null && !methods.isEmpty()) {
0495     dfaPanel.resetTo(methods, codeEditorPane);
0496     dfaPanel.repaint();
0497       }
0498   }
0499     }
0500 
0501     private class XPathListener implements ActionListener {
0502   public void actionPerformed(ActionEvent ae) {
0503       xpathResults.clear();
0504       if (xpathQueryArea.getText().length() == 0) {
0505     xpathResults.addElement("XPath query field is empty.");
0506     xpathResultList.repaint();
0507     codeEditorPane.requestFocus();
0508     return;
0509       }
0510       Node c = getCompilationUnit();
0511       try {
0512     XPathRule xpathRule = new XPathRule() {
0513         @Override
0514         public void addViolation(Object data, Node node, String arg) {
0515       xpathResults.addElement(node);
0516         }
0517     };
0518     xpathRule.setMessage("");
0519     xpathRule.setLanguage(getLanguageVersion().getLanguage());
0520     xpathRule.setProperty(XPathRule.XPATH_DESCRIPTOR, xpathQueryArea.getText());
0521     xpathRule.setProperty(XPathRule.VERSION_DESCRIPTOR, xpathVersionButtonGroup.getSelection()
0522       .getActionCommand());
0523 
0524     RuleSet ruleSet = new RuleSet();
0525     ruleSet.addRule(xpathRule);
0526 
0527     RuleSets ruleSets = new RuleSets();
0528     ruleSets.addRuleSet(ruleSet);
0529 
0530     RuleContext ruleContext = new RuleContext();
0531     ruleContext.setLanguageVersion(getLanguageVersion());
0532 
0533     List<Node> nodes = new ArrayList<Node>();
0534     nodes.add(c);
0535     ruleSets.apply(nodes, ruleContext, xpathRule.getLanguage());
0536 
0537     if (xpathResults.isEmpty()) {
0538         xpathResults.addElement("No matching nodes " + System.currentTimeMillis());
0539     }
0540       catch (ParseException pe) {
0541     xpathResults.addElement(pe.fillInStackTrace().getMessage());
0542       }
0543       xpathResultList.repaint();
0544       xpathQueryArea.requestFocus();
0545   }
0546     }
0547 
0548     private class SymbolTableListener implements TreeSelectionListener {
0549   public void valueChanged(TreeSelectionEvent e) {
0550       if (e.getNewLeadSelectionPath() != null) {
0551     ASTTreeNode astTreeNode = (ASTTreeNodee.getNewLeadSelectionPath().getLastPathComponent();
0552 
0553     DefaultMutableTreeNode symbolTableTreeNode = new DefaultMutableTreeNode();
0554     DefaultMutableTreeNode selectedAstTreeNode = new DefaultMutableTreeNode("AST Node: "
0555       + astTreeNode.label());
0556     symbolTableTreeNode.add(selectedAstTreeNode);
0557 
0558     List<Scope> scopes = new ArrayList<Scope>();
0559     Scope scope = astTreeNode.getScope();
0560     while (scope != null) {
0561         scopes.add(scope);
0562         scope = scope.getParent();
0563     }
0564     Collections.reverse(scopes);
0565     for (int i = 0; i < scopes.size(); i++) {
0566         scope = scopes.get(i);
0567         DefaultMutableTreeNode scopeTreeNode = new DefaultMutableTreeNode("Scope: "
0568           + scope.getClass().getSimpleName());
0569         selectedAstTreeNode.add(scopeTreeNode);
0570         if (!(scope instanceof MethodScope || scope instanceof LocalScope)) {
0571       if (!scope.getClassDeclarations().isEmpty()) {
0572           for (ClassNameDeclaration classNameDeclaration : scope.getClassDeclarations().keySet()) {
0573         DefaultMutableTreeNode classNameDeclarationTreeNode = new DefaultMutableTreeNode(
0574           "Class name declaration: " + classNameDeclaration);
0575         scopeTreeNode.add(classNameDeclarationTreeNode);
0576         for (NameOccurrence nameOccurrence : scope.getClassDeclarations().get(
0577           classNameDeclaration)) {
0578             DefaultMutableTreeNode nameOccurenceTreeNode = new DefaultMutableTreeNode(
0579               "Name occurrence: " + nameOccurrence);
0580             classNameDeclarationTreeNode.add(nameOccurenceTreeNode);
0581         }
0582           }
0583       }
0584         }
0585         if (scope instanceof ClassScope) {
0586       ClassScope classScope = (ClassScopescope;
0587       if (!classScope.getMethodDeclarations().isEmpty()) {
0588           for (MethodNameDeclaration methodNameDeclaration : classScope.getMethodDeclarations()
0589             .keySet()) {
0590         DefaultMutableTreeNode methodNameDeclarationTreeNode = new DefaultMutableTreeNode(
0591           "Method name declaration: " + methodNameDeclaration);
0592         scopeTreeNode.add(methodNameDeclarationTreeNode);
0593         for (NameOccurrence nameOccurrence : classScope.getMethodDeclarations().get(
0594           methodNameDeclaration)) {
0595             DefaultMutableTreeNode nameOccurenceTreeNode = new DefaultMutableTreeNode(
0596               "Name occurrence: " + nameOccurrence);
0597             methodNameDeclarationTreeNode.add(nameOccurenceTreeNode);
0598         }
0599           }
0600       }
0601         }
0602         if (!(scope instanceof SourceFileScope)) {
0603       if (!scope.getVariableDeclarations().isEmpty()) {
0604           for (VariableNameDeclaration variableNameDeclaration : scope.getVariableDeclarations()
0605             .keySet()) {
0606         DefaultMutableTreeNode variableNameDeclarationTreeNode = new DefaultMutableTreeNode(
0607           "Variable name declaration: " + variableNameDeclaration);
0608         scopeTreeNode.add(variableNameDeclarationTreeNode);
0609         for (NameOccurrence nameOccurrence : scope.getVariableDeclarations().get(
0610           variableNameDeclaration)) {
0611             DefaultMutableTreeNode nameOccurenceTreeNode = new DefaultMutableTreeNode(
0612               "Name occurrence: " + nameOccurrence);
0613             variableNameDeclarationTreeNode.add(nameOccurenceTreeNode);
0614         }
0615           }
0616       }
0617         }
0618     }
0619     loadSymbolTableTreeData(symbolTableTreeNode);
0620       }
0621   }
0622     }
0623 
0624     private class CodeHighlightListener implements TreeSelectionListener {
0625   public void valueChanged(TreeSelectionEvent e) {
0626       if (e.getNewLeadSelectionPath() != null) {
0627     ASTTreeNode selected = (ASTTreeNodee.getNewLeadSelectionPath().getLastPathComponent();
0628     if (selected != null) {
0629         codeEditorPane.select(selected.node);
0630     }
0631       }
0632   }
0633     }
0634 
0635     private class ASTListCellRenderer extends JLabel implements ListCellRenderer {
0636   private static final long serialVersionUID = 1L;
0637 
0638   public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
0639     boolean cellHasFocus) {
0640 
0641       if (isSelected) {
0642     setBackground(list.getSelectionBackground());
0643     setForeground(list.getSelectionForeground());
0644       else {
0645     setBackground(list.getBackground());
0646     setForeground(list.getForeground());
0647       }
0648 
0649       String text;
0650       if (value instanceof Node) {
0651     Node node = (Nodevalue;
0652     StringBuffer sb = new StringBuffer();
0653     String name = node.getClass().getName().substring(node.getClass().getName().lastIndexOf('.'1);
0654     if (Proxy.isProxyClass(value.getClass())) {
0655         name = value.toString();
0656     }
0657     sb.append(name).append(" at line ").append(node.getBeginLine()).append(" column ").append(
0658       node.getBeginColumn()).append(PMD.EOL);
0659     text = sb.toString();
0660       else {
0661     text = value.toString();
0662       }
0663       setText(text);
0664       return this;
0665   }
0666     }
0667 
0668     private class ASTSelectionListener implements ListSelectionListener {
0669   public void valueChanged(ListSelectionEvent e) {
0670       ListSelectionModel lsm = (ListSelectionModele.getSource();
0671       if (!lsm.isSelectionEmpty()) {
0672     Object o = xpathResults.get(lsm.getMinSelectionIndex());
0673     if (instanceof Node) {
0674         codeEditorPane.select((Nodeo);
0675     }
0676       }
0677   }
0678     }
0679 
0680     private boolean exitOnClose = true;
0681     private final CodeEditorTextPane codeEditorPane = new CodeEditorTextPane();
0682     private final TreeWidget astTreeWidget = new TreeWidget(new Object[0]);
0683     private DefaultListModel xpathResults = new DefaultListModel();
0684     private final JList xpathResultList = new JList(xpathResults);
0685     private final JTextArea xpathQueryArea = new JTextArea(1530);
0686     private final ButtonGroup xpathVersionButtonGroup = new ButtonGroup();
0687     private final TreeWidget symbolTableTreeWidget = new TreeWidget(new Object[0]);
0688     private final JFrame frame = new JFrame("PMD Rule Designer (v " + PMD.VERSION + ')');
0689     private final DFAPanel dfaPanel = new DFAPanel();
0690     private final JRadioButtonMenuItem[] languageVersionMenuItems = new JRadioButtonMenuItem[getSupportedLanguageVersions().length];
0691 
0692     public Designer(String[] args) {
0693   if (args.length > 0) {
0694       exitOnClose = !args[0].equals("-noexitonclose");
0695   }
0696 
0697   Initializer.initialize();
0698 
0699   xpathQueryArea.setFont(new Font("Verdana", Font.PLAIN, 16));
0700   JSplitPane controlSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createCodeEditorPanel(),
0701     createXPathQueryPanel());
0702 
0703   JSplitPane astAndSymbolTablePane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, createASTPanel(),
0704     createSymbolTableResultPanel());
0705 
0706   JSplitPane resultsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, astAndSymbolTablePane,
0707     createXPathResultPanel());
0708 
0709   JTabbedPane tabbed = new JTabbedPane();
0710   tabbed.addTab("Abstract Syntax Tree / XPath / Symbol Table", resultsSplitPane);
0711   tabbed.addTab("Data Flow Analysis", dfaPanel);
0712   tabbed.setMnemonicAt(0, KeyEvent.VK_A);
0713   tabbed.setMnemonicAt(1, KeyEvent.VK_D);
0714 
0715   JSplitPane containerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlSplitPane, tabbed);
0716   containerSplitPane.setContinuousLayout(true);
0717 
0718   JMenuBar menuBar = createMenuBar();
0719   frame.setJMenuBar(menuBar);
0720   frame.getContentPane().add(containerSplitPane);
0721   frame.setDefaultCloseOperation(exitOnClose ? JFrame.EXIT_ON_CLOSE : JFrame.DISPOSE_ON_CLOSE);
0722 
0723   Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
0724   int screenHeight = screenSize.height;
0725   int screenWidth = screenSize.width;
0726 
0727   frame.pack();
0728   frame.setSize(screenWidth * 4, screenHeight * 4);
0729   frame.setLocation((screenWidth - frame.getWidth()) 2(screenHeight - frame.getHeight()) 2);
0730   frame.setVisible(true);
0731   int horozontalMiddleLocation = controlSplitPane.getMaximumDividerLocation() 5;
0732   controlSplitPane.setDividerLocation(horozontalMiddleLocation);
0733   containerSplitPane.setDividerLocation(containerSplitPane.getMaximumDividerLocation() 2);
0734   astAndSymbolTablePane.setDividerLocation(astAndSymbolTablePane.getMaximumDividerLocation() 3);
0735   resultsSplitPane.setDividerLocation(horozontalMiddleLocation);
0736 
0737   loadSettings();
0738     }
0739 
0740     private JMenuBar createMenuBar() {
0741   JMenuBar menuBar = new JMenuBar();
0742   JMenu menu = new JMenu("Language");
0743   ButtonGroup group = new ButtonGroup();
0744 
0745   LanguageVersion[] languageVersions = getSupportedLanguageVersions();
0746   for (int i = 0; i < languageVersions.length; i++) {
0747       LanguageVersion languageVersion = languageVersions[i];
0748       JRadioButtonMenuItem button = new JRadioButtonMenuItem(languageVersion.getShortName());
0749       languageVersionMenuItems[i= button;
0750       group.add(button);
0751       menu.add(button);
0752   }
0753   languageVersionMenuItems[DEFAULT_LANGUAGE_VERSION_SELECTION_INDEX].setSelected(true);
0754   menuBar.add(menu);
0755 
0756   JMenu actionsMenu = new JMenu("Actions");
0757   JMenuItem copyXMLItem = new JMenuItem("Copy xml to clipboard");
0758   copyXMLItem.addActionListener(new ActionListener() {
0759       public void actionPerformed(ActionEvent e) {
0760     copyXmlToClipboard();
0761       }
0762   });
0763   actionsMenu.add(copyXMLItem);
0764   JMenuItem createRuleXMLItem = new JMenuItem("Create rule XML");
0765   createRuleXMLItem.addActionListener(new ActionListener() {
0766       public void actionPerformed(ActionEvent e) {
0767     createRuleXML();
0768       }
0769   });
0770   actionsMenu.add(createRuleXMLItem);
0771   menuBar.add(actionsMenu);
0772 
0773   return menuBar;
0774     }
0775 
0776     private void createRuleXML() {
0777   CreateXMLRulePanel rulePanel = new CreateXMLRulePanel(xpathQueryArea, codeEditorPane);
0778   JFrame xmlframe = new JFrame("Create XML Rule");
0779   xmlframe.setContentPane(rulePanel);
0780   xmlframe.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
0781   xmlframe.setSize(new Dimension(600700));
0782   xmlframe.addComponentListener(new java.awt.event.ComponentAdapter() {
0783       @Override
0784       public void componentResized(ComponentEvent e) {
0785     JFrame tmp = (JFramee.getSource();
0786     if (tmp.getWidth() 600 || tmp.getHeight() 700) {
0787         tmp.setSize(600700);
0788     }
0789       }
0790   });
0791   int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
0792   int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
0793   xmlframe.pack();
0794   xmlframe.setLocation((screenWidth - xmlframe.getWidth()) 2(screenHeight - xmlframe.getHeight()) 2);
0795   xmlframe.setVisible(true);
0796     }
0797 
0798     private JComponent createCodeEditorPanel() {
0799   JPanel p = new JPanel();
0800   p.setLayout(new BorderLayout());
0801   codeEditorPane.setBorder(BorderFactory.createLineBorder(Color.black));
0802   makeTextComponentUndoable(codeEditorPane);
0803 
0804   p.add(new JLabel("Source code:"), BorderLayout.NORTH);
0805   p.add(new JScrollPane(codeEditorPane), BorderLayout.CENTER);
0806 
0807   return p;
0808     }
0809 
0810     private JComponent createASTPanel() {
0811   astTreeWidget.setCellRenderer(createNoImageTreeCellRenderer());
0812   TreeSelectionModel model = astTreeWidget.getSelectionModel();
0813   model.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
0814   model.addTreeSelectionListener(new SymbolTableListener());
0815   model.addTreeSelectionListener(new CodeHighlightListener());
0816   return new JScrollPane(astTreeWidget);
0817     }
0818 
0819     private JComponent createXPathResultPanel() {
0820   xpathResults.addElement("No XPath results yet, run an XPath Query first.");
0821   xpathResultList.setBorder(BorderFactory.createLineBorder(Color.black));
0822   xpathResultList.setFixedCellWidth(300);
0823   xpathResultList.setCellRenderer(new ASTListCellRenderer());
0824   xpathResultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
0825   xpathResultList.getSelectionModel().addListSelectionListener(new ASTSelectionListener());
0826   JScrollPane scrollPane = new JScrollPane();
0827   scrollPane.getViewport().setView(xpathResultList);
0828   return scrollPane;
0829     }
0830 
0831     private JPanel createXPathQueryPanel() {
0832   JPanel p = new JPanel();
0833   p.setLayout(new BorderLayout());
0834   xpathQueryArea.setBorder(BorderFactory.createLineBorder(Color.black));
0835   makeTextComponentUndoable(xpathQueryArea);
0836   JScrollPane scrollPane = new JScrollPane(xpathQueryArea);
0837   scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
0838   scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
0839   final JButton b = createGoButton();
0840 
0841   JPanel topPanel = new JPanel();
0842   topPanel.setLayout(new BorderLayout());
0843   topPanel.add(new JLabel("XPath Query (if any):"), BorderLayout.WEST);
0844   topPanel.add(createXPathVersionPanel(), BorderLayout.EAST);
0845 
0846   p.add(topPanel, BorderLayout.NORTH);
0847   p.add(scrollPane, BorderLayout.CENTER);
0848   p.add(b, BorderLayout.SOUTH);
0849 
0850   return p;
0851     }
0852 
0853     private JComponent createSymbolTableResultPanel() {
0854   symbolTableTreeWidget.setCellRenderer(createNoImageTreeCellRenderer());
0855   return new JScrollPane(symbolTableTreeWidget);
0856     }
0857 
0858     private JPanel createXPathVersionPanel() {
0859   JPanel p = new JPanel();
0860   p.add(new JLabel("XPath Version:"));
0861   for (Object[] values : XPathRule.VERSION_DESCRIPTOR.choices()) {
0862       JRadioButton b = new JRadioButton();
0863       b.setText((Stringvalues[0]);
0864       b.setActionCommand(b.getText());
0865       if (values[0].equals(XPathRule.VERSION_DESCRIPTOR.defaultValue())) {
0866     b.setSelected(true);
0867       }
0868       xpathVersionButtonGroup.add(b);
0869       p.add(b);
0870   }
0871   return p;
0872     }
0873 
0874     private JButton createGoButton() {
0875   JButton b = new JButton("Go");
0876   b.setMnemonic('g');
0877   b.addActionListener(new ShowListener());
0878   b.addActionListener(new XPathListener());
0879   b.addActionListener(new DFAListener());
0880   b.addActionListener(new ActionListener() {
0881       public void actionPerformed(ActionEvent e) {
0882     saveSettings();
0883       }
0884   });
0885   return b;
0886     }
0887 
0888     private static void makeTextComponentUndoable(JTextComponent textConponent) {
0889   final UndoManager undoManager = new UndoManager();
0890   textConponent.getDocument().addUndoableEditListener(new UndoableEditListener() {
0891       public void undoableEditHappened(UndoableEditEvent evt) {
0892     undoManager.addEdit(evt.getEdit());
0893       }
0894   });
0895   ActionMap actionMap = textConponent.getActionMap();
0896   InputMap inputMap = textConponent.getInputMap();
0897   actionMap.put("Undo"new AbstractAction("Undo") {
0898       public void actionPerformed(ActionEvent evt) {
0899     try {
0900         if (undoManager.canUndo()) {
0901       undoManager.undo();
0902         }
0903     catch (CannotUndoException e) {
0904     }
0905       }
0906   });
0907   inputMap.put(KeyStroke.getKeyStroke("control Z")"Undo");
0908 
0909   actionMap.put("Redo"new AbstractAction("Redo") {
0910       public void actionPerformed(ActionEvent evt) {
0911     try {
0912         if (undoManager.canRedo()) {
0913       undoManager.redo();
0914         }
0915     catch (CannotRedoException e) {
0916     }
0917       }
0918   });
0919   inputMap.put(KeyStroke.getKeyStroke("control Y")"Redo");
0920     }
0921 
0922     public static void main(String[] args) {
0923   new Designer(args);
0924     }
0925 
0926     private final void copyXmlToClipboard() {
0927   if (codeEditorPane.getText() != null && codeEditorPane.getText().trim().length() 0) {
0928       String xml = "";
0929       Node cu = getCompilationUnit();
0930       if (cu != null) {
0931     try {
0932         xml = getXmlString(cu);
0933     catch (TransformerException e) {
0934         e.printStackTrace();
0935         xml = "Error trying to construct XML representation";
0936     }
0937       }
0938       Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(xml)this);
0939   }
0940     }
0941 
0942     /**
0943      * Returns an unformatted xml string (without the declaration)
0944      *
0945      @throws TransformerException if the XML cannot be converted to a string
0946      */
0947     private String getXmlString(Node nodethrows TransformerException {
0948   StringWriter writer = new StringWriter();
0949 
0950   Source source = new DOMSource(node.getAsDocument());
0951   Result result = new StreamResult(writer);
0952   TransformerFactory transformerFactory = TransformerFactory.newInstance();
0953   transformerFactory.setAttribute("indent-number"3);
0954   Transformer xformer = transformerFactory.newTransformer();
0955   xformer.setOutputProperty(OutputKeys.INDENT, "yes");
0956   xformer.transform(source, result);
0957 
0958   return writer.toString();
0959     }
0960 
0961     public void lostOwnership(Clipboard clipboard, Transferable contents) {
0962     }
0963 
0964     private static final String SETTINGS_FILE_NAME = System.getProperty("user.home")
0965       + System.getProperty("file.separator"".pmd_designer.xml";
0966 
0967     private void loadSettings() {
0968   try {
0969       File file = new File(SETTINGS_FILE_NAME);
0970       if (file.exists()) {
0971     DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
0972     Document document = builder.parse(new FileInputStream(file));
0973     Element settingsElement = document.getDocumentElement();
0974     Element codeElement = (ElementsettingsElement.getElementsByTagName("code").item(0);
0975     Element xpathElement = (ElementsettingsElement.getElementsByTagName("xpath").item(0);
0976 
0977     String code = getTextContext(codeElement);
0978     String languageVersion = codeElement.getAttribute("language-version");
0979     String xpath = getTextContext(xpathElement);
0980     String xpathVersion = xpathElement.getAttribute("version");
0981 
0982     codeEditorPane.setText(code);
0983     setLanguageVersion(LanguageVersion.findByTerseName(languageVersion));
0984     xpathQueryArea.setText(xpath);
0985     for (Enumeration<AbstractButton> e = xpathVersionButtonGroup.getElements(); e.hasMoreElements();) {
0986         AbstractButton button = e.nextElement();
0987         if (xpathVersion.equals(button.getActionCommand())) {
0988       button.setSelected(true);
0989       break;
0990         }
0991     }
0992       }
0993   catch (ParserConfigurationException e) {
0994       e.printStackTrace();
0995   catch (IOException e) {
0996       e.printStackTrace();
0997   catch (SAXException e) {
0998       e.printStackTrace();
0999   }
1000     }
1001 
1002     private void saveSettings() {
1003   try {
1004       DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
1005       DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
1006       Document document = documentBuilder.newDocument();
1007 
1008       Element settingsElement = document.createElement("settings");
1009       document.appendChild(settingsElement);
1010 
1011       Element codeElement = document.createElement("code");
1012       settingsElement.appendChild(codeElement);
1013       codeElement.setAttribute("language-version", getLanguageVersion().getTerseName());
1014       codeElement.appendChild(document.createCDATASection(codeEditorPane.getText()));
1015 
1016       Element xpathElement = document.createElement("xpath");
1017       settingsElement.appendChild(xpathElement);
1018       xpathElement.setAttribute("version", xpathVersionButtonGroup.getSelection().getActionCommand());
1019       xpathElement.appendChild(document.createCDATASection(xpathQueryArea.getText()));
1020 
1021       TransformerFactory transformerFactory = TransformerFactory.newInstance();
1022       Transformer transformer = transformerFactory.newTransformer();
1023       transformer.setOutputProperty(OutputKeys.METHOD, "xml");
1024       // This is as close to pretty printing as we'll get using standard Java APIs.
1025       transformer.setOutputProperty(OutputKeys.INDENT, "yes");
1026       transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount""3");
1027       transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
1028 
1029       Source source = new DOMSource(document);
1030       Result result = new StreamResult(new FileWriter(new File(SETTINGS_FILE_NAME)));
1031       transformer.transform(source, result);
1032   catch (ParserConfigurationException e) {
1033       e.printStackTrace();
1034   catch (IOException e) {
1035       e.printStackTrace();
1036   catch (TransformerException e) {
1037       e.printStackTrace();
1038   }
1039     }
1040     
1041     private String getTextContext(Element element) {
1042   StringBuilder buf = new StringBuilder();
1043   for (int i = 0; i < element.getChildNodes().getLength(); i++) {
1044       org.w3c.dom.Node child = element.getChildNodes().item(i);
1045       if (child instanceof Text) {
1046     buf.append(((Text)child).getData());
1047       }
1048   }
1049   return buf.toString();
1050     }
1051 }