View Javadoc

    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 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; // JDK 1.4
    97
    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     	// each line in the error message becomes a separate tree node
    129     	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     // Tree node that wraps the AST node for the tree widget and
    168     // any possible children they may have.
    169     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);	// force it to build kids
    190
    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     // Create our own renderer to ensure we don't show the default icon
    236     // and render any node image data in a different colour to hightlight
    237     // it.
    238
    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     	// overwrite the image data (if present) in a different colour
    254     	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     // Special tree variant that knows how to retrieve node labels and
    276     // provides the ability to expand all nodes at once.
    277
    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             // Traverse children
    302             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 (PMDException pmde) {
    358 //               loadTreeData(new ExceptionNode(pmde));
    359            } 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                         // if it's a Boolean and it's 'false', what does that mean?
    390                         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             // Remove when minimal runtime support is >= JDK 1.4
    432             Method setMnemonicAt = JTabbedPane.class.getMethod("setMnemonicAt", new Class[]{Integer.TYPE, Integer.TYPE});
    433             if (setMnemonicAt != null) {
    434                 //        // Compatible with >= JDK 1.4
    435                 //        tabbed.setMnemonicAt(0, KeyEvent.VK_A);
    436                 //        tabbed.setMnemonicAt(1, KeyEvent.VK_D);
    437                 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) { // Runtime is < JDK 1.4
    441         } catch (IllegalAccessException e) { // Runtime is >= JDK 1.4 but there was an error accessing the function
    442             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) { // Runtime is >= JDK 1.4 but there was an error accessing the function
    448             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     /***
    624      * Returns an unformatted xml string (without the declaration)
    625      *
    626      * @param node
    627      * @return String
    628      * @throws java.io.IOException
    629      */
    630     private String getXmlString(SimpleNode node) throws IOException {
    631 /*
    632         StringWriter writer = new StringWriter();
    633         XMLSerializer xmlSerializer = new XMLSerializer(writer, new OutputFormat("XML", "UTF-8", true));
    634         xmlSerializer.asDOMSerializer();
    635         xmlSerializer.serialize(node.asXml());
    636         return writer.toString();
    637 */
    638         return "FIXME"; // FIXME
    639     }
    640
    641     public void lostOwnership(Clipboard clipboard, Transferable contents) {
    642     }
    643 }
    644