Java tutorial
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O'Reilly */ // ParenMatcher.java //A simple parenthesis matcher. import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.StyledDocument; public class ParenMatcher extends JTextPane implements Runnable { public static Color[] matchColor = { Color.blue, Color.magenta, Color.green }; public static Color badColor = Color.red; private AttributeSet[] matchAttrSet; private AttributeSet badAttrSet; public ParenMatcher() { // create an array of AttributeSets from the array of Colors StyleContext sc = StyleContext.getDefaultStyleContext(); badAttrSet = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, badColor); matchAttrSet = new AttributeSet[matchColor.length]; for (int j = 0; j < matchColor.length; j += 1) matchAttrSet[j] = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, matchColor[j]); } // match and color the parens/brackets/braces public void run() { StyledDocument doc = getStyledDocument(); String text = ""; int len = doc.getLength(); try { text = doc.getText(0, len); } catch (BadLocationException ble) { } java.util.Stack stack = new java.util.Stack(); for (int j = 0; j < text.length(); j += 1) { char ch = text.charAt(j); if (ch == '(' || ch == '[' || ch == '{') { int depth = stack.size(); stack.push("" + ch + j); // push a String containg the char and // the offset AttributeSet aset = matchAttrSet[depth % matchAttrSet.length]; doc.setCharacterAttributes(j, 1, aset, false); } if (ch == ')' || ch == ']' || ch == '}') { String peek = stack.empty() ? "." : (String) stack.peek(); if (matches(peek.charAt(0), ch)) { // does it match? stack.pop(); int depth = stack.size(); AttributeSet aset = matchAttrSet[depth % matchAttrSet.length]; doc.setCharacterAttributes(j, 1, aset, false); } else { // mismatch doc.setCharacterAttributes(j, 1, badAttrSet, false); } } } while (!stack.empty()) { // anything left in the stack is a mismatch String pop = (String) stack.pop(); int offset = Integer.parseInt(pop.substring(1)); doc.setCharacterAttributes(offset, 1, badAttrSet, false); } } // unset the foreground color (if any) whenever the user enters text // (if not for this, text entered after a paren would catch the paren's // color) public void replaceSelection(String content) { getInputAttributes().removeAttribute(StyleConstants.Foreground); super.replaceSelection(content); } // return true if 'left' and 'right' are matching parens/brackets/braces public static boolean matches(char left, char right) { if (left == '(') return (right == ')'); if (left == '[') return (right == ']'); if (left == '{') return (right == '}'); return false; } public static void main(String[] args) { JFrame frame = new JFrame("ParenMatcher"); final ParenMatcher matcher = new ParenMatcher(); matcher.setText("int fact(int n) {\n" + " if (n <= 1) return 1;\n" + " return(n * fact(n-1));\n" + "}\n"); frame.getContentPane().add(new JScrollPane(matcher), BorderLayout.CENTER); JButton matchButton = new JButton("match parens"); matchButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { matcher.run(); } }); frame.getContentPane().add(matchButton, BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200, 150); frame.setVisible(true); } }