1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
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.StringReader;
22 import java.io.StringWriter;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.Iterator;
29 import java.util.List;
30
31 import javax.swing.AbstractAction;
32 import javax.swing.ActionMap;
33 import javax.swing.BorderFactory;
34 import javax.swing.ButtonGroup;
35 import javax.swing.DefaultListModel;
36 import javax.swing.InputMap;
37 import javax.swing.JButton;
38 import javax.swing.JComponent;
39 import javax.swing.JFrame;
40 import javax.swing.JLabel;
41 import javax.swing.JList;
42 import javax.swing.JMenu;
43 import javax.swing.JMenuBar;
44 import javax.swing.JMenuItem;
45 import javax.swing.JPanel;
46 import javax.swing.JRadioButtonMenuItem;
47 import javax.swing.JScrollPane;
48 import javax.swing.JSplitPane;
49 import javax.swing.JTabbedPane;
50 import javax.swing.JTextArea;
51 import javax.swing.JTree;
52 import javax.swing.KeyStroke;
53 import javax.swing.ListCellRenderer;
54 import javax.swing.ListSelectionModel;
55 import javax.swing.ScrollPaneConstants;
56 import javax.swing.WindowConstants;
57 import javax.swing.event.ListSelectionEvent;
58 import javax.swing.event.ListSelectionListener;
59 import javax.swing.event.TreeSelectionEvent;
60 import javax.swing.event.TreeSelectionListener;
61 import javax.swing.event.UndoableEditEvent;
62 import javax.swing.event.UndoableEditListener;
63 import javax.swing.text.JTextComponent;
64 import javax.swing.tree.DefaultMutableTreeNode;
65 import javax.swing.tree.DefaultTreeCellRenderer;
66 import javax.swing.tree.DefaultTreeModel;
67 import javax.swing.tree.TreeCellRenderer;
68 import javax.swing.tree.TreeNode;
69 import javax.swing.tree.TreePath;
70 import javax.swing.tree.TreeSelectionModel;
71 import javax.swing.undo.CannotRedoException;
72 import javax.swing.undo.CannotUndoException;
73 import javax.swing.undo.UndoManager;
74 import javax.xml.transform.OutputKeys;
75 import javax.xml.transform.Result;
76 import javax.xml.transform.Source;
77 import javax.xml.transform.Transformer;
78 import javax.xml.transform.TransformerException;
79 import javax.xml.transform.TransformerFactory;
80 import javax.xml.transform.dom.DOMSource;
81 import javax.xml.transform.stream.StreamResult;
82
83 import net.sourceforge.pmd.PMD;
84 import net.sourceforge.pmd.RuleContext;
85 import net.sourceforge.pmd.RuleSet;
86 import net.sourceforge.pmd.SourceType;
87 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
88 import net.sourceforge.pmd.ast.AccessNode;
89 import net.sourceforge.pmd.ast.Node;
90 import net.sourceforge.pmd.ast.ParseException;
91 import net.sourceforge.pmd.ast.SimpleNode;
92 import net.sourceforge.pmd.jaxen.DocumentNavigator;
93 import net.sourceforge.pmd.jaxen.MatchesFunction;
94 import net.sourceforge.pmd.jaxen.TypeOfFunction;
95 import net.sourceforge.pmd.parsers.Parser;
96 import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandler;
97 import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandlerBroker;
98 import net.sourceforge.pmd.symboltable.ClassNameDeclaration;
99 import net.sourceforge.pmd.symboltable.ClassScope;
100 import net.sourceforge.pmd.symboltable.LocalScope;
101 import net.sourceforge.pmd.symboltable.MethodNameDeclaration;
102 import net.sourceforge.pmd.symboltable.MethodScope;
103 import net.sourceforge.pmd.symboltable.NameOccurrence;
104 import net.sourceforge.pmd.symboltable.Scope;
105 import net.sourceforge.pmd.symboltable.SourceFileScope;
106 import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
107 import net.sourceforge.pmd.util.NumericConstants;
108 import net.sourceforge.pmd.util.StringUtil;
109
110 import org.jaxen.BaseXPath;
111 import org.jaxen.JaxenException;
112 import org.jaxen.XPath;
113
114 public class Designer implements ClipboardOwner {
115
116 private static final char LABEL_IMAGE_SEPARATOR = ':';
117
118 private static final Object[][] sourceTypeSets = new Object[][] {
119 { "JDK 1.3", SourceType.JAVA_13 },
120 { "JDK 1.4", SourceType.JAVA_14 },
121 { "JDK 1.5", SourceType.JAVA_15 },
122 { "JDK 1.6", SourceType.JAVA_16 },
123 { "JDK 1.7", SourceType.JAVA_17 },
124 { "JSP", SourceType.JSP }
125 };
126
127 private static final int defaultSourceTypeSelectionIndex = 2;
128
129 private SimpleNode getCompilationUnit() {
130 SourceTypeHandler handler = SourceTypeHandlerBroker.getVisitorsFactoryForSourceType(getSourceType());
131 Parser parser = handler.getParser();
132 parser.setExcludeMarker(PMD.EXCLUDE_MARKER);
133 SimpleNode simpleNode = (SimpleNode)parser.parse(new StringReader(codeEditorPane.getText()));
134 handler.getSymbolFacade().start(simpleNode);
135 handler.getTypeResolutionFacade(null).start(simpleNode);
136 return simpleNode;
137 }
138
139 private SourceType getSourceType() {
140
141 return (SourceType)sourceTypeSets[selectedSourceTypeIndex()][1];
142 }
143
144 private int selectedSourceTypeIndex() {
145 for (int i=0; i<sourceTypeMenuItems.length; i++) {
146 if (sourceTypeMenuItems[i].isSelected()) return i;
147 }
148 throw new RuntimeException("Initial default source type not specified");
149 }
150
151 private class ExceptionNode implements TreeNode {
152
153 private Object item;
154 private ExceptionNode[] kids;
155
156 public ExceptionNode(Object theItem) {
157 item = theItem;
158
159 if (item instanceof ParseException) createKids();
160 }
161
162
163 private void createKids() {
164
165 String message = ((ParseException)item).getMessage();
166 String[] lines = StringUtil.substringsOf(message, PMD.EOL);
167
168 kids = new ExceptionNode[lines.length];
169 for (int i=0; i<lines.length; i++) {
170 kids[i] = new ExceptionNode(lines[i]);
171 }
172 }
173
174 public int getChildCount() { return kids == null ? 0 : kids.length; }
175 public boolean getAllowsChildren() {return false; }
176 public boolean isLeaf() { return kids == null; }
177 public TreeNode getParent() { return null; }
178 public TreeNode getChildAt(int childIndex) { return kids[childIndex]; }
179 public String label() { return item.toString(); }
180
181 public Enumeration children() {
182 Enumeration e = new Enumeration() {
183 int i = 0;
184 public boolean hasMoreElements() {
185 return kids != null && i < kids.length;
186 }
187
188 public Object nextElement() { return kids[i++]; }
189 };
190 return e;
191 }
192
193 public int getIndex(TreeNode node) {
194 for (int i=0; i<kids.length; i++) {
195 if (kids[i] == node) return i;
196 }
197 return -1;
198 }
199 }
200
201
202
203 private class ASTTreeNode implements TreeNode {
204
205 private Node node;
206 private ASTTreeNode parent;
207 private ASTTreeNode[] kids;
208
209 public ASTTreeNode(Node theNode) {
210 node = theNode;
211
212 Node prnt = node.jjtGetParent();
213 if (prnt != null) parent = new ASTTreeNode(prnt);
214 }
215
216 public int getChildCount() { return node.jjtGetNumChildren(); }
217 public boolean getAllowsChildren() { return false; }
218 public boolean isLeaf() { return node.jjtGetNumChildren() == 0; }
219 public TreeNode getParent() { return parent; }
220
221 public Scope getScope() {
222 if (node instanceof SimpleNode)
223 return ((SimpleNode)node).getScope();
224 return null;
225 }
226
227 public Enumeration children() {
228
229 if (getChildCount() > 0) getChildAt(0);
230
231 Enumeration e = new Enumeration() {
232 int i = 0;
233 public boolean hasMoreElements() {
234 return kids != null && i < kids.length;
235 }
236 public Object nextElement() { return kids[i++]; }
237 };
238 return e;
239 }
240
241 public TreeNode getChildAt(int childIndex) {
242
243 if (kids == null) {
244 kids = new ASTTreeNode[node.jjtGetNumChildren()];
245 for (int i=0; i<kids.length; i++) {
246 kids[i] = new ASTTreeNode(node.jjtGetChild(i));
247 }
248 }
249 return kids[childIndex];
250 }
251
252 public int getIndex(TreeNode node) {
253
254 for (int i=0; i<kids.length; i++) {
255 if (kids[i] == node) return i;
256 }
257 return -1;
258 }
259
260 public String label() {
261 if (node instanceof SimpleNode) {
262 SimpleNode sn = (SimpleNode)node;
263 if (sn.getLabel() != null) {
264 return node.toString() + LABEL_IMAGE_SEPARATOR + sn.getLabel();
265 }
266 if (sn.getImage() == null) {
267 return node.toString();
268 }
269 return node.toString() + LABEL_IMAGE_SEPARATOR + sn.getImage();
270 }
271 return node.toString();
272 }
273
274 public String getToolTipText() {
275 String tooltip = "";
276 if (node instanceof SimpleNode) {
277 SimpleNode sn = (SimpleNode)node;
278 tooltip = "Line: " + sn.getBeginLine() + " Column: " + sn.getBeginColumn();
279 }
280
281 if (node instanceof AccessNode)
282 {
283 AccessNode accessNode = (AccessNode)node;
284 if ( ! "".equals(tooltip))
285 tooltip += ",";
286 tooltip += accessNode.isAbstract() ? " Abstract" : "";
287 tooltip += accessNode.isStatic() ? " Static" : "";
288 tooltip += accessNode.isFinal() ? " Final" : "";
289 tooltip += accessNode.isNative() ? " Native" : "";
290 tooltip += accessNode.isPrivate() ? " Private" : "";
291 tooltip += accessNode.isSynchronized() ? " Synchronised" : "";
292 tooltip += accessNode.isTransient() ? " Transient" : "";
293 tooltip += accessNode.isVolatile() ? " Volatile" : "";
294 tooltip += accessNode.isStrictfp() ? " Strictfp" : "";
295 }
296 return tooltip;
297 }
298 }
299
300 private TreeCellRenderer createNoImageTreeCellRenderer() {
301 DefaultTreeCellRenderer treeCellRenderer = new DefaultTreeCellRenderer();
302 treeCellRenderer.setLeafIcon(null);
303 treeCellRenderer.setOpenIcon(null);
304 treeCellRenderer.setClosedIcon(null);
305 return treeCellRenderer;
306 }
307
308
309
310 private class TreeWidget extends JTree {
311
312 private static final long serialVersionUID = 1L;
313
314 public TreeWidget(Object[] items) {
315 super(items);
316 setToolTipText("");
317 }
318
319 public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
320 if (value == null) return "";
321 if (value instanceof ASTTreeNode) {
322 return ((ASTTreeNode)value).label();
323 }
324 if (value instanceof ExceptionNode) {
325 return ((ExceptionNode)value).label();
326 }
327 return value.toString();
328 }
329
330 public String getToolTipText(MouseEvent e) {
331 if (getRowForLocation(e.getX(), e.getY()) == -1) return null;
332 TreePath curPath = getPathForLocation(e.getX(), e.getY());
333 if (curPath.getLastPathComponent() instanceof ASTTreeNode) {
334 return ((ASTTreeNode)curPath.getLastPathComponent()).getToolTipText();
335 } else {
336 return super.getToolTipText(e);
337 }
338 }
339
340 public void expandAll(boolean expand) {
341 TreeNode root = (TreeNode)getModel().getRoot();
342 expandAll(new TreePath(root), expand);
343 }
344
345 private void expandAll(TreePath parent, boolean expand) {
346
347 TreeNode node = (TreeNode)parent.getLastPathComponent();
348 if (node.getChildCount() >= 0) {
349 for (Enumeration e=node.children(); e.hasMoreElements(); ) {
350 TreeNode n = (TreeNode)e.nextElement();
351 TreePath path = parent.pathByAddingChild(n);
352 expandAll(path, expand);
353 }
354 }
355
356 if (expand) {
357 expandPath(parent);
358 } else {
359 collapsePath(parent);
360 }
361 }
362 }
363
364 private void loadASTTreeData(TreeNode rootNode) {
365 astTreeWidget.setModel(new DefaultTreeModel(rootNode));
366 astTreeWidget.expandAll(true);
367 }
368
369 private void loadSymbolTableTreeData(TreeNode rootNode) {
370 if (rootNode != null) {
371 symbolTableTreeWidget.setModel(new DefaultTreeModel(rootNode));
372 symbolTableTreeWidget.expandAll(true);
373 } else {
374 symbolTableTreeWidget.setModel(null);
375 }
376 }
377
378 private class ShowListener implements ActionListener {
379 public void actionPerformed(ActionEvent ae) {
380 MyPrintStream ps = new MyPrintStream();
381 System.setOut(ps);
382 TreeNode tn;
383 try {
384 SimpleNode lastCompilationUnit = getCompilationUnit();
385 tn = new ASTTreeNode(lastCompilationUnit);
386 } catch (ParseException pe) {
387 tn = new ExceptionNode(pe);
388 }
389
390 loadASTTreeData(tn);
391 loadSymbolTableTreeData(null);
392 }
393 }
394
395 private class DFAListener implements ActionListener {
396 public void actionPerformed(ActionEvent ae) {
397
398 DFAGraphRule dfaGraphRule = new DFAGraphRule();
399 RuleSet rs = new RuleSet();
400 SourceType sourceType = getSourceType();
401 if (!sourceType.equals(SourceType.JSP)){
402 rs.addRule(dfaGraphRule);
403 }
404 RuleContext ctx = new RuleContext();
405 ctx.setSourceCodeFilename("[no filename]");
406 StringReader reader = new StringReader(codeEditorPane.getText());
407 PMD pmd = new PMD();
408 pmd.setJavaVersion(sourceType);
409
410 try {
411 pmd.processFile(reader, rs, ctx);
412
413
414 } catch (Exception e) {
415 e.printStackTrace();
416 }
417
418 List<ASTMethodDeclaration> methods = dfaGraphRule.getMethods();
419 if (methods != null && !methods.isEmpty()) {
420 dfaPanel.resetTo(methods, codeEditorPane);
421 dfaPanel.repaint();
422 }
423 }
424 }
425
426 private class XPathListener implements ActionListener {
427 public void actionPerformed(ActionEvent ae) {
428 xpathResults.clear();
429 if (xpathQueryArea.getText().length() == 0) {
430 xpathResults.addElement("XPath query field is empty.");
431 xpathResultList.repaint();
432 codeEditorPane.requestFocus();
433 return;
434 }
435 SimpleNode c = getCompilationUnit();
436 try {
437 XPath xpath = new BaseXPath(xpathQueryArea.getText(), new DocumentNavigator());
438 for (Iterator iter = xpath.selectNodes(c).iterator(); iter.hasNext();) {
439 Object obj = iter.next();
440 if (obj instanceof String) {
441 System.out.println("Result was a string: " + ((String) obj));
442 } else if (!(obj instanceof Boolean)) {
443
444 xpathResults.addElement(obj);
445 }
446 }
447 if (xpathResults.isEmpty()) {
448 xpathResults.addElement("No matching nodes " + System.currentTimeMillis());
449 }
450 } catch (ParseException pe) {
451 xpathResults.addElement(pe.fillInStackTrace().getMessage());
452 } catch (JaxenException je) {
453 xpathResults.addElement(je.fillInStackTrace().getMessage());
454 }
455 xpathResultList.repaint();
456 xpathQueryArea.requestFocus();
457 }
458 }
459
460 private class SymbolTableListener implements TreeSelectionListener {
461 public void valueChanged(TreeSelectionEvent e) {
462 if (e.getNewLeadSelectionPath() != null) {
463 ASTTreeNode astTreeNode = (ASTTreeNode)e.getNewLeadSelectionPath().getLastPathComponent();
464
465 DefaultMutableTreeNode symbolTableTreeNode = new DefaultMutableTreeNode();
466 DefaultMutableTreeNode selectedAstTreeNode = new DefaultMutableTreeNode("AST Node: " + astTreeNode.label());
467 symbolTableTreeNode.add(selectedAstTreeNode);
468
469 List<Scope> scopes = new ArrayList<Scope>();
470 Scope scope = astTreeNode.getScope();
471 while (scope != null)
472 {
473 scopes.add(scope);
474 scope = scope.getParent();
475 }
476 Collections.reverse(scopes);
477 for (int i = 0; i < scopes.size(); i++) {
478 scope = scopes.get(i);
479 DefaultMutableTreeNode scopeTreeNode = new DefaultMutableTreeNode("Scope: " + scope.getClass().getSimpleName());
480 selectedAstTreeNode.add(scopeTreeNode);
481 if (!(scope instanceof MethodScope || scope instanceof LocalScope)) {
482 if (!scope.getClassDeclarations().isEmpty()) {
483 for (ClassNameDeclaration classNameDeclaration: scope.getClassDeclarations().keySet()) {
484 DefaultMutableTreeNode classNameDeclarationTreeNode = new DefaultMutableTreeNode("Class name declaration: " + classNameDeclaration);
485 scopeTreeNode.add(classNameDeclarationTreeNode);
486 for (NameOccurrence nameOccurrence: scope.getClassDeclarations().get(classNameDeclaration)) {
487 DefaultMutableTreeNode nameOccurenceTreeNode = new DefaultMutableTreeNode("Name occurrence: " + nameOccurrence);
488 classNameDeclarationTreeNode.add(nameOccurenceTreeNode);
489 }
490 }
491 }
492 }
493 if (scope instanceof ClassScope) {
494 ClassScope classScope = (ClassScope)scope;
495 if (!classScope.getMethodDeclarations().isEmpty()) {
496 for (MethodNameDeclaration methodNameDeclaration: classScope.getMethodDeclarations().keySet()) {
497 DefaultMutableTreeNode methodNameDeclarationTreeNode = new DefaultMutableTreeNode("Method name declaration: " + methodNameDeclaration);
498 scopeTreeNode.add(methodNameDeclarationTreeNode);
499 for (NameOccurrence nameOccurrence: classScope.getMethodDeclarations().get(methodNameDeclaration)) {
500 DefaultMutableTreeNode nameOccurenceTreeNode = new DefaultMutableTreeNode("Name occurrence: " + nameOccurrence);
501 methodNameDeclarationTreeNode.add(nameOccurenceTreeNode);
502 }
503 }
504 }
505 }
506 if (!(scope instanceof SourceFileScope)) {
507 if (!scope.getVariableDeclarations().isEmpty()) {
508 for (VariableNameDeclaration variableNameDeclaration: scope.getVariableDeclarations().keySet()) {
509 DefaultMutableTreeNode variableNameDeclarationTreeNode = new DefaultMutableTreeNode("Variable name declaration: " + variableNameDeclaration);
510 scopeTreeNode.add(variableNameDeclarationTreeNode);
511 for (NameOccurrence nameOccurrence: scope.getVariableDeclarations().get(variableNameDeclaration)) {
512 DefaultMutableTreeNode nameOccurenceTreeNode = new DefaultMutableTreeNode("Name occurrence: " + nameOccurrence);
513 variableNameDeclarationTreeNode.add(nameOccurenceTreeNode);
514 }
515 }
516 }
517 }
518 }
519 loadSymbolTableTreeData(symbolTableTreeNode);
520 }
521 }
522 }
523
524 private class CodeHighlightListener implements TreeSelectionListener {
525 public void valueChanged(TreeSelectionEvent e) {
526 if (e.getNewLeadSelectionPath() != null) {
527 ASTTreeNode selected = (ASTTreeNode)e.getNewLeadSelectionPath().getLastPathComponent();
528 if (selected != null && selected.node instanceof SimpleNode) {
529 SimpleNode node = (SimpleNode) selected.node;
530
531 codeEditorPane.select(node);
532 }
533 }
534 }
535 }
536
537 private class ASTListCellRenderer extends JLabel implements ListCellRenderer {
538 private static final long serialVersionUID = 1L;
539
540 public Component getListCellRendererComponent(
541 JList list,
542 Object value,
543 int index,
544 boolean isSelected,
545 boolean cellHasFocus) {
546
547 if (isSelected) {
548 setBackground(list.getSelectionBackground());
549 setForeground(list.getSelectionForeground());
550 } else {
551 setBackground(list.getBackground());
552 setForeground(list.getForeground());
553 }
554
555 String text;
556 if (value instanceof SimpleNode) {
557 SimpleNode node = (SimpleNode) value;
558 StringBuffer sb = new StringBuffer();
559 String name = node.getClass().getName().substring(node.getClass().getName().lastIndexOf('.') + 1);
560 sb.append(name)
561 .append(" at line ").append(node.getBeginLine())
562 .append(" column ").append(node.getBeginColumn())
563 .append(PMD.EOL);
564 text = sb.toString();
565 } else {
566 text = value.toString();
567 }
568 setText(text);
569 return this;
570 }
571 }
572
573 private class ASTSelectionListener implements ListSelectionListener {
574 public void valueChanged(ListSelectionEvent e) {
575 ListSelectionModel lsm = (ListSelectionModel)e.getSource();
576 if (!lsm.isSelectionEmpty()) {
577 Object o = xpathResults.get(lsm.getMinSelectionIndex());
578 if (o instanceof SimpleNode) {
579 codeEditorPane.select((SimpleNode) o);
580 }
581 }
582 }
583 }
584
585 private boolean exitOnClose = true;
586 private final CodeEditorTextPane codeEditorPane = new CodeEditorTextPane();
587 private final TreeWidget astTreeWidget = new TreeWidget(new Object[0]);
588 private DefaultListModel xpathResults = new DefaultListModel();
589 private final JList xpathResultList = new JList(xpathResults);
590 private final JTextArea xpathQueryArea = new JTextArea(15, 30);
591 private final TreeWidget symbolTableTreeWidget = new TreeWidget(new Object[0]);
592 private final JFrame frame = new JFrame("PMD Rule Designer (v " + PMD.VERSION + ')');
593 private final DFAPanel dfaPanel = new DFAPanel();
594 private final JRadioButtonMenuItem[] sourceTypeMenuItems = new JRadioButtonMenuItem[sourceTypeSets.length];
595
596 public Designer(String[] args) {
597 if (args.length > 0) {
598 exitOnClose = !args[0].equals("-noexitonclose");
599 }
600
601 MatchesFunction.registerSelfInSimpleContext();
602 TypeOfFunction.registerSelfInSimpleContext();
603
604 xpathQueryArea.setFont(new Font("Verdana", Font.PLAIN, 16));
605 JSplitPane controlSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createCodeEditorPanel(), createXPathQueryPanel());
606
607 JSplitPane astAndSymbolTablePane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, createASTPanel(), createSymbolTableResultPanel());
608
609 JSplitPane resultsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, astAndSymbolTablePane, createXPathResultPanel());
610
611 JTabbedPane tabbed = new JTabbedPane();
612 tabbed.addTab("Abstract Syntax Tree / XPath / Symbol Table", resultsSplitPane);
613 tabbed.addTab("Data Flow Analysis", dfaPanel);
614 try {
615
616 Method setMnemonicAt = JTabbedPane.class.getMethod("setMnemonicAt", new Class[]{Integer.TYPE, Integer.TYPE});
617 if (setMnemonicAt != null) {
618
619
620
621 setMnemonicAt.invoke(tabbed, new Object[]{NumericConstants.ZERO, KeyEvent.VK_A});
622 setMnemonicAt.invoke(tabbed, new Object[]{NumericConstants.ONE, KeyEvent.VK_D});
623 }
624 } catch (NoSuchMethodException nsme) {
625 } catch (IllegalAccessException e) {
626 e.printStackTrace();
627 throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
628 } catch (IllegalArgumentException e) {
629 e.printStackTrace();
630 throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
631 } catch (InvocationTargetException e) {
632 e.printStackTrace();
633 throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
634 }
635
636 JSplitPane containerSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlSplitPane, tabbed);
637 containerSplitPane.setContinuousLayout(true);
638
639 JMenuBar menuBar = createMenuBar();
640 frame.setJMenuBar(menuBar);
641 frame.getContentPane().add(containerSplitPane);
642 frame.setDefaultCloseOperation(exitOnClose ? JFrame.EXIT_ON_CLOSE : JFrame.DISPOSE_ON_CLOSE);
643
644 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
645 int screenHeight = screenSize.height;
646 int screenWidth = screenSize.width;
647
648 frame.pack();
649 frame.setSize((screenWidth*3/4),(screenHeight*3/4));
650 frame.setLocation((screenWidth -frame.getWidth()) / 2, (screenHeight - frame.getHeight()) / 2);
651 frame.setVisible(true);
652 int horozontalMiddleLocation = controlSplitPane.getMaximumDividerLocation() * 3 / 5;
653 controlSplitPane.setDividerLocation(horozontalMiddleLocation);
654 containerSplitPane.setDividerLocation(containerSplitPane.getMaximumDividerLocation() / 2);
655 astAndSymbolTablePane.setDividerLocation(astAndSymbolTablePane.getMaximumDividerLocation()/3);
656 resultsSplitPane.setDividerLocation(horozontalMiddleLocation);
657 }
658
659 private JMenuBar createMenuBar() {
660 JMenuBar menuBar = new JMenuBar();
661 JMenu menu = new JMenu("JDK");
662 ButtonGroup group = new ButtonGroup();
663
664 for (int i=0; i<sourceTypeSets.length; i++) {
665 JRadioButtonMenuItem button = new JRadioButtonMenuItem(sourceTypeSets[i][0].toString());
666 sourceTypeMenuItems[i] = button;
667 group.add(button);
668 menu.add(button);
669 }
670 sourceTypeMenuItems[defaultSourceTypeSelectionIndex].setSelected(true);
671 menuBar.add(menu);
672
673 JMenu actionsMenu = new JMenu("Actions");
674 JMenuItem copyXMLItem = new JMenuItem("Copy xml to clipboard");
675 copyXMLItem.addActionListener(new ActionListener() {
676 public void actionPerformed(ActionEvent e) {
677 copyXmlToClipboard();
678 }
679 });
680 actionsMenu.add(copyXMLItem);
681 JMenuItem createRuleXMLItem = new JMenuItem("Create rule XML");
682 createRuleXMLItem.addActionListener(new ActionListener() {
683 public void actionPerformed(ActionEvent e) {
684 createRuleXML();
685 }
686 });
687 actionsMenu.add(createRuleXMLItem);
688 menuBar.add(actionsMenu);
689
690 return menuBar;
691 }
692
693 private void createRuleXML() {
694 CreateXMLRulePanel rulePanel = new CreateXMLRulePanel(xpathQueryArea, codeEditorPane);
695 JFrame xmlframe = new JFrame("Create XML Rule");
696 xmlframe.setContentPane(rulePanel);
697 xmlframe.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
698 xmlframe.setSize(new Dimension(600, 700));
699 xmlframe.addComponentListener(new java.awt.event.ComponentAdapter() {
700 public void componentResized(ComponentEvent e) {
701 JFrame tmp = (JFrame)e.getSource();
702 if (tmp.getWidth()<600 || tmp.getHeight()<700) {
703 tmp.setSize(600, 700);
704 }
705 }
706 });
707 int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
708 int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
709 xmlframe.pack();
710 xmlframe.setLocation((screenWidth - xmlframe.getWidth()) / 2, (screenHeight - xmlframe.getHeight()) / 2);
711 xmlframe.setVisible(true);
712 }
713
714 private JComponent createCodeEditorPanel()
715 {
716 JPanel p = new JPanel();
717 p.setLayout(new BorderLayout());
718 codeEditorPane.setBorder(BorderFactory.createLineBorder(Color.black));
719 makeTextComponentUndoable(codeEditorPane);
720
721 p.add(new JLabel("Source code:"), BorderLayout.NORTH);
722 p.add(new JScrollPane(codeEditorPane), BorderLayout.CENTER);
723
724 return p;
725 }
726
727 private JComponent createASTPanel() {
728 astTreeWidget.setCellRenderer(createNoImageTreeCellRenderer());
729 TreeSelectionModel model = astTreeWidget.getSelectionModel();
730 model.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
731 model.addTreeSelectionListener(new SymbolTableListener());
732 model.addTreeSelectionListener(new CodeHighlightListener());
733 return new JScrollPane(astTreeWidget);
734 }
735
736 private JComponent createXPathResultPanel() {
737 xpathResults.addElement("No XPath results yet, run an XPath Query first.");
738 xpathResultList.setBorder(BorderFactory.createLineBorder(Color.black));
739 xpathResultList.setFixedCellWidth(300);
740 xpathResultList.setCellRenderer(new ASTListCellRenderer());
741 xpathResultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
742 xpathResultList.getSelectionModel().addListSelectionListener(new ASTSelectionListener());
743 JScrollPane scrollPane = new JScrollPane();
744 scrollPane.getViewport().setView(xpathResultList);
745 return scrollPane;
746 }
747
748 private JPanel createXPathQueryPanel() {
749 JPanel p = new JPanel();
750 p.setLayout(new BorderLayout());
751 xpathQueryArea.setBorder(BorderFactory.createLineBorder(Color.black));
752 makeTextComponentUndoable(xpathQueryArea);
753 JScrollPane scrollPane = new JScrollPane(xpathQueryArea);
754 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
755 scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
756 final JButton b = createGoButton();
757
758 p.add(new JLabel("XPath Query (if any):"), BorderLayout.NORTH);
759 p.add(scrollPane, BorderLayout.CENTER);
760 p.add(b, BorderLayout.SOUTH);
761
762 return p;
763 }
764
765 private JComponent createSymbolTableResultPanel() {
766 symbolTableTreeWidget.setCellRenderer(createNoImageTreeCellRenderer());
767 return new JScrollPane(symbolTableTreeWidget);
768 }
769
770 private JButton createGoButton() {
771 JButton b = new JButton("Go");
772 b.setMnemonic('g');
773 b.addActionListener(new ShowListener());
774 b.addActionListener(codeEditorPane);
775 b.addActionListener(new XPathListener());
776 b.addActionListener(new DFAListener());
777 return b;
778 }
779
780 private static void makeTextComponentUndoable(JTextComponent textConponent) {
781 final UndoManager undoManager = new UndoManager();
782 textConponent.getDocument().addUndoableEditListener(new UndoableEditListener() {
783 public void undoableEditHappened(
784 UndoableEditEvent evt) {
785 undoManager.addEdit(evt.getEdit());
786 }
787 });
788 ActionMap actionMap = textConponent.getActionMap();
789 InputMap inputMap = textConponent.getInputMap();
790 actionMap.put("Undo", new AbstractAction("Undo") {
791 public void actionPerformed(ActionEvent evt) {
792 try {
793 if (undoManager.canUndo()) {
794 undoManager.undo();
795 }
796 } catch (CannotUndoException e) {
797 }
798 }
799 });
800 inputMap.put(KeyStroke.getKeyStroke("control Z"), "Undo");
801
802 actionMap.put("Redo", new AbstractAction("Redo") {
803 public void actionPerformed(ActionEvent evt) {
804 try {
805 if (undoManager.canRedo()) {
806 undoManager.redo();
807 }
808 } catch (CannotRedoException e) {
809 }
810 }
811 });
812 inputMap.put(KeyStroke.getKeyStroke("control Y"), "Redo");
813 }
814
815 public static void main(String[] args) {
816 new Designer(args);
817 }
818
819 private final void copyXmlToClipboard() {
820 if (codeEditorPane.getText() != null && codeEditorPane.getText().trim().length() > 0) {
821 String xml = "";
822 SimpleNode cu = getCompilationUnit();
823 if (cu != null) {
824 try {
825 xml = getXmlString(cu);
826 } catch (TransformerException e) {
827 e.printStackTrace();
828 xml = "Error trying to construct XML representation";
829 }
830 }
831 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(xml), this);
832 }
833 }
834
835 /**
836 * Returns an unformatted xml string (without the declaration)
837 *
838 * @throws TransformerException if the XML cannot be converted to a string
839 */
840 private String getXmlString(SimpleNode node) throws TransformerException {
841 StringWriter writer = new StringWriter();
842
843 Source source = new DOMSource(node.asXml());
844 Result result = new StreamResult(writer);
845 TransformerFactory transformerFactory = TransformerFactory.newInstance();
846 try {
847 transformerFactory.setAttribute("indent-number", 4);
848 } catch (IllegalArgumentException e) {
849
850 }
851 Transformer xformer = transformerFactory.newTransformer();
852 xformer.setOutputProperty(OutputKeys.INDENT, "yes");
853 xformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "4"); //For java 1.4
854 xformer.transform(source, result);
855
856 return writer.toString();
857 }
858
859 public void lostOwnership(Clipboard clipboard, Transferable contents) {
860 }
861 }
862