1
2
3
4 package net.sourceforge.pmd.util.designer;
5
6 import java.awt.BorderLayout;
7 import java.awt.Color;
8 import java.awt.Component;
9 import java.awt.Dimension;
10 import java.awt.Font;
11 import java.awt.Toolkit;
12 import java.awt.datatransfer.Clipboard;
13 import java.awt.datatransfer.ClipboardOwner;
14 import java.awt.datatransfer.StringSelection;
15 import java.awt.datatransfer.Transferable;
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.awt.event.ComponentEvent;
19 import java.awt.event.KeyEvent;
20 import java.awt.event.MouseEvent;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.io.StringReader;
26 import java.io.StringWriter;
27 import java.lang.reflect.Proxy;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Map;
35
36 import javax.swing.AbstractAction;
37 import javax.swing.AbstractButton;
38 import javax.swing.ActionMap;
39 import javax.swing.BorderFactory;
40 import javax.swing.ButtonGroup;
41 import javax.swing.DefaultListModel;
42 import javax.swing.InputMap;
43 import javax.swing.JButton;
44 import javax.swing.JComponent;
45 import javax.swing.JFrame;
46 import javax.swing.JLabel;
47 import javax.swing.JList;
48 import javax.swing.JMenu;
49 import javax.swing.JMenuBar;
50 import javax.swing.JMenuItem;
51 import javax.swing.JPanel;
52 import javax.swing.JRadioButton;
53 import javax.swing.JRadioButtonMenuItem;
54 import javax.swing.JScrollPane;
55 import javax.swing.JSplitPane;
56 import javax.swing.JTabbedPane;
57 import javax.swing.JTextArea;
58 import javax.swing.JTree;
59 import javax.swing.KeyStroke;
60 import javax.swing.ListCellRenderer;
61 import javax.swing.ListSelectionModel;
62 import javax.swing.ScrollPaneConstants;
63 import javax.swing.WindowConstants;
64 import javax.swing.event.ListSelectionEvent;
65 import javax.swing.event.ListSelectionListener;
66 import javax.swing.event.TreeSelectionEvent;
67 import javax.swing.event.TreeSelectionListener;
68 import javax.swing.event.UndoableEditEvent;
69 import javax.swing.event.UndoableEditListener;
70 import javax.swing.text.JTextComponent;
71 import javax.swing.tree.DefaultMutableTreeNode;
72 import javax.swing.tree.DefaultTreeCellRenderer;
73 import javax.swing.tree.DefaultTreeModel;
74 import javax.swing.tree.TreeCellRenderer;
75 import javax.swing.tree.TreeNode;
76 import javax.swing.tree.TreePath;
77 import javax.swing.tree.TreeSelectionModel;
78 import javax.swing.undo.CannotRedoException;
79 import javax.swing.undo.CannotUndoException;
80 import javax.swing.undo.UndoManager;
81 import javax.xml.parsers.DocumentBuilder;
82 import javax.xml.parsers.DocumentBuilderFactory;
83 import javax.xml.parsers.ParserConfigurationException;
84 import javax.xml.transform.OutputKeys;
85 import javax.xml.transform.Result;
86 import javax.xml.transform.Source;
87 import javax.xml.transform.Transformer;
88 import javax.xml.transform.TransformerException;
89 import javax.xml.transform.TransformerFactory;
90 import javax.xml.transform.dom.DOMSource;
91 import javax.xml.transform.stream.StreamResult;
92
93 import net.sourceforge.pmd.PMD;
94 import net.sourceforge.pmd.PMDConfiguration;
95 import net.sourceforge.pmd.RuleContext;
96 import net.sourceforge.pmd.RuleSet;
97 import net.sourceforge.pmd.RuleSets;
98 import net.sourceforge.pmd.SourceCodeProcessor;
99 import net.sourceforge.pmd.lang.LanguageRegistry;
100 import net.sourceforge.pmd.lang.LanguageVersion;
101 import net.sourceforge.pmd.lang.LanguageVersionHandler;
102 import net.sourceforge.pmd.lang.Parser;
103 import net.sourceforge.pmd.lang.ast.Node;
104 import net.sourceforge.pmd.lang.ast.ParseException;
105 import net.sourceforge.pmd.lang.ast.xpath.Attribute;
106 import net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator;
107 import net.sourceforge.pmd.lang.dfa.DFAGraphMethod;
108 import net.sourceforge.pmd.lang.dfa.DFAGraphRule;
109 import net.sourceforge.pmd.lang.rule.XPathRule;
110 import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
111 import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
112 import net.sourceforge.pmd.lang.symboltable.Scope;
113 import net.sourceforge.pmd.lang.symboltable.ScopedNode;
114 import net.sourceforge.pmd.lang.xpath.Initializer;
115 import net.sourceforge.pmd.util.StringUtil;
116
117 import org.w3c.dom.Document;
118 import org.w3c.dom.Element;
119 import org.w3c.dom.Text;
120 import org.xml.sax.SAXException;
121
122 public class Designer implements ClipboardOwner {
123
124 private int getDefaultLanguageVersionSelectionIndex() {
125 return Arrays.asList(getSupportedLanguageVersions())
126 .indexOf(LanguageRegistry.getLanguage("Java").getDefaultVersion());
127 }
128
129 private Node getCompilationUnit() {
130 LanguageVersionHandler languageVersionHandler = getLanguageVersionHandler();
131 return getCompilationUnit(languageVersionHandler);
132 }
133 static Node getCompilationUnit(LanguageVersionHandler languageVersionHandler, String code) {
134 Parser parser = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions());
135 Node node = parser.parse(null, new StringReader(code));
136 languageVersionHandler.getSymbolFacade().start(node);
137 languageVersionHandler.getTypeResolutionFacade(Designer.class.getClassLoader()).start(node);
138 return node;
139 }
140 private Node getCompilationUnit(LanguageVersionHandler languageVersionHandler) {
141 return getCompilationUnit(languageVersionHandler, codeEditorPane.getText());
142 }
143
144 private static LanguageVersion[] getSupportedLanguageVersions() {
145 List<LanguageVersion> languageVersions = new ArrayList<LanguageVersion>();
146 for (LanguageVersion languageVersion : LanguageRegistry.findAllVersions()) {
147 LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
148 if (languageVersionHandler != null) {
149 Parser parser = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions());
150 if (parser != null && parser.canParse()) {
151 languageVersions.add(languageVersion);
152 }
153 }
154 }
155 return languageVersions.toArray(new LanguageVersion[languageVersions.size()]);
156 }
157
158 private LanguageVersion getLanguageVersion() {
159 return getSupportedLanguageVersions()[selectedLanguageVersionIndex()];
160 }
161
162 private void setLanguageVersion(LanguageVersion languageVersion) {
163 if (languageVersion != null) {
164 LanguageVersion[] versions = getSupportedLanguageVersions();
165 for (int i = 0; i < versions.length; i++) {
166 LanguageVersion version = versions[i];
167 if (languageVersion.equals(version)) {
168 languageVersionMenuItems[i].setSelected(true);
169 break;
170 }
171 }
172 }
173 }
174
175 private int selectedLanguageVersionIndex() {
176 for (int i = 0; i < languageVersionMenuItems.length; i++) {
177 if (languageVersionMenuItems[i].isSelected()) {
178 return i;
179 }
180 }
181 throw new RuntimeException("Initial default language version not specified");
182 }
183
184 private LanguageVersionHandler getLanguageVersionHandler() {
185 LanguageVersion languageVersion = getLanguageVersion();
186 return languageVersion.getLanguageVersionHandler();
187 }
188
189 private class ExceptionNode implements TreeNode {
190
191 private Object item;
192 private ExceptionNode[] kids;
193
194 public ExceptionNode(Object theItem) {
195 item = theItem;
196
197 if (item instanceof ParseException) {
198 createKids();
199 }
200 }
201
202
203 private void createKids() {
204
205 String message = ((ParseException) item).getMessage();
206 String[] lines = StringUtil.substringsOf(message, PMD.EOL);
207
208 kids = new ExceptionNode[lines.length];
209 for (int i = 0; i < lines.length; i++) {
210 kids[i] = new ExceptionNode(lines[i]);
211 }
212 }
213
214 public int getChildCount() {
215 return kids == null ? 0 : kids.length;
216 }
217
218 public boolean getAllowsChildren() {
219 return false;
220 }
221
222 public boolean isLeaf() {
223 return kids == null;
224 }
225
226 public TreeNode getParent() {
227 return null;
228 }
229
230 public TreeNode getChildAt(int childIndex) {
231 return kids[childIndex];
232 }
233
234 public String label() {
235 return item.toString();
236 }
237
238 public Enumeration<TreeNode> children() {
239 Enumeration<TreeNode> e = new Enumeration<TreeNode>() {
240 int i = 0;
241
242 public boolean hasMoreElements() {
243 return kids != null && i < kids.length;
244 }
245
246 public ExceptionNode nextElement() {
247 return kids[i++];
248 }
249 };
250 return e;
251 }
252
253 public int getIndex(TreeNode node) {
254 for (int i = 0; i < kids.length; i++) {
255 if (kids[i] == node) {
256 return i;
257 }
258 }
259 return -1;
260 }
261 }
262
263
264
265 private class ASTTreeNode implements TreeNode {
266
267 private Node node;
268 private ASTTreeNode parent;
269 private ASTTreeNode[] kids;
270
271 public ASTTreeNode(Node theNode) {
272 node = theNode;
273
274 Node parent = node.jjtGetParent();
275 if (parent != null) {
276 this.parent = new ASTTreeNode(parent);
277 }
278 }
279
280 private ASTTreeNode(ASTTreeNode parent, Node theNode) {
281 node = theNode;
282 this.parent = parent;
283 }
284
285 public int getChildCount() {
286 return node.jjtGetNumChildren();
287 }
288
289 public boolean getAllowsChildren() {
290 return false;
291 }
292
293 public boolean isLeaf() {
294 return node.jjtGetNumChildren() == 0;
295 }
296
297 public TreeNode getParent() {
298 return parent;
299 }
300
301 public Scope getScope() {
302 if (node instanceof ScopedNode) {
303 return ((ScopedNode) node).getScope();
304 }
305 return null;
306 }
307
308 public Enumeration<TreeNode> children() {
309
310 if (getChildCount() > 0) {
311 getChildAt(0);
312 }
313
314 Enumeration<TreeNode> e = new Enumeration<TreeNode>() {
315 int i = 0;
316
317 public boolean hasMoreElements() {
318 return kids != null && i < kids.length;
319 }
320
321 public ASTTreeNode nextElement() {
322 return kids[i++];
323 }
324 };
325 return e;
326 }
327
328 public TreeNode getChildAt(int childIndex) {
329
330 if (kids == null) {
331 kids = new ASTTreeNode[node.jjtGetNumChildren()];
332 for (int i = 0; i < kids.length; i++) {
333 kids[i] = new ASTTreeNode(this.parent, node.jjtGetChild(i));
334 }
335 }
336 return kids[childIndex];
337 }
338
339 public int getIndex(TreeNode node) {
340
341 for (int i = 0; i < kids.length; i++) {
342 if (kids[i] == node) {
343 return i;
344 }
345 }
346 return -1;
347 }
348
349 public String label() {
350 LanguageVersionHandler languageVersionHandler = getLanguageVersionHandler();
351 StringWriter writer = new StringWriter();
352 languageVersionHandler.getDumpFacade(writer, "", false).start(node);
353 return writer.toString();
354 }
355
356 public String getToolTipText() {
357 String tooltip = "Line: " + node.getBeginLine() + " Column: " + node.getBeginColumn();
358 tooltip += " " + label();
359 return tooltip;
360 }
361
362 public List<String> getAttributes() {
363 List<String> result = new LinkedList<String>();
364 AttributeAxisIterator attributeAxisIterator = new AttributeAxisIterator(node);
365 while (attributeAxisIterator.hasNext()) {
366 Attribute attribute = attributeAxisIterator.next();
367 result.add(attribute.getName() + "=" + attribute.getStringValue());
368 }
369 return result;
370 }
371 }
372
373 private TreeCellRenderer createNoImageTreeCellRenderer() {
374 DefaultTreeCellRenderer treeCellRenderer = new DefaultTreeCellRenderer();
375 treeCellRenderer.setLeafIcon(null);
376 treeCellRenderer.setOpenIcon(null);
377 treeCellRenderer.setClosedIcon(null);
378 return treeCellRenderer;
379 }
380
381
382
383 private class TreeWidget extends JTree {
384
385 private static final long serialVersionUID = 1L;
386
387 public TreeWidget(Object[] items) {
388 super(items);
389 setToolTipText("");
390 }
391
392 @Override
393 public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row,
394 boolean hasFocus) {
395 if (value == null) {
396 return "";
397 }
398 if (value instanceof ASTTreeNode) {
399 return ((ASTTreeNode) value).label();
400 }
401 if (value instanceof ExceptionNode) {
402 return ((ExceptionNode) value).label();
403 }
404 return value.toString();
405 }
406
407 @Override
408 public String getToolTipText(MouseEvent e) {
409 if (getRowForLocation(e.getX(), e.getY()) == -1) {
410 return null;
411 }
412 TreePath curPath = getPathForLocation(e.getX(), e.getY());
413 if (curPath.getLastPathComponent() instanceof ASTTreeNode) {
414 return ((ASTTreeNode) curPath.getLastPathComponent()).getToolTipText();
415 } else {
416 return super.getToolTipText(e);
417 }
418 }
419
420 public void expandAll(boolean expand) {
421 TreeNode root = (TreeNode) getModel().getRoot();
422 expandAll(new TreePath(root), expand);
423 }
424
425 private void expandAll(TreePath parent, boolean expand) {
426
427 TreeNode node = (TreeNode) parent.getLastPathComponent();
428 if (node.getChildCount() >= 0) {
429 for (Enumeration<? extends TreeNode> e = node.children(); e.hasMoreElements();) {
430 TreeNode n = e.nextElement();
431 TreePath path = parent.pathByAddingChild(n);
432 expandAll(path, expand);
433 }
434 }
435
436 if (expand) {
437 expandPath(parent);
438 } else {
439 collapsePath(parent);
440 }
441 }
442 }
443
444 private void loadASTTreeData(TreeNode rootNode) {
445 astTreeWidget.setModel(new DefaultTreeModel(rootNode));
446 astTreeWidget.setRootVisible(true);
447 astTreeWidget.expandAll(true);
448 }
449
450 private void loadSymbolTableTreeData(TreeNode rootNode) {
451 if (rootNode != null) {
452 symbolTableTreeWidget.setModel(new DefaultTreeModel(rootNode));
453 symbolTableTreeWidget.expandAll(true);
454 } else {
455 symbolTableTreeWidget.setModel(null);
456 }
457 }
458
459 private class ShowListener implements ActionListener {
460 public void actionPerformed(ActionEvent ae) {
461 TreeNode tn;
462 try {
463 Node lastCompilationUnit = getCompilationUnit();
464 tn = new ASTTreeNode(lastCompilationUnit);
465 } catch (ParseException pe) {
466 tn = new ExceptionNode(pe);
467 }
468
469 loadASTTreeData(tn);
470 loadSymbolTableTreeData(null);
471 }
472 }
473
474 private class DFAListener implements ActionListener {
475 public void actionPerformed(ActionEvent ae) {
476
477 LanguageVersion languageVersion = getLanguageVersion();
478 DFAGraphRule dfaGraphRule = languageVersion.getLanguageVersionHandler().getDFAGraphRule();
479 RuleSet rs = new RuleSet();
480 if (dfaGraphRule != null) {
481 rs.addRule(dfaGraphRule);
482 }
483 RuleContext ctx = new RuleContext();
484 ctx.setSourceCodeFilename("[no filename]." + languageVersion.getLanguage().getExtensions().get(0));
485 StringReader reader = new StringReader(codeEditorPane.getText());
486 PMDConfiguration config = new PMDConfiguration();
487 config.setDefaultLanguageVersion(languageVersion);
488
489 try {
490 new SourceCodeProcessor(config).processSourceCode(reader, new RuleSets(rs), ctx);
491
492
493 } catch (Exception e) {
494 e.printStackTrace();
495 }
496
497 if (dfaGraphRule != null) {
498 List<DFAGraphMethod> methods = dfaGraphRule.getMethods();
499 if (methods != null && !methods.isEmpty()) {
500 dfaPanel.resetTo(methods, codeEditorPane);
501 dfaPanel.repaint();
502 }
503 }
504 }
505 }
506
507 private class XPathListener implements ActionListener {
508 public void actionPerformed(ActionEvent ae) {
509 xpathResults.clear();
510 if (StringUtil.isEmpty(xpathQueryArea.getText())) {
511 xpathResults.addElement("XPath query field is empty.");
512 xpathResultList.repaint();
513 codeEditorPane.requestFocus();
514 return;
515 }
516 Node c = getCompilationUnit();
517 try {
518 XPathRule xpathRule = new XPathRule() {
519 @Override
520 public void addViolation(Object data, Node node, String arg) {
521 xpathResults.addElement(node);
522 }
523 };
524 xpathRule.setMessage("");
525 xpathRule.setLanguage(getLanguageVersion().getLanguage());
526 xpathRule.setXPath(xpathQueryArea.getText());
527 xpathRule.setVersion(xpathVersionButtonGroup.getSelection().getActionCommand());
528
529 RuleSet ruleSet = new RuleSet();
530 ruleSet.addRule(xpathRule);
531
532 RuleSets ruleSets = new RuleSets(ruleSet);
533
534 RuleContext ruleContext = new RuleContext();
535 ruleContext.setLanguageVersion(getLanguageVersion());
536
537 List<Node> nodes = new ArrayList<Node>();
538 nodes.add(c);
539 ruleSets.apply(nodes, ruleContext, xpathRule.getLanguage());
540
541 if (xpathResults.isEmpty()) {
542 xpathResults.addElement("No matching nodes " + System.currentTimeMillis());
543 }
544 } catch (ParseException pe) {
545 xpathResults.addElement(pe.fillInStackTrace().getMessage());
546 }
547 xpathResultList.repaint();
548 xpathQueryArea.requestFocus();
549 }
550 }
551
552 private class SymbolTableListener implements TreeSelectionListener {
553 public void valueChanged(TreeSelectionEvent e) {
554 if (e.getNewLeadSelectionPath() != null) {
555 ASTTreeNode astTreeNode = (ASTTreeNode) e.getNewLeadSelectionPath().getLastPathComponent();
556
557 DefaultMutableTreeNode symbolTableTreeNode = new DefaultMutableTreeNode();
558 DefaultMutableTreeNode selectedAstTreeNode = new DefaultMutableTreeNode("AST Node: "
559 + astTreeNode.label());
560 symbolTableTreeNode.add(selectedAstTreeNode);
561
562 List<Scope> scopes = new ArrayList<Scope>();
563 Scope scope = astTreeNode.getScope();
564 while (scope != null) {
565 scopes.add(scope);
566 scope = scope.getParent();
567 }
568 Collections.reverse(scopes);
569 for (int i = 0; i < scopes.size(); i++) {
570 scope = scopes.get(i);
571 DefaultMutableTreeNode scopeTreeNode = new DefaultMutableTreeNode("Scope: "
572 + scope.getClass().getSimpleName());
573 selectedAstTreeNode.add(scopeTreeNode);
574 for (Map.Entry<NameDeclaration, List<NameOccurrence>> entry : scope.getDeclarations().entrySet()) {
575 DefaultMutableTreeNode nameDeclarationTreeNode = new DefaultMutableTreeNode(
576 entry.getKey().getClass().getSimpleName() + ": " + entry.getKey());
577 scopeTreeNode.add(nameDeclarationTreeNode);
578 for (NameOccurrence nameOccurrence : entry.getValue()) {
579 DefaultMutableTreeNode nameOccurranceTreeNode = new DefaultMutableTreeNode(
580 "Name occurrence: " + nameOccurrence);
581 nameDeclarationTreeNode.add(nameOccurranceTreeNode);
582 }
583 }
584 }
585
586 List<String> attributes = astTreeNode.getAttributes();
587 DefaultMutableTreeNode attributesNode = new DefaultMutableTreeNode("Attributes (accessible via XPath):");
588 selectedAstTreeNode.add(attributesNode);
589 for (String attribute : attributes) {
590 attributesNode.add(new DefaultMutableTreeNode(attribute));
591 }
592
593 loadSymbolTableTreeData(symbolTableTreeNode);
594 }
595 }
596 }
597
598 private class CodeHighlightListener implements TreeSelectionListener {
599 public void valueChanged(TreeSelectionEvent e) {
600 if (e.getNewLeadSelectionPath() != null) {
601 ASTTreeNode selected = (ASTTreeNode) e.getNewLeadSelectionPath().getLastPathComponent();
602 if (selected != null) {
603 codeEditorPane.select(selected.node);
604 }
605 }
606 }
607 }
608
609 private class ASTListCellRenderer extends JLabel implements ListCellRenderer {
610 private static final long serialVersionUID = 1L;
611
612 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
613 boolean cellHasFocus) {
614
615 if (isSelected) {
616 setBackground(list.getSelectionBackground());
617 setForeground(list.getSelectionForeground());
618 } else {
619 setBackground(list.getBackground());
620 setForeground(list.getForeground());
621 }
622
623 String text;
624 if (value instanceof Node) {
625 Node node = (Node) value;
626 StringBuffer sb = new StringBuffer();
627 String name = node.getClass().getName().substring(node.getClass().getName().lastIndexOf('.') + 1);
628 if (Proxy.isProxyClass(value.getClass())) {
629 name = value.toString();
630 }
631 sb.append(name).append(" at line ").append(node.getBeginLine()).append(" column ").append(
632 node.getBeginColumn()).append(PMD.EOL);
633 text = sb.toString();
634 } else {
635 text = value.toString();
636 }
637 setText(text);
638 return this;
639 }
640 }
641
642 private class ASTSelectionListener implements ListSelectionListener {
643 public void valueChanged(ListSelectionEvent e) {
644 ListSelectionModel lsm = (ListSelectionModel) e.getSource();
645 if (!lsm.isSelectionEmpty()) {
646 Object o = xpathResults.get(lsm.getMinSelectionIndex());
647 if (o instanceof Node) {
648 codeEditorPane.select((Node) o);
649 }
650 }
651 }
652 }
653
654 private boolean exitOnClose = true;
655 private final CodeEditorTextPane codeEditorPane = new CodeEditorTextPane();
656 private final TreeWidget astTreeWidget = new TreeWidget(new Object[0]);
657 private DefaultListModel xpathResults = new DefaultListModel();
658 private final JList xpathResultList = new JList(xpathResults);
659 private final JTextArea xpathQueryArea = new JTextArea(15, 30);
660 private final ButtonGroup xpathVersionButtonGroup = new ButtonGroup();
661 private final TreeWidget symbolTableTreeWidget = new TreeWidget(new Object[0]);
662 private final JFrame frame = new JFrame("PMD Rule Designer (v " + PMD.VERSION + ')');
663 private final DFAPanel dfaPanel = new DFAPanel();
664 private final JRadioButtonMenuItem[] languageVersionMenuItems = new JRadioButtonMenuItem[getSupportedLanguageVersions().length];
665
666 public Designer(String[] args) {
667 if (args.length > 0) {
668 exitOnClose = !args[0].equals("-noexitonclose");
669 }
670
671 Initializer.initialize();
672
673 xpathQueryArea.setFont(new Font("Verdana", Font.PLAIN, 16));
674 JSplitPane controlSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createCodeEditorPanel(),
675 createXPathQueryPanel());
676
677 JSplitPane astAndSymbolTablePane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, createASTPanel(),
678 createSymbolTableResultPanel());
679
680 JSplitPane resultsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, astAndSymbolTablePane,
681 createXPathResultPanel());
682
683 JTabbedPane tabbed = new JTabbedPane();
684 tabbed.addTab("Abstract Syntax Tree / XPath / Symbol Table", resultsSplitPane);
685 tabbed.addTab("Data Flow Analysis", dfaPanel);
686 tabbed.setMnemonicAt(0, KeyEvent.VK_A);
687 tabbed.setMnemonicAt(1, KeyEvent.VK_D);
688
689 JSplitPane containerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlSplitPane, tabbed);
690 containerSplitPane.setContinuousLayout(true);
691
692 JMenuBar menuBar = createMenuBar();
693 frame.setJMenuBar(menuBar);
694 frame.getContentPane().add(containerSplitPane);
695 frame.setDefaultCloseOperation(exitOnClose ? JFrame.EXIT_ON_CLOSE : JFrame.DISPOSE_ON_CLOSE);
696
697 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
698 int screenHeight = screenSize.height;
699 int screenWidth = screenSize.width;
700
701 frame.pack();
702 frame.setSize(screenWidth * 3 / 4, screenHeight * 3 / 4);
703 frame.setLocation((screenWidth - frame.getWidth()) / 2, (screenHeight - frame.getHeight()) / 2);
704 frame.setVisible(true);
705 int horozontalMiddleLocation = controlSplitPane.getMaximumDividerLocation() * 3 / 5;
706 controlSplitPane.setDividerLocation(horozontalMiddleLocation);
707 containerSplitPane.setDividerLocation(containerSplitPane.getMaximumDividerLocation() / 2);
708 astAndSymbolTablePane.setDividerLocation(astAndSymbolTablePane.getMaximumDividerLocation() / 3);
709 resultsSplitPane.setDividerLocation(horozontalMiddleLocation);
710
711 loadSettings();
712 }
713
714 private JMenuBar createMenuBar() {
715 JMenuBar menuBar = new JMenuBar();
716 JMenu menu = new JMenu("Language");
717 ButtonGroup group = new ButtonGroup();
718
719 LanguageVersion[] languageVersions = getSupportedLanguageVersions();
720 for (int i = 0; i < languageVersions.length; i++) {
721 LanguageVersion languageVersion = languageVersions[i];
722 JRadioButtonMenuItem button = new JRadioButtonMenuItem(languageVersion.getShortName());
723 languageVersionMenuItems[i] = button;
724 group.add(button);
725 menu.add(button);
726 }
727 languageVersionMenuItems[getDefaultLanguageVersionSelectionIndex()].setSelected(true);
728 menuBar.add(menu);
729
730 JMenu actionsMenu = new JMenu("Actions");
731 JMenuItem copyXMLItem = new JMenuItem("Copy xml to clipboard");
732 copyXMLItem.addActionListener(new ActionListener() {
733 public void actionPerformed(ActionEvent e) {
734 copyXmlToClipboard();
735 }
736 });
737 actionsMenu.add(copyXMLItem);
738 JMenuItem createRuleXMLItem = new JMenuItem("Create rule XML");
739 createRuleXMLItem.addActionListener(new ActionListener() {
740 public void actionPerformed(ActionEvent e) {
741 createRuleXML();
742 }
743 });
744 actionsMenu.add(createRuleXMLItem);
745 menuBar.add(actionsMenu);
746
747 return menuBar;
748 }
749
750 private void createRuleXML() {
751 CreateXMLRulePanel rulePanel = new CreateXMLRulePanel(xpathQueryArea, codeEditorPane);
752 JFrame xmlframe = new JFrame("Create XML Rule");
753 xmlframe.setContentPane(rulePanel);
754 xmlframe.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
755 xmlframe.setSize(new Dimension(600, 700));
756 xmlframe.addComponentListener(new java.awt.event.ComponentAdapter() {
757 @Override
758 public void componentResized(ComponentEvent e) {
759 JFrame tmp = (JFrame) e.getSource();
760 if (tmp.getWidth() < 600 || tmp.getHeight() < 700) {
761 tmp.setSize(600, 700);
762 }
763 }
764 });
765 int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
766 int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
767 xmlframe.pack();
768 xmlframe.setLocation((screenWidth - xmlframe.getWidth()) / 2, (screenHeight - xmlframe.getHeight()) / 2);
769 xmlframe.setVisible(true);
770 }
771
772 private JComponent createCodeEditorPanel() {
773 JPanel p = new JPanel();
774 p.setLayout(new BorderLayout());
775 codeEditorPane.setBorder(BorderFactory.createLineBorder(Color.black));
776 makeTextComponentUndoable(codeEditorPane);
777
778 p.add(new JLabel("Source code:"), BorderLayout.NORTH);
779 p.add(new JScrollPane(codeEditorPane), BorderLayout.CENTER);
780
781 return p;
782 }
783
784 private JComponent createASTPanel() {
785 astTreeWidget.setCellRenderer(createNoImageTreeCellRenderer());
786 TreeSelectionModel model = astTreeWidget.getSelectionModel();
787 model.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
788 model.addTreeSelectionListener(new SymbolTableListener());
789 model.addTreeSelectionListener(new CodeHighlightListener());
790 return new JScrollPane(astTreeWidget);
791 }
792
793 private JComponent createXPathResultPanel() {
794 xpathResults.addElement("No XPath results yet, run an XPath Query first.");
795 xpathResultList.setBorder(BorderFactory.createLineBorder(Color.black));
796 xpathResultList.setFixedCellWidth(300);
797 xpathResultList.setCellRenderer(new ASTListCellRenderer());
798 xpathResultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
799 xpathResultList.getSelectionModel().addListSelectionListener(new ASTSelectionListener());
800 JScrollPane scrollPane = new JScrollPane();
801 scrollPane.getViewport().setView(xpathResultList);
802 return scrollPane;
803 }
804
805 private JPanel createXPathQueryPanel() {
806 JPanel p = new JPanel();
807 p.setLayout(new BorderLayout());
808 xpathQueryArea.setBorder(BorderFactory.createLineBorder(Color.black));
809 makeTextComponentUndoable(xpathQueryArea);
810 JScrollPane scrollPane = new JScrollPane(xpathQueryArea);
811 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
812 scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
813 final JButton b = createGoButton();
814
815 JPanel topPanel = new JPanel();
816 topPanel.setLayout(new BorderLayout());
817 topPanel.add(new JLabel("XPath Query (if any):"), BorderLayout.WEST);
818 topPanel.add(createXPathVersionPanel(), BorderLayout.EAST);
819
820 p.add(topPanel, BorderLayout.NORTH);
821 p.add(scrollPane, BorderLayout.CENTER);
822 p.add(b, BorderLayout.SOUTH);
823
824 return p;
825 }
826
827 private JComponent createSymbolTableResultPanel() {
828 symbolTableTreeWidget.setCellRenderer(createNoImageTreeCellRenderer());
829 return new JScrollPane(symbolTableTreeWidget);
830 }
831
832 private JPanel createXPathVersionPanel() {
833 JPanel p = new JPanel();
834 p.add(new JLabel("XPath Version:"));
835 for (Object[] values : XPathRule.VERSION_DESCRIPTOR.choices()) {
836 JRadioButton b = new JRadioButton();
837 b.setText((String) values[0]);
838 b.setActionCommand(b.getText());
839 if (values[0].equals(XPathRule.VERSION_DESCRIPTOR.defaultValue())) {
840 b.setSelected(true);
841 }
842 xpathVersionButtonGroup.add(b);
843 p.add(b);
844 }
845 return p;
846 }
847
848 private JButton createGoButton() {
849 JButton b = new JButton("Go");
850 b.setMnemonic('g');
851 b.addActionListener(new ShowListener());
852 b.addActionListener(new XPathListener());
853 b.addActionListener(new DFAListener());
854 b.addActionListener(new ActionListener() {
855 public void actionPerformed(ActionEvent e) {
856 saveSettings();
857 }
858 });
859 return b;
860 }
861
862 private static void makeTextComponentUndoable(JTextComponent textConponent) {
863 final UndoManager undoManager = new UndoManager();
864 textConponent.getDocument().addUndoableEditListener(new UndoableEditListener() {
865 public void undoableEditHappened(UndoableEditEvent evt) {
866 undoManager.addEdit(evt.getEdit());
867 }
868 });
869 ActionMap actionMap = textConponent.getActionMap();
870 InputMap inputMap = textConponent.getInputMap();
871 actionMap.put("Undo", new AbstractAction("Undo") {
872 public void actionPerformed(ActionEvent evt) {
873 try {
874 if (undoManager.canUndo()) {
875 undoManager.undo();
876 }
877 } catch (CannotUndoException e) {
878 }
879 }
880 });
881 inputMap.put(KeyStroke.getKeyStroke("control Z"), "Undo");
882
883 actionMap.put("Redo", new AbstractAction("Redo") {
884 public void actionPerformed(ActionEvent evt) {
885 try {
886 if (undoManager.canRedo()) {
887 undoManager.redo();
888 }
889 } catch (CannotRedoException e) {
890 }
891 }
892 });
893 inputMap.put(KeyStroke.getKeyStroke("control Y"), "Redo");
894 }
895
896 public static void main(String[] args) {
897 new Designer(args);
898 }
899
900 final void setCodeEditPaneText(String text) {
901 codeEditorPane.setText(text);
902 }
903
904 private final String getXmlTreeCode() {
905 if (codeEditorPane.getText() != null && codeEditorPane.getText().trim().length() > 0) {
906 Node cu = getCompilationUnit();
907 return getXmlTreeCode(cu);
908 }
909 return null;
910 }
911 static final String getXmlTreeCode(Node cu) {
912 String xml = null;
913 if (cu != null) {
914 try {
915 xml = getXmlString(cu);
916 } catch (TransformerException e) {
917 e.printStackTrace();
918 xml = "Error trying to construct XML representation";
919 }
920 }
921 return xml;
922 }
923
924 private final void copyXmlToClipboard() {
925 String xml = getXmlTreeCode();
926 if (xml != null) {
927 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(xml), this);
928 }
929 }
930
931
932
933
934
935
936 private static String getXmlString(Node node) throws TransformerException {
937 StringWriter writer = new StringWriter();
938
939 Source source = new DOMSource(node.getAsDocument());
940 Result result = new StreamResult(writer);
941 TransformerFactory transformerFactory = TransformerFactory.newInstance();
942 Transformer xformer = transformerFactory.newTransformer();
943 xformer.setOutputProperty(OutputKeys.INDENT, "yes");
944 xformer.transform(source, result);
945
946 return writer.toString();
947 }
948
949 public void lostOwnership(Clipboard clipboard, Transferable contents) {
950 }
951
952 private static final String SETTINGS_FILE_NAME = System.getProperty("user.home")
953 + System.getProperty("file.separator") + ".pmd_designer.xml";
954
955 private void loadSettings() {
956 try {
957 File file = new File(SETTINGS_FILE_NAME);
958 if (file.exists()) {
959 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
960 Document document = builder.parse(new FileInputStream(file));
961 Element settingsElement = document.getDocumentElement();
962 Element codeElement = (Element) settingsElement.getElementsByTagName("code").item(0);
963 Element xpathElement = (Element) settingsElement.getElementsByTagName("xpath").item(0);
964
965 String code = getTextContext(codeElement);
966 String languageVersion = codeElement.getAttribute("language-version");
967 String xpath = getTextContext(xpathElement);
968 String xpathVersion = xpathElement.getAttribute("version");
969
970 codeEditorPane.setText(code);
971 setLanguageVersion(LanguageRegistry.findLanguageVersionByTerseName(languageVersion));
972 xpathQueryArea.setText(xpath);
973 for (Enumeration<AbstractButton> e = xpathVersionButtonGroup.getElements(); e.hasMoreElements();) {
974 AbstractButton button = e.nextElement();
975 if (xpathVersion.equals(button.getActionCommand())) {
976 button.setSelected(true);
977 break;
978 }
979 }
980 }
981 } catch (ParserConfigurationException e) {
982 e.printStackTrace();
983 } catch (IOException e) {
984 e.printStackTrace();
985 } catch (SAXException e) {
986 e.printStackTrace();
987 }
988 }
989
990 private void saveSettings() {
991 try {
992 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
993 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
994 Document document = documentBuilder.newDocument();
995
996 Element settingsElement = document.createElement("settings");
997 document.appendChild(settingsElement);
998
999 Element codeElement = document.createElement("code");
1000 settingsElement.appendChild(codeElement);
1001 codeElement.setAttribute("language-version", getLanguageVersion().getTerseName());
1002 codeElement.appendChild(document.createCDATASection(codeEditorPane.getText()));
1003
1004 Element xpathElement = document.createElement("xpath");
1005 settingsElement.appendChild(xpathElement);
1006 xpathElement.setAttribute("version", xpathVersionButtonGroup.getSelection().getActionCommand());
1007 xpathElement.appendChild(document.createCDATASection(xpathQueryArea.getText()));
1008
1009 TransformerFactory transformerFactory = TransformerFactory.newInstance();
1010 Transformer transformer = transformerFactory.newTransformer();
1011 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
1012
1013 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
1014 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
1015 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
1016
1017 Source source = new DOMSource(document);
1018 Result result = new StreamResult(new FileWriter(new File(SETTINGS_FILE_NAME)));
1019 transformer.transform(source, result);
1020 } catch (ParserConfigurationException e) {
1021 e.printStackTrace();
1022 } catch (IOException e) {
1023 e.printStackTrace();
1024 } catch (TransformerException e) {
1025 e.printStackTrace();
1026 }
1027 }
1028
1029 private String getTextContext(Element element) {
1030 StringBuilder buf = new StringBuilder();
1031 for (int i = 0; i < element.getChildNodes().getLength(); i++) {
1032 org.w3c.dom.Node child = element.getChildNodes().item(i);
1033 if (child instanceof Text) {
1034 buf.append(((Text)child).getData());
1035 }
1036 }
1037 return buf.toString();
1038 }
1039 }