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