1 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.TargetJDK1_6; 14 import net.sourceforge.pmd.ast.Node; 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 import net.sourceforge.pmd.util.NumericConstants; 22 import net.sourceforge.pmd.util.StringUtil; 23 import org.jaxen.BaseXPath; 24 import org.jaxen.JaxenException; 25 import org.jaxen.XPath; 26 27 import javax.swing.*; 28 import javax.swing.event.*; 29 import javax.swing.text.JTextComponent ; 30 import javax.swing.tree.DefaultTreeCellRenderer ; 31 import javax.swing.tree.DefaultTreeModel ; 32 import javax.swing.tree.TreeNode ; 33 import javax.swing.tree.TreePath ; 34 import javax.swing.undo.*; 35 import java.awt.BorderLayout ; 36 import java.awt.Color ; 37 import java.awt.Component ; 38 import java.awt.Dimension ; 39 import java.awt.Font ; 40 import java.awt.FontMetrics ; 41 import java.awt.Graphics ; 42 import java.awt.Toolkit ; 43 import java.awt.datatransfer.Clipboard ; 44 import java.awt.datatransfer.ClipboardOwner ; 45 import java.awt.datatransfer.StringSelection ; 46 import java.awt.datatransfer.Transferable ; 47 import java.awt.event.ActionEvent ; 48 import java.awt.event.ActionListener ; 49 import java.awt.event.ComponentEvent ; 50 import java.awt.event.KeyEvent ; 51 import java.io.IOException ; 52 import java.io.StringReader ; 53 import java.io.StringWriter ; 54 import java.lang.reflect.InvocationTargetException ; 55 import java.lang.reflect.Method ; 56 import java.util.Enumeration ; 57 import java.util.Iterator ; 58 import java.util.List ; 59 import java.util.Vector ; 60 61 public class Designer implements ClipboardOwner { 62 63 private static final char LABEL_IMAGE_SEPARATOR = ':'; 64 private static final Color IMAGE_TEXT_COLOR = Color.BLUE; 65 66 private interface Parser { public SimpleNode parse(StringReader sr); }; 67 68 private static final Parser jdkParser1_3 = new Parser() { 69 public SimpleNode parse(StringReader sr) { return new TargetJDK1_3().createParser(sr).CompilationUnit(); }; 70 }; 71 72 private static final Parser jdkParser1_4 = new Parser() { 73 public SimpleNode parse(StringReader sr) { return new TargetJDK1_4().createParser(sr).CompilationUnit(); }; 74 }; 75 76 private static final Parser jdkParser1_5 = new Parser() { 77 public SimpleNode parse(StringReader sr) { return new TargetJDK1_5().createParser(sr).CompilationUnit(); }; 78 }; 79 80 private static final Parser jdkParser1_6 = new Parser() { 81 public SimpleNode parse(StringReader sr) { return new TargetJDK1_6().createParser(sr).CompilationUnit(); }; 82 }; 83 84 private static final Parser jspParser = new Parser() { 85 public SimpleNode parse(StringReader sr) { return new JspParser(new JspCharStream(sr)).CompilationUnit(); }; 86 }; 87 88 private static final Object [][] sourceTypeSets = new Object [][] { 89 { "JDK 1.3", SourceType.JAVA_13, jdkParser1_3 }, 90 { "JDK 1.4", SourceType.JAVA_14, jdkParser1_4 }, 91 { "JDK 1.5", SourceType.JAVA_15, jdkParser1_5 }, 92 { "JDK 1.6", SourceType.JAVA_16, jdkParser1_6 }, 93 { "JSP", SourceType.JSP, jspParser } 94 }; 95 96 private static final int defaultSourceTypeSelectionIndex = 1; 98 99 private SimpleNode getCompilationUnit() { 100 101 Parser parser = (Parser)sourceTypeSets[selectedSourceTypeIndex()][2]; 102 return parser.parse(new StringReader (codeEditorPane.getText())); 103 } 104 105 private SourceType getSourceType() { 106 107 return (SourceType)sourceTypeSets[selectedSourceTypeIndex()][1]; 108 } 109 110 private int selectedSourceTypeIndex() { 111 for (int i=0; i<sourceTypeMenuItems.length; i++) { 112 if (sourceTypeMenuItems[i].isSelected()) return i; 113 } 114 throw new RuntimeException ("Initial default source type not specified"); 115 } 116 117 private class ExceptionNode implements TreeNode { 118 119 private Object item; 120 private ExceptionNode[] kids; 121 122 public ExceptionNode(Object theItem) { 123 item = theItem; 124 125 if (item instanceof ParseException) createKids(); 126 } 127 128 private void createKids() { 130 131 String message = ((ParseException)item).getMessage(); 132 String [] lines = StringUtil.substringsOf(message, PMD.EOL); 133 134 kids = new ExceptionNode[lines.length]; 135 for (int i=0; i<lines.length; i++) { 136 kids[i] = new ExceptionNode(lines[i]); 137 } 138 } 139 140 public int getChildCount() { return kids == null ? 0 : kids.length; } 141 public boolean getAllowsChildren() {return false; } 142 public boolean isLeaf() { return kids == null; } 143 public TreeNode getParent() { return null; } 144 public TreeNode getChildAt(int childIndex) { return kids[childIndex]; } 145 public String label() { return item.toString(); } 146 147 public Enumeration children() { 148 Enumeration e = new Enumeration () { 149 int i = 0; 150 public boolean hasMoreElements() { 151 return kids != null && i < kids.length; 152 } 153 154 public Object nextElement() { return kids[i++]; } 155 }; 156 return e; 157 } 158 159 public int getIndex(TreeNode node) { 160 for (int i=0; i<kids.length; i++) { 161 if (kids[i] == node) return i; 162 } 163 return -1; 164 } 165 } 166 167 private class ASTTreeNode implements TreeNode { 170 171 private Node node; 172 private ASTTreeNode parent; 173 private ASTTreeNode[] kids; 174 175 public ASTTreeNode(Node theNode) { 176 node = theNode; 177 178 Node prnt = node.jjtGetParent(); 179 if (prnt != null) parent = new ASTTreeNode(prnt); 180 } 181 182 public int getChildCount() { return node.jjtGetNumChildren(); } 183 public boolean getAllowsChildren() { return false; } 184 public boolean isLeaf() { return node.jjtGetNumChildren() == 0; } 185 public TreeNode getParent() { return parent; } 186 187 public Enumeration children() { 188 189 if (getChildCount() > 0) getChildAt(0); 191 Enumeration e = new Enumeration () { 192 int i = 0; 193 public boolean hasMoreElements() { 194 return kids != null && i < kids.length; 195 } 196 public Object nextElement() { return kids[i++]; } 197 }; 198 return e; 199 } 200 201 public TreeNode getChildAt(int childIndex) { 202 203 if (kids == null) { 204 kids = new ASTTreeNode[node.jjtGetNumChildren()]; 205 for (int i=0; i<kids.length; i++) { 206 kids[i] = new ASTTreeNode(node.jjtGetChild(i)); 207 } 208 } 209 return kids[childIndex]; 210 } 211 212 public int getIndex(TreeNode node) { 213 214 for (int i=0; i<kids.length; i++) { 215 if (kids[i] == node) return i; 216 } 217 return -1; 218 } 219 220 public String label() { 221 if (node instanceof SimpleNode) { 222 SimpleNode sn = (SimpleNode)node; 223 if (sn.getLabel() != null) { 224 return node.toString() + LABEL_IMAGE_SEPARATOR + sn.getLabel(); 225 } 226 if (sn.getImage() == null) { 227 return node.toString(); 228 } 229 return node.toString() + LABEL_IMAGE_SEPARATOR + sn.getImage(); 230 } 231 return node.toString(); 232 } 233 } 234 235 239 private class ASTCellRenderer extends DefaultTreeCellRenderer { 240 241 private ASTTreeNode node; 242 243 public Icon getIcon() { return null; }; 244 245 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,boolean expanded,boolean leaf, int row, boolean hasFocus) { 246 247 if (value instanceof ASTTreeNode) { 248 node = (ASTTreeNode)value; 249 } 250 return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); 251 } 252 253 public void paint(Graphics g) { 255 256 super.paint(g); 257 258 if (node == null) return; 259 260 String text = node.label(); 261 int separatorPos = text.indexOf(LABEL_IMAGE_SEPARATOR); 262 if (separatorPos < 0) return; 263 264 String label = text.substring(0, separatorPos+1); 265 String image = text.substring(separatorPos+1); 266 267 FontMetrics fm = g.getFontMetrics(); 268 int width = SwingUtilities.computeStringWidth(fm, label); 269 270 g.setColor(IMAGE_TEXT_COLOR); 271 g.drawString(image, width, fm.getMaxAscent()); 272 } 273 } 274 275 278 private class ASTTreeWidget extends JTree { 279 280 public ASTTreeWidget(Vector items) { 281 super(items); 282 } 283 284 public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { 285 if (value == null) return ""; 286 if (value instanceof ASTTreeNode) { 287 return ((ASTTreeNode)value).label(); 288 } 289 if (value instanceof ExceptionNode) { 290 return ((ExceptionNode)value).label(); 291 } 292 return value.toString(); 293 } 294 295 public void expandAll(boolean expand) { 296 TreeNode root = (TreeNode )getModel().getRoot(); 297 expandAll(new TreePath (root), expand); 298 } 299 300 private void expandAll(TreePath parent, boolean expand) { 301 TreeNode node = (TreeNode )parent.getLastPathComponent(); 303 if (node.getChildCount() >= 0) { 304 for (Enumeration e=node.children(); e.hasMoreElements(); ) { 305 TreeNode n = (TreeNode )e.nextElement(); 306 TreePath path = parent.pathByAddingChild(n); 307 expandAll(path, expand); 308 } 309 } 310 311 if (expand) { 312 expandPath(parent); 313 } else { 314 collapsePath(parent); 315 } 316 } 317 } 318 319 private void loadTreeData(TreeNode rootNode) { 320 astWidget.setModel(new DefaultTreeModel (rootNode)); 321 astWidget.expandAll(true); 322 } 323 324 private class ShowListener implements ActionListener { 325 public void actionPerformed(ActionEvent ae) { 326 MyPrintStream ps = new MyPrintStream(); 327 System.setOut(ps); 328 TreeNode tn; 329 try { 330 SimpleNode lastCompilationUnit = getCompilationUnit(); 331 tn = new ASTTreeNode(lastCompilationUnit); 332 } catch (ParseException pe) { 333 tn = new ExceptionNode(pe); 334 } 335 336 loadTreeData(tn); 337 } 338 } 339 340 private class DFAListener implements ActionListener { 341 public void actionPerformed(ActionEvent ae) { 342 343 DFAGraphRule dfaGraphRule = new DFAGraphRule(); 344 RuleSet rs = new RuleSet(); 345 SourceType sourceType = getSourceType(); 346 if (!sourceType.equals(SourceType.JSP)){ 347 rs.addRule(dfaGraphRule); 348 } 349 RuleContext ctx = new RuleContext(); 350 ctx.setSourceCodeFilename("[no filename]"); 351 StringReader reader = new StringReader (codeEditorPane.getText()); 352 PMD pmd = new PMD(); 353 pmd.setJavaVersion(sourceType); 354 355 try { 356 pmd.processFile(reader, rs, ctx); 357 } catch (Exception e) { 360 e.printStackTrace(); 361 } 362 363 List methods = dfaGraphRule.getMethods(); 364 if (methods != null && !methods.isEmpty()) { 365 dfaPanel.resetTo(methods, codeEditorPane); 366 dfaPanel.repaint(); 367 } 368 } 369 } 370 371 private class XPathListener implements ActionListener { 372 public void actionPerformed(ActionEvent ae) { 373 xpathResults.clear(); 374 if (xpathQueryArea.getText().length() == 0) { 375 xpathResults.addElement("XPath query field is empty"); 376 xpathResultList.repaint(); 377 codeEditorPane.requestFocus(); 378 return; 379 } 380 SimpleNode c = getCompilationUnit(); 381 try { 382 XPath xpath = new BaseXPath(xpathQueryArea.getText(), new DocumentNavigator()); 383 for (Iterator iter = xpath.selectNodes(c).iterator(); iter.hasNext();) { 384 StringBuffer sb = new StringBuffer (); 385 Object obj = iter.next(); 386 if (obj instanceof String ) { 387 System.out.println("Result was a string: " + ((String ) obj)); 388 } else if (!(obj instanceof Boolean )) { 389 SimpleNode node = (SimpleNode) obj; 391 String name = node.getClass().getName().substring(node.getClass().getName().lastIndexOf('.') + 1); 392 String line = " at line " + node.getBeginLine(); 393 sb.append(name).append(line).append(PMD.EOL); 394 xpathResults.addElement(sb.toString().trim()); 395 } 396 } 397 if (xpathResults.isEmpty()) { 398 xpathResults.addElement("No matching nodes " + System.currentTimeMillis()); 399 } 400 } catch (ParseException pe) { 401 xpathResults.addElement(pe.fillInStackTrace().getMessage()); 402 } catch (JaxenException je) { 403 xpathResults.addElement(je.fillInStackTrace().getMessage()); 404 } 405 xpathResultList.repaint(); 406 xpathQueryArea.requestFocus(); 407 } 408 } 409 410 private final CodeEditorTextPane codeEditorPane = new CodeEditorTextPane(); 411 private final ASTTreeWidget astWidget = new ASTTreeWidget(new Vector ()); 412 private DefaultListModel xpathResults = new DefaultListModel(); 413 private final JList xpathResultList = new JList(xpathResults); 414 private final JTextArea xpathQueryArea = new JTextArea(15, 30); 415 private final JFrame frame = new JFrame("PMD Rule Designer"); 416 private final DFAPanel dfaPanel = new DFAPanel(); 417 private final JRadioButtonMenuItem[] sourceTypeMenuItems = new JRadioButtonMenuItem[sourceTypeSets.length]; 418 419 public Designer() { 420 MatchesFunction.registerSelfInSimpleContext(); 421 422 xpathQueryArea.setFont(new Font ("Verdana", Font.PLAIN, 16)); 423 makeTextComponentUndoable(codeEditorPane); 424 JSplitPane controlSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(codeEditorPane), createXPathQueryPanel()); 425 JSplitPane resultsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createASTPanel(), createXPathResultPanel()); 426 427 JTabbedPane tabbed = new JTabbedPane(); 428 tabbed.addTab("Abstract Syntax Tree / XPath", resultsSplitPane); 429 tabbed.addTab("Data Flow Analysis", dfaPanel); 430 try { 431 Method setMnemonicAt = JTabbedPane.class.getMethod("setMnemonicAt", new Class []{Integer.TYPE, Integer.TYPE}); 433 if (setMnemonicAt != null) { 434 setMnemonicAt.invoke(tabbed, new Object []{NumericConstants.ZERO, new Integer (KeyEvent.VK_A)}); 438 setMnemonicAt.invoke(tabbed, new Object []{NumericConstants.ONE, new Integer (KeyEvent.VK_D)}); 439 } 440 } catch (NoSuchMethodException nsme) { } catch (IllegalAccessException e) { e.printStackTrace(); 443 throw new InternalError ("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken."); 444 } catch (IllegalArgumentException e) { 445 e.printStackTrace(); 446 throw new InternalError ("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken."); 447 } catch (InvocationTargetException e) { e.printStackTrace(); 449 throw new InternalError ("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken."); 450 } 451 452 JSplitPane containerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlSplitPane, tabbed); 453 containerSplitPane.setContinuousLayout(true); 454 455 JMenuBar menuBar = createMenuBar(); 456 frame.setJMenuBar(menuBar); 457 frame.getContentPane().add(containerSplitPane); 458 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 459 460 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 461 int screenHeight = screenSize.height; 462 int screenWidth = screenSize.width; 463 464 frame.pack(); 465 frame.setSize((screenWidth*3/4),(screenHeight*3/4)); 466 frame.setLocation((screenWidth -frame.getWidth()) / 2, (screenHeight - frame.getHeight()) / 2); 467 frame.setVisible(true); 468 resultsSplitPane.setDividerLocation(resultsSplitPane.getMaximumDividerLocation() - (resultsSplitPane.getMaximumDividerLocation() / 2)); 469 containerSplitPane.setDividerLocation(containerSplitPane.getMaximumDividerLocation() / 2); 470 } 471 472 private JMenuBar createMenuBar() { 473 JMenuBar menuBar = new JMenuBar(); 474 JMenu menu = new JMenu("JDK"); 475 ButtonGroup group = new ButtonGroup(); 476 477 for (int i=0; i<sourceTypeSets.length; i++) { 478 JRadioButtonMenuItem button = new JRadioButtonMenuItem(sourceTypeSets[i][0].toString()); 479 sourceTypeMenuItems[i] = button; 480 group.add(button); 481 menu.add(button); 482 } 483 sourceTypeMenuItems[defaultSourceTypeSelectionIndex].setSelected(true); 484 menuBar.add(menu); 485 486 JMenu actionsMenu = new JMenu("Actions"); 487 JMenuItem copyXMLItem = new JMenuItem("Copy xml to clipboard"); 488 copyXMLItem.addActionListener(new ActionListener () { 489 public void actionPerformed(ActionEvent e) { 490 copyXmlToClipboard(); 491 } 492 }); 493 actionsMenu.add(copyXMLItem); 494 JMenuItem createRuleXMLItem = new JMenuItem("Create rule XML"); 495 createRuleXMLItem.addActionListener(new ActionListener () { 496 public void actionPerformed(ActionEvent e) { 497 createRuleXML(); 498 } 499 }); 500 actionsMenu.add(createRuleXMLItem); 501 menuBar.add(actionsMenu); 502 503 return menuBar; 504 } 505 506 private void createRuleXML() { 507 CreateXMLRulePanel rulePanel = new CreateXMLRulePanel(xpathQueryArea, codeEditorPane); 508 JFrame xmlframe = new JFrame("Create XML Rule"); 509 xmlframe.setContentPane(rulePanel); 510 xmlframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 511 xmlframe.setSize(new Dimension (600, 700)); 512 xmlframe.addComponentListener(new java.awt.event.ComponentAdapter () { 513 public void componentResized(ComponentEvent e) { 514 JFrame tmp = (JFrame)e.getSource(); 515 if (tmp.getWidth()<600 || tmp.getHeight()<700) { 516 tmp.setSize(600, 700); 517 } 518 } 519 }); 520 int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; 521 int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width; 522 xmlframe.pack(); 523 xmlframe.setLocation((screenWidth - xmlframe.getWidth()) / 2, (screenHeight - xmlframe.getHeight()) / 2); 524 xmlframe.setVisible(true); 525 } 526 527 private JComponent createASTPanel() { 528 astWidget.setCellRenderer(new ASTCellRenderer()); 529 return new JScrollPane(astWidget); 530 } 531 532 private JComponent createXPathResultPanel() { 533 xpathResults.addElement("No results yet"); 534 xpathResultList.setBorder(BorderFactory.createLineBorder(Color.black)); 535 xpathResultList.setFixedCellWidth(300); 536 JScrollPane scrollPane = new JScrollPane(); 537 scrollPane.getViewport().setView(xpathResultList); 538 return scrollPane; 539 } 540 541 private JPanel createXPathQueryPanel() { 542 JPanel p = new JPanel(); 543 p.setLayout(new BorderLayout ()); 544 xpathQueryArea.setBorder(BorderFactory.createLineBorder(Color.black)); 545 makeTextComponentUndoable(xpathQueryArea); 546 JScrollPane scrollPane = new JScrollPane(xpathQueryArea); 547 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 548 scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 549 final JButton b = createGoButton(); 550 551 p.add(new JLabel("XPath Query (if any)"), BorderLayout.NORTH); 552 p.add(scrollPane, BorderLayout.CENTER); 553 p.add(b, BorderLayout.SOUTH); 554 555 return p; 556 } 557 558 private JButton createGoButton() { 559 JButton b = new JButton("Go"); 560 b.setMnemonic('g'); 561 b.addActionListener(new ShowListener()); 562 b.addActionListener(codeEditorPane); 563 b.addActionListener(new XPathListener()); 564 b.addActionListener(new DFAListener()); 565 return b; 566 } 567 568 private static void makeTextComponentUndoable(JTextComponent textConponent) { 569 final UndoManager undoManager = new UndoManager(); 570 textConponent.getDocument().addUndoableEditListener(new UndoableEditListener() { 571 public void undoableEditHappened( 572 UndoableEditEvent evt) { 573 undoManager.addEdit(evt.getEdit()); 574 } 575 }); 576 ActionMap actionMap = textConponent.getActionMap(); 577 InputMap inputMap = textConponent.getInputMap(); 578 actionMap.put("Undo", new AbstractAction("Undo") { 579 public void actionPerformed(ActionEvent evt) { 580 try { 581 if (undoManager.canUndo()) { 582 undoManager.undo(); 583 } 584 } catch (CannotUndoException e) { 585 } 586 } 587 }); 588 inputMap.put(KeyStroke.getKeyStroke("control Z"), "Undo"); 589 590 actionMap.put("Redo", new AbstractAction("Redo") { 591 public void actionPerformed(ActionEvent evt) { 592 try { 593 if (undoManager.canRedo()) { 594 undoManager.redo(); 595 } 596 } catch (CannotRedoException e) { 597 } 598 } 599 }); 600 inputMap.put(KeyStroke.getKeyStroke("control Y"), "Redo"); 601 } 602 603 public static void main(String [] args) { 604 new Designer(); 605 } 606 607 private final void copyXmlToClipboard() { 608 if (codeEditorPane.getText() != null && codeEditorPane.getText().trim().length() > 0) { 609 String xml = ""; 610 SimpleNode cu = getCompilationUnit(); 611 if (cu != null) { 612 try { 613 xml = getXmlString(cu); 614 } catch (IOException e) { 615 e.printStackTrace(); 616 xml = "Error trying to construct XML representation"; 617 } 618 } 619 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection (xml), this); 620 } 621 } 622 623 630 private String getXmlString(SimpleNode node) throws IOException { 631 638 return "FIXME"; } 640 641 public void lostOwnership(Clipboard clipboard, Transferable contents) { 642 } 643 } 644 645 | Popular Tags |