
net.sourceforge.pmd.util.designer.Designer Maven / Gradle / Ivy
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.util.designer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.JTextComponent;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.SourceType;
import net.sourceforge.pmd.TargetJDK1_3;
import net.sourceforge.pmd.TargetJDK1_4;
import net.sourceforge.pmd.TargetJDK1_5;
import net.sourceforge.pmd.TargetJDK1_6;
import net.sourceforge.pmd.ast.ASTCompilationUnit;
import net.sourceforge.pmd.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.ast.Node;
import net.sourceforge.pmd.ast.ParseException;
import net.sourceforge.pmd.ast.SimpleNode;
import net.sourceforge.pmd.jaxen.DocumentNavigator;
import net.sourceforge.pmd.jaxen.MatchesFunction;
import net.sourceforge.pmd.jaxen.TypeOfFunction;
import net.sourceforge.pmd.jsp.ast.JspCharStream;
import net.sourceforge.pmd.jsp.ast.JspParser;
import net.sourceforge.pmd.typeresolution.ClassTypeResolver;
import net.sourceforge.pmd.util.NumericConstants;
import net.sourceforge.pmd.util.StringUtil;
import org.jaxen.BaseXPath;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
public class Designer implements ClipboardOwner {
private static final char LABEL_IMAGE_SEPARATOR = ':';
private static final Color IMAGE_TEXT_COLOR = Color.BLUE;
private interface Parser { public SimpleNode parse(StringReader sr); }
private static final Parser jdkParser1_3 = new Parser() {
public SimpleNode parse(StringReader sr) { return new TargetJDK1_3().createParser(sr).CompilationUnit(); }
};
private static final Parser jdkParser1_4 = new Parser() {
public SimpleNode parse(StringReader sr) { return new TargetJDK1_4().createParser(sr).CompilationUnit(); }
};
private static final Parser jdkParser1_5 = new Parser() {
public SimpleNode parse(StringReader sr) { return new TargetJDK1_5().createParser(sr).CompilationUnit(); }
};
private static final Parser jdkParser1_6 = new Parser() {
public SimpleNode parse(StringReader sr) { return new TargetJDK1_6().createParser(sr).CompilationUnit(); }
};
private static final Parser jspParser = new Parser() {
public SimpleNode parse(StringReader sr) { return new JspParser(new JspCharStream(sr)).CompilationUnit(); }
};
private static final Object[][] sourceTypeSets = new Object[][] {
{ "JDK 1.3", SourceType.JAVA_13, jdkParser1_3 },
{ "JDK 1.4", SourceType.JAVA_14, jdkParser1_4 },
{ "JDK 1.5", SourceType.JAVA_15, jdkParser1_5 },
{ "JDK 1.6", SourceType.JAVA_16, jdkParser1_6 },
{ "JSP", SourceType.JSP, jspParser }
};
private static final int defaultSourceTypeSelectionIndex = 2; // Java 1.5
private SimpleNode getCompilationUnit() {
Parser parser = (Parser)sourceTypeSets[selectedSourceTypeIndex()][2];
ASTCompilationUnit n = (ASTCompilationUnit)parser.parse(new StringReader(codeEditorPane.getText()));
ClassTypeResolver ctr = new ClassTypeResolver();
n.jjtAccept(ctr, null);
return n;
}
private SourceType getSourceType() {
return (SourceType)sourceTypeSets[selectedSourceTypeIndex()][1];
}
private int selectedSourceTypeIndex() {
for (int i=0; i 0) getChildAt(0); // force it to build kids
Enumeration e = new Enumeration() {
int i = 0;
public boolean hasMoreElements() {
return kids != null && i < kids.length;
}
public Object nextElement() { return kids[i++]; }
};
return e;
}
public TreeNode getChildAt(int childIndex) {
if (kids == null) {
kids = new ASTTreeNode[node.jjtGetNumChildren()];
for (int i=0; i= 0) {
for (Enumeration e=node.children(); e.hasMoreElements(); ) {
TreeNode n = (TreeNode)e.nextElement();
TreePath path = parent.pathByAddingChild(n);
expandAll(path, expand);
}
}
if (expand) {
expandPath(parent);
} else {
collapsePath(parent);
}
}
}
private void loadTreeData(TreeNode rootNode) {
astWidget.setModel(new DefaultTreeModel(rootNode));
astWidget.expandAll(true);
}
private class ShowListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
MyPrintStream ps = new MyPrintStream();
System.setOut(ps);
TreeNode tn;
try {
SimpleNode lastCompilationUnit = getCompilationUnit();
tn = new ASTTreeNode(lastCompilationUnit);
} catch (ParseException pe) {
tn = new ExceptionNode(pe);
}
loadTreeData(tn);
}
}
private class DFAListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
DFAGraphRule dfaGraphRule = new DFAGraphRule();
RuleSet rs = new RuleSet();
SourceType sourceType = getSourceType();
if (!sourceType.equals(SourceType.JSP)){
rs.addRule(dfaGraphRule);
}
RuleContext ctx = new RuleContext();
ctx.setSourceCodeFilename("[no filename]");
StringReader reader = new StringReader(codeEditorPane.getText());
PMD pmd = new PMD();
pmd.setJavaVersion(sourceType);
try {
pmd.processFile(reader, rs, ctx);
// } catch (PMDException pmde) {
// loadTreeData(new ExceptionNode(pmde));
} catch (Exception e) {
e.printStackTrace();
}
List methods = dfaGraphRule.getMethods();
if (methods != null && !methods.isEmpty()) {
dfaPanel.resetTo(methods, codeEditorPane);
dfaPanel.repaint();
}
}
}
private class XPathListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
xpathResults.clear();
if (xpathQueryArea.getText().length() == 0) {
xpathResults.addElement("XPath query field is empty");
xpathResultList.repaint();
codeEditorPane.requestFocus();
return;
}
SimpleNode c = getCompilationUnit();
try {
XPath xpath = new BaseXPath(xpathQueryArea.getText(), new DocumentNavigator());
for (Iterator iter = xpath.selectNodes(c).iterator(); iter.hasNext();) {
StringBuffer sb = new StringBuffer();
Object obj = iter.next();
if (obj instanceof String) {
System.out.println("Result was a string: " + ((String) obj));
} else if (!(obj instanceof Boolean)) {
// if it's a Boolean and it's 'false', what does that mean?
SimpleNode node = (SimpleNode) obj;
String name = node.getClass().getName().substring(node.getClass().getName().lastIndexOf('.') + 1);
String line = " at line " + node.getBeginLine();
sb.append(name).append(line).append(PMD.EOL);
xpathResults.addElement(sb.toString().trim());
}
}
if (xpathResults.isEmpty()) {
xpathResults.addElement("No matching nodes " + System.currentTimeMillis());
}
} catch (ParseException pe) {
xpathResults.addElement(pe.fillInStackTrace().getMessage());
} catch (JaxenException je) {
xpathResults.addElement(je.fillInStackTrace().getMessage());
}
xpathResultList.repaint();
xpathQueryArea.requestFocus();
}
}
private final CodeEditorTextPane codeEditorPane = new CodeEditorTextPane();
private final ASTTreeWidget astWidget = new ASTTreeWidget(new Object[0]);
private DefaultListModel xpathResults = new DefaultListModel();
private final JList xpathResultList = new JList(xpathResults);
private final JTextArea xpathQueryArea = new JTextArea(15, 30);
private final JFrame frame = new JFrame("PMD Rule Designer");
private final DFAPanel dfaPanel = new DFAPanel();
private final JRadioButtonMenuItem[] sourceTypeMenuItems = new JRadioButtonMenuItem[sourceTypeSets.length];
public Designer() {
MatchesFunction.registerSelfInSimpleContext();
TypeOfFunction.registerSelfInSimpleContext();
xpathQueryArea.setFont(new Font("Verdana", Font.PLAIN, 16));
makeTextComponentUndoable(codeEditorPane);
JSplitPane controlSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(codeEditorPane), createXPathQueryPanel());
JSplitPane resultsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createASTPanel(), createXPathResultPanel());
JTabbedPane tabbed = new JTabbedPane();
tabbed.addTab("Abstract Syntax Tree / XPath", resultsSplitPane);
tabbed.addTab("Data Flow Analysis", dfaPanel);
try {
// Remove when minimal runtime support is >= JDK 1.4
Method setMnemonicAt = JTabbedPane.class.getMethod("setMnemonicAt", new Class[]{Integer.TYPE, Integer.TYPE});
if (setMnemonicAt != null) {
// // Compatible with >= JDK 1.4
// tabbed.setMnemonicAt(0, KeyEvent.VK_A);
// tabbed.setMnemonicAt(1, KeyEvent.VK_D);
setMnemonicAt.invoke(tabbed, new Object[]{NumericConstants.ZERO, KeyEvent.VK_A});
setMnemonicAt.invoke(tabbed, new Object[]{NumericConstants.ONE, KeyEvent.VK_D});
}
} catch (NoSuchMethodException nsme) { // Runtime is < JDK 1.4
} catch (IllegalAccessException e) { // Runtime is >= JDK 1.4 but there was an error accessing the function
e.printStackTrace();
throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
} catch (InvocationTargetException e) { // Runtime is >= JDK 1.4 but there was an error accessing the function
e.printStackTrace();
throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
}
JSplitPane containerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlSplitPane, tabbed);
containerSplitPane.setContinuousLayout(true);
JMenuBar menuBar = createMenuBar();
frame.setJMenuBar(menuBar);
frame.getContentPane().add(containerSplitPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
frame.pack();
frame.setSize((screenWidth*3/4),(screenHeight*3/4));
frame.setLocation((screenWidth -frame.getWidth()) / 2, (screenHeight - frame.getHeight()) / 2);
frame.setVisible(true);
resultsSplitPane.setDividerLocation(resultsSplitPane.getMaximumDividerLocation() - (resultsSplitPane.getMaximumDividerLocation() / 2));
containerSplitPane.setDividerLocation(containerSplitPane.getMaximumDividerLocation() / 2);
}
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("JDK");
ButtonGroup group = new ButtonGroup();
for (int i=0; i 0) {
String xml = "";
SimpleNode cu = getCompilationUnit();
if (cu != null) {
try {
xml = getXmlString(cu);
} catch (TransformerException e) {
e.printStackTrace();
xml = "Error trying to construct XML representation";
}
}
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(xml), this);
}
}
/**
* Returns an unformatted xml string (without the declaration)
*
* @throws TransformerException if the XML cannot be converted to a string
*/
private String getXmlString(SimpleNode node) throws TransformerException {
StringWriter writer = new StringWriter();
Source source = new DOMSource(node.asXml());
Result result = new StreamResult(writer);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
try {
transformerFactory.setAttribute("indent-number", 4); //For java 5
} catch (IllegalArgumentException e) {
//Running on Java 1.4 which does not support this attribute
}
Transformer xformer = transformerFactory.newTransformer();
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
xformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "4"); //For java 1.4
xformer.transform(source, result);
return writer.toString();
}
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy