0001: package org.dbbrowser.ui.widget;
0002:
0003: /*
0004: * A simple text editor that demonstrates the integration of the
0005: * com.Ostermiller.Syntax Syntax Highlighting package with a text editor.
0006: * Copyright (C) 2001 Stephen Ostermiller
0007: * http://ostermiller.org/contact.pl?regarding=Syntax+Highlighting
0008: *
0009: * This program is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This program is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0017: * GNU General Public License for more details.
0018: *
0019: * See COPYING.TXT for details.
0020: */
0021: import java.awt.*;
0022: import java.awt.event.*;
0023: import javax.swing.*;
0024: import javax.swing.text.*;
0025: import java.io.*;
0026: import java.util.*;
0027: import com.Ostermiller.Syntax.Lexer.*;
0028:
0029: /**
0030: * A <a href="http://ostermiller.org/syntax/editor.html">demonstration text editor</a>
0031: * that uses syntax highlighting.
0032: */
0033: public class SyntaxHighlighterPanel extends JPanel {
0034: /**
0035: * The place where the text is drawn.
0036: */
0037: protected JTextPane textPane;
0038: /**
0039: * the styled document that is the model for
0040: * the textPane
0041: */
0042: protected HighLightedDocument document;
0043: /**
0044: * A reader wrapped around the document
0045: * so that the document can be fed into
0046: * the lexer.
0047: */
0048: protected DocumentReader documentReader;
0049: /**
0050: * The lexer that tells us what colors different
0051: * words should be.
0052: */
0053: protected Lexer syntaxLexer;
0054: /**
0055: * A thread that handles the actual coloring.
0056: */
0057: protected Colorer colorer;
0058: /**
0059: * A lock for modifying the document, or for
0060: * actions that depend on the document not being
0061: * modified.
0062: */
0063: private Object doclock = new Object();
0064:
0065: /**
0066: * Create a new Demo
0067: */
0068: public SyntaxHighlighterPanel() {
0069: // initial set up that sets the title
0070: //super("Programmer's Editor Demonstration");
0071:
0072: // Create the document model.
0073: document = new HighLightedDocument();
0074:
0075: // Create the text pane and configure it.
0076: textPane = new JTextPane(document);
0077:
0078: textPane.setCaretPosition(0);
0079: textPane.setMargin(new Insets(5, 5, 5, 5));
0080: JScrollPane scrollPane = new JScrollPane(textPane);
0081:
0082: // specify the initial size and location for the window.
0083: scrollPane.setPreferredSize(new Dimension(620, 460));
0084: setLocation(50, 50);
0085:
0086: // Add the components to the frame.
0087: // JPanel contentPane = new JPanel(new BorderLayout());
0088: // contentPane.add(scrollPane, BorderLayout.CENTER);
0089: // setContentPane(contentPane);
0090: this .setLayout(new BorderLayout());
0091: this .add(scrollPane, BorderLayout.CENTER);
0092:
0093: // Set up the menu bar.
0094: JMenu styleMenu = createStyleMenu();
0095: JMenuBar mb = new JMenuBar();
0096: mb.add(styleMenu);
0097: //setJMenuBar(mb);
0098:
0099: // Make the window so that it can close the application
0100: // addWindowListener(new WindowAdapter() {
0101: // public void windowClosing(WindowEvent e) {
0102: // System.exit(0);
0103: // }
0104: // public void windowActivated(WindowEvent e) {
0105: // // focus magic
0106: // textPane.requestFocus();
0107: // }
0108: // });
0109:
0110: // Start the thread that does the coloring
0111: colorer = new Colorer();
0112: colorer.start();
0113:
0114: // Set up the hash table that contains the styles.
0115: initStyles();
0116:
0117: // create the new document.
0118: documentReader = new DocumentReader(document);
0119:
0120: // Put the initial text into the text pane and
0121: // set it's initial coloring style.
0122: initDocument();
0123:
0124: // put it all together and show it.
0125: //pack();
0126: setVisible(true);
0127: }
0128:
0129: /**
0130: * Returns the text pane
0131: * @return
0132: */
0133: public JTextPane getTextPane() {
0134: return this .textPane;
0135: }
0136:
0137: /**
0138: * Returns the selected text or the text in the text area if no text has been selected
0139: * @return
0140: */
0141: public String getText() {
0142: //Get the selected text or if there is no selection, get all the text in the text area
0143: String sql = this .textPane.getSelectedText();
0144: if (sql == null) {
0145: sql = this .textPane.getText();
0146: }
0147:
0148: //Strip the newline characters from the sql
0149: sql = sql.replaceAll("\r\n", " ");
0150: sql = sql.replaceAll("\n", " ");
0151:
0152: return sql;
0153: }
0154:
0155: /**
0156: * Append this text
0157: * @param text
0158: */
0159: public void append(String text) {
0160: this .textPane.setText(this .textPane.getText() + text);
0161: }
0162:
0163: public void setText(String text) {
0164: this .textPane.setText(text);
0165: }
0166:
0167: /**
0168: * Run the Syntax Highlighting as a separate thread.
0169: * Things that need to be colored are messaged to the
0170: * thread and put in a list.
0171: */
0172: private class Colorer extends Thread {
0173:
0174: /**
0175: * Keep a list of places in the file that it is safe to restart the
0176: * highlighting. This happens whenever the lexer reports that it has
0177: * returned to its initial state. Since this list needs to be sorted
0178: * and we need to be able to retrieve ranges from it, it is stored in a
0179: * balanced tree.
0180: */
0181: private TreeSet iniPositions = new TreeSet(
0182: new DocPositionComparator());
0183:
0184: /**
0185: * As we go through and remove invalid positions we will also be finding
0186: * new valid positions.
0187: * Since the position list cannot be deleted from and written to at the same
0188: * time, we will keep a list of the new positions and simply add it to the
0189: * list of positions once all the old positions have been removed.
0190: */
0191: private HashSet newPositions = new HashSet();
0192:
0193: /**
0194: * A simple wrapper representing something that needs to be colored.
0195: * Placed into an object so that it can be stored in a Vector.
0196: */
0197: private class RecolorEvent {
0198: public int position;
0199: public int adjustment;
0200:
0201: public RecolorEvent(int position, int adjustment) {
0202: this .position = position;
0203: this .adjustment = adjustment;
0204: }
0205: }
0206:
0207: /**
0208: * Vector that stores the communication between the two threads.
0209: */
0210: private volatile Vector v = new Vector();
0211:
0212: /**
0213: * The amount of change that has occurred before the place in the
0214: * document that we are currently highlighting (lastPosition).
0215: */
0216: private volatile int change = 0;
0217:
0218: /**
0219: * The last position colored
0220: */
0221: private volatile int lastPosition = -1;
0222:
0223: private volatile boolean asleep = false;
0224:
0225: /**
0226: * When accessing the vector, we need to create a critical section.
0227: * we will synchronize on this object to ensure that we don't get
0228: * unsafe thread behavior.
0229: */
0230: private Object lock = new Object();
0231:
0232: /**
0233: * Tell the Syntax Highlighting thread to take another look at this
0234: * section of the document. It will process this as a FIFO.
0235: * This method should be done inside a doclock.
0236: */
0237: public void color(int position, int adjustment) {
0238: // figure out if this adjustment effects the current run.
0239: // if it does, then adjust the place in the document
0240: // that gets highlighted.
0241: if (position < lastPosition) {
0242: if (lastPosition < position - adjustment) {
0243: change -= lastPosition - position;
0244: } else {
0245: change += adjustment;
0246: }
0247: }
0248: synchronized (lock) {
0249: v.add(new RecolorEvent(position, adjustment));
0250: if (asleep) {
0251: this .interrupt();
0252: }
0253: }
0254: }
0255:
0256: /**
0257: * The colorer runs forever and may sleep for long
0258: * periods of time. It should be interrupted every
0259: * time there is something for it to do.
0260: */
0261: public void run() {
0262: int position = -1;
0263: int adjustment = 0;
0264: // if we just finish, we can't go to sleep until we
0265: // ensure there is nothing else for us to do.
0266: // use try again to keep track of this.
0267: boolean tryAgain = false;
0268: for (;;) { // forever
0269: synchronized (lock) {
0270: if (v.size() > 0) {
0271: RecolorEvent re = (RecolorEvent) (v
0272: .elementAt(0));
0273: v.removeElementAt(0);
0274: position = re.position;
0275: adjustment = re.adjustment;
0276: } else {
0277: tryAgain = false;
0278: position = -1;
0279: adjustment = 0;
0280: }
0281: }
0282: if (position != -1) {
0283: SortedSet workingSet;
0284: Iterator workingIt;
0285: DocPosition startRequest = new DocPosition(position);
0286: DocPosition endRequest = new DocPosition(position
0287: + ((adjustment >= 0) ? adjustment
0288: : -adjustment));
0289: DocPosition dp;
0290: DocPosition dpStart = null;
0291: DocPosition dpEnd = null;
0292:
0293: // find the starting position. We must start at least one
0294: // token before the current position
0295: try {
0296: // all the good positions before
0297: workingSet = iniPositions.headSet(startRequest);
0298: // the last of the stuff before
0299: dpStart = ((DocPosition) workingSet.last());
0300: } catch (NoSuchElementException x) {
0301: // if there were no good positions before the requested start,
0302: // we can always start at the very beginning.
0303: dpStart = new DocPosition(0);
0304: }
0305:
0306: // if stuff was removed, take any removed positions off the list.
0307: if (adjustment < 0) {
0308: workingSet = iniPositions.subSet(startRequest,
0309: endRequest);
0310: workingIt = workingSet.iterator();
0311: while (workingIt.hasNext()) {
0312: workingIt.next();
0313: workingIt.remove();
0314: }
0315: }
0316:
0317: // adjust the positions of everything after the insertion/removal.
0318: workingSet = iniPositions.tailSet(startRequest);
0319: workingIt = workingSet.iterator();
0320: while (workingIt.hasNext()) {
0321: ((DocPosition) workingIt.next())
0322: .adjustPosition(adjustment);
0323: }
0324:
0325: // now go through and highlight as much as needed
0326: workingSet = iniPositions.tailSet(dpStart);
0327: workingIt = workingSet.iterator();
0328: dp = null;
0329: if (workingIt.hasNext()) {
0330: dp = (DocPosition) workingIt.next();
0331: }
0332: try {
0333: Token t;
0334: boolean done = false;
0335: dpEnd = dpStart;
0336: synchronized (doclock) {
0337: // we are playing some games with the lexer for efficiency.
0338: // we could just create a new lexer each time here, but instead,
0339: // we will just reset it so that it thinks it is starting at the
0340: // beginning of the document but reporting a funny start position.
0341: // Reseting the lexer causes the close() method on the reader
0342: // to be called but because the close() method has no effect on the
0343: // DocumentReader, we can do this.
0344: syntaxLexer.reset(documentReader, 0,
0345: dpStart.getPosition(), 0);
0346: // After the lexer has been set up, scroll the reader so that it
0347: // is in the correct spot as well.
0348: documentReader.seek(dpStart.getPosition());
0349: // we will highlight tokens until we reach a good stopping place.
0350: // the first obvious stopping place is the end of the document.
0351: // the lexer will return null at the end of the document and wee
0352: // need to stop there.
0353: t = syntaxLexer.getNextToken();
0354: }
0355: newPositions.add(dpStart);
0356: while (!done && t != null) {
0357: // this is the actual command that colors the stuff.
0358: // Color stuff with the description of the style matched
0359: // to the hash table that has been set up ahead of time.
0360: synchronized (doclock) {
0361: if (t.getCharEnd() <= document
0362: .getLength()) {
0363: document
0364: .setCharacterAttributes(
0365: t.getCharBegin()
0366: + change,
0367: t.getCharEnd()
0368: - t
0369: .getCharBegin(),
0370: getStyle(t
0371: .getDescription()),
0372: true);
0373: // record the position of the last bit of text that we colored
0374: dpEnd = new DocPosition(t
0375: .getCharEnd());
0376: }
0377: lastPosition = (t.getCharEnd() + change);
0378: }
0379: // The other more complicated reason for doing no more highlighting
0380: // is that all the colors are the same from here on out anyway.
0381: // We can detect this by seeing if the place that the lexer returned
0382: // to the initial state last time we highlighted is the same as the
0383: // place that returned to the initial state this time.
0384: // As long as that place is after the last changed text, everything
0385: // from there on is fine already.
0386: if (t.getState() == Token.INITIAL_STATE) {
0387: //System.out.println(t);
0388: // look at all the positions from last time that are less than or
0389: // equal to the current position
0390: while (dp != null
0391: && dp.getPosition() <= t
0392: .getCharEnd()) {
0393: if (dp.getPosition() == t
0394: .getCharEnd()
0395: && dp.getPosition() >= endRequest
0396: .getPosition()) {
0397: // we have found a state that is the same
0398: done = true;
0399: dp = null;
0400: } else if (workingIt.hasNext()) {
0401: // didn't find it, try again.
0402: dp = (DocPosition) workingIt
0403: .next();
0404: } else {
0405: // didn't find it, and there is no more info from last
0406: // time. This means that we will just continue
0407: // until the end of the document.
0408: dp = null;
0409: }
0410: }
0411: // so that we can do this check next time, record all the
0412: // initial states from this time.
0413: newPositions.add(dpEnd);
0414: }
0415: synchronized (doclock) {
0416: t = syntaxLexer.getNextToken();
0417: }
0418: }
0419:
0420: // remove all the old initial positions from the place where
0421: // we started doing the highlighting right up through the last
0422: // bit of text we touched.
0423: workingIt = iniPositions.subSet(dpStart, dpEnd)
0424: .iterator();
0425: while (workingIt.hasNext()) {
0426: workingIt.next();
0427: workingIt.remove();
0428: }
0429:
0430: // Remove all the positions that are after the end of the file.:
0431: workingIt = iniPositions.tailSet(
0432: new DocPosition(document.getLength()))
0433: .iterator();
0434: while (workingIt.hasNext()) {
0435: workingIt.next();
0436: workingIt.remove();
0437: }
0438:
0439: // and put the new initial positions that we have found on the list.
0440: iniPositions.addAll(newPositions);
0441: newPositions.clear();
0442:
0443: /*workingIt = iniPositions.iterator();
0444: while (workingIt.hasNext()){
0445: System.out.println(workingIt.next());
0446: }
0447:
0448: System.out.println("Started: " + dpStart.getPosition() + " Ended: " + dpEnd.getPosition());*/
0449: } catch (IOException x) {
0450: }
0451: synchronized (doclock) {
0452: lastPosition = -1;
0453: change = 0;
0454: }
0455: // since we did something, we should check that there is
0456: // nothing else to do before going back to sleep.
0457: tryAgain = true;
0458: }
0459: asleep = true;
0460: if (!tryAgain) {
0461: try {
0462: sleep(0xffffff);
0463: } catch (InterruptedException x) {
0464: }
0465:
0466: }
0467: asleep = false;
0468: }
0469: }
0470: }
0471:
0472: /**
0473: * Color or recolor the entire document
0474: */
0475: public void colorAll() {
0476: color(0, document.getLength());
0477: }
0478:
0479: /**
0480: * Color a section of the document.
0481: * The actual coloring will start somewhere before
0482: * the requested position and continue as long
0483: * as needed.
0484: *
0485: * @param position the starting point for the coloring.
0486: * @param adjustment amount of text inserted or removed
0487: * at the starting point.
0488: */
0489: public void color(int position, int adjustment) {
0490: colorer.color(position, adjustment);
0491: }
0492:
0493: /**
0494: * Create the style menu.
0495: *
0496: * @return the style menu.
0497: */
0498: private JMenu createStyleMenu() {
0499: ActionListener actionListener = new ActionListener() {
0500: public void actionPerformed(ActionEvent e) {
0501: if (e.getActionCommand().equals("Java")) {
0502: syntaxLexer = new JavaLexer(documentReader);
0503: colorAll();
0504: } else if (e.getActionCommand().equals("C/C++")) {
0505: syntaxLexer = new CLexer(documentReader);
0506: colorAll();
0507: } else if (e.getActionCommand().equals("LaTeX")) {
0508: syntaxLexer = new LatexLexer(documentReader);
0509: colorAll();
0510: } else if (e.getActionCommand().equals("SQL")) {
0511: syntaxLexer = new SQLLexer(documentReader);
0512: colorAll();
0513: } else if (e.getActionCommand().equals(
0514: "Java Properties")) {
0515: syntaxLexer = new PropertiesLexer(documentReader);
0516: colorAll();
0517: } else if (e.getActionCommand().equals("HTML (Simple)")) {
0518: syntaxLexer = new HTMLLexer(documentReader);
0519: colorAll();
0520: } else if (e.getActionCommand()
0521: .equals("HTML (Complex)")) {
0522: syntaxLexer = new HTMLLexer1(documentReader);
0523: colorAll();
0524: } else if (e.getActionCommand().equals("Gray Out")) {
0525: SimpleAttributeSet style = new SimpleAttributeSet();
0526: StyleConstants.setFontFamily(style, "Monospaced");
0527: StyleConstants.setFontSize(style, 12);
0528: StyleConstants.setBackground(style, Color.gray);
0529: StyleConstants.setForeground(style, Color.black);
0530: StyleConstants.setBold(style, false);
0531: StyleConstants.setItalic(style, false);
0532: document.setCharacterAttributes(0, document
0533: .getLength(), style, true);
0534: }
0535: }
0536: };
0537:
0538: JMenu menu = new JMenu("Style");
0539: ButtonGroup group = new ButtonGroup();
0540: JRadioButtonMenuItem item;
0541: item = new JRadioButtonMenuItem("Java", true);
0542: group.add(item);
0543: item.addActionListener(actionListener);
0544: menu.add(item);
0545: item = new JRadioButtonMenuItem("C/C++", false);
0546: group.add(item);
0547: item.addActionListener(actionListener);
0548: menu.add(item);
0549: item = new JRadioButtonMenuItem("HTML (Simple)", false);
0550: group.add(item);
0551: item.addActionListener(actionListener);
0552: menu.add(item);
0553: item = new JRadioButtonMenuItem("HTML (Complex)", false);
0554: group.add(item);
0555: item.addActionListener(actionListener);
0556: menu.add(item);
0557: item = new JRadioButtonMenuItem("LaTeX", false);
0558: group.add(item);
0559: item.addActionListener(actionListener);
0560: menu.add(item);
0561: item = new JRadioButtonMenuItem("SQL", false);
0562: group.add(item);
0563: item.addActionListener(actionListener);
0564: menu.add(item);
0565: item = new JRadioButtonMenuItem("Java Properties", false);
0566: group.add(item);
0567: item.addActionListener(actionListener);
0568: menu.add(item);
0569:
0570: JMenuItem i = new JMenuItem("Gray Out");
0571: i.addActionListener(actionListener);
0572: menu.add(i);
0573:
0574: return menu;
0575: }
0576:
0577: /**
0578: * Initialize the document with some default text and set
0579: * they initial type of syntax highlighting.
0580: */
0581: private void initDocument() {
0582: syntaxLexer = new SQLLexer(documentReader);
0583:
0584: try {
0585: document.insertString(document.getLength(), "",
0586: getStyle("text"));
0587: } catch (BadLocationException ble) {
0588: System.err.println("Couldn't insert initial text.");
0589: }
0590: }
0591:
0592: /**
0593: * A hash table containing the text styles.
0594: * Simple attribute sets are hashed by name (String)
0595: */
0596: private Hashtable styles = new Hashtable();
0597:
0598: /**
0599: * retrieve the style for the given type of text.
0600: *
0601: * @param styleName the label for the type of text ("tag" for example)
0602: * or null if the styleName is not known.
0603: * @return the style
0604: */
0605: private SimpleAttributeSet getStyle(String styleName) {
0606: return ((SimpleAttributeSet) styles.get(styleName));
0607: }
0608:
0609: /**
0610: * Create the styles and place them in the hash table.
0611: */
0612: private void initStyles() {
0613: SimpleAttributeSet style;
0614:
0615: style = new SimpleAttributeSet();
0616: StyleConstants.setFontFamily(style, "Monospaced");
0617: StyleConstants.setFontSize(style, 12);
0618: StyleConstants.setBackground(style, Color.white);
0619: StyleConstants.setForeground(style, Color.black);
0620: StyleConstants.setBold(style, false);
0621: StyleConstants.setItalic(style, false);
0622: styles.put("body", style);
0623:
0624: style = new SimpleAttributeSet();
0625: StyleConstants.setFontFamily(style, "Monospaced");
0626: StyleConstants.setFontSize(style, 12);
0627: StyleConstants.setBackground(style, Color.white);
0628: StyleConstants.setForeground(style, Color.blue);
0629: StyleConstants.setBold(style, true);
0630: StyleConstants.setItalic(style, false);
0631: styles.put("tag", style);
0632:
0633: style = new SimpleAttributeSet();
0634: StyleConstants.setFontFamily(style, "Monospaced");
0635: StyleConstants.setFontSize(style, 12);
0636: StyleConstants.setBackground(style, Color.white);
0637: StyleConstants.setForeground(style, Color.blue);
0638: StyleConstants.setBold(style, false);
0639: StyleConstants.setItalic(style, false);
0640: styles.put("endtag", style);
0641:
0642: style = new SimpleAttributeSet();
0643: StyleConstants.setFontFamily(style, "Monospaced");
0644: StyleConstants.setFontSize(style, 12);
0645: StyleConstants.setBackground(style, Color.white);
0646: StyleConstants.setForeground(style, Color.black);
0647: StyleConstants.setBold(style, false);
0648: StyleConstants.setItalic(style, false);
0649: styles.put("reference", style);
0650:
0651: style = new SimpleAttributeSet();
0652: StyleConstants.setFontFamily(style, "Monospaced");
0653: StyleConstants.setFontSize(style, 12);
0654: StyleConstants.setBackground(style, Color.white);
0655: StyleConstants
0656: .setForeground(style, new Color(0xB03060)/*Color.maroon*/);
0657: StyleConstants.setBold(style, true);
0658: StyleConstants.setItalic(style, false);
0659: styles.put("name", style);
0660:
0661: style = new SimpleAttributeSet();
0662: StyleConstants.setFontFamily(style, "Monospaced");
0663: StyleConstants.setFontSize(style, 12);
0664: StyleConstants.setBackground(style, Color.white);
0665: StyleConstants
0666: .setForeground(style, new Color(0xB03060)/*Color.maroon*/);
0667: StyleConstants.setBold(style, false);
0668: StyleConstants.setItalic(style, true);
0669: styles.put("value", style);
0670:
0671: style = new SimpleAttributeSet();
0672: StyleConstants.setFontFamily(style, "Monospaced");
0673: StyleConstants.setFontSize(style, 12);
0674: StyleConstants.setBackground(style, Color.white);
0675: StyleConstants.setForeground(style, Color.black);
0676: StyleConstants.setBold(style, true);
0677: StyleConstants.setItalic(style, false);
0678: styles.put("text", style);
0679:
0680: style = new SimpleAttributeSet();
0681: StyleConstants.setFontFamily(style, "Monospaced");
0682: StyleConstants.setFontSize(style, 12);
0683: StyleConstants.setBackground(style, Color.white);
0684: StyleConstants.setForeground(style, Color.blue);
0685: StyleConstants.setBold(style, false);
0686: StyleConstants.setItalic(style, false);
0687: styles.put("reservedWord", style);
0688:
0689: style = new SimpleAttributeSet();
0690: StyleConstants.setFontFamily(style, "Monospaced");
0691: StyleConstants.setFontSize(style, 12);
0692: StyleConstants.setBackground(style, Color.white);
0693: StyleConstants.setForeground(style, Color.black);
0694: StyleConstants.setBold(style, false);
0695: StyleConstants.setItalic(style, false);
0696: styles.put("identifier", style);
0697:
0698: style = new SimpleAttributeSet();
0699: StyleConstants.setFontFamily(style, "Monospaced");
0700: StyleConstants.setFontSize(style, 12);
0701: StyleConstants.setBackground(style, Color.white);
0702: StyleConstants
0703: .setForeground(style, new Color(0xB03060)/*Color.maroon*/);
0704: StyleConstants.setBold(style, false);
0705: StyleConstants.setItalic(style, false);
0706: styles.put("literal", style);
0707:
0708: style = new SimpleAttributeSet();
0709: StyleConstants.setFontFamily(style, "Monospaced");
0710: StyleConstants.setFontSize(style, 12);
0711: StyleConstants.setBackground(style, Color.white);
0712: StyleConstants
0713: .setForeground(style, new Color(0x000080)/*Color.navy*/);
0714: StyleConstants.setBold(style, false);
0715: StyleConstants.setItalic(style, false);
0716: styles.put("separator", style);
0717:
0718: style = new SimpleAttributeSet();
0719: StyleConstants.setFontFamily(style, "Monospaced");
0720: StyleConstants.setFontSize(style, 12);
0721: StyleConstants.setBackground(style, Color.white);
0722: StyleConstants.setForeground(style, Color.black);
0723: StyleConstants.setBold(style, true);
0724: StyleConstants.setItalic(style, false);
0725: styles.put("operator", style);
0726:
0727: style = new SimpleAttributeSet();
0728: StyleConstants.setFontFamily(style, "Monospaced");
0729: StyleConstants.setFontSize(style, 12);
0730: StyleConstants.setBackground(style, Color.white);
0731: StyleConstants.setForeground(style, Color.green.darker());
0732: StyleConstants.setBold(style, false);
0733: StyleConstants.setItalic(style, false);
0734: styles.put("comment", style);
0735:
0736: style = new SimpleAttributeSet();
0737: StyleConstants.setFontFamily(style, "Monospaced");
0738: StyleConstants.setFontSize(style, 12);
0739: StyleConstants.setBackground(style, Color.white);
0740: StyleConstants.setForeground(style, new Color(0xA020F0)
0741: .darker()/*Color.purple*/);
0742: StyleConstants.setBold(style, false);
0743: StyleConstants.setItalic(style, false);
0744: styles.put("preprocessor", style);
0745:
0746: style = new SimpleAttributeSet();
0747: StyleConstants.setFontFamily(style, "Monospaced");
0748: StyleConstants.setFontSize(style, 12);
0749: StyleConstants.setBackground(style, Color.white);
0750: StyleConstants.setForeground(style, Color.black);
0751: StyleConstants.setBold(style, false);
0752: StyleConstants.setItalic(style, false);
0753: styles.put("whitespace", style);
0754:
0755: style = new SimpleAttributeSet();
0756: StyleConstants.setFontFamily(style, "Monospaced");
0757: StyleConstants.setFontSize(style, 12);
0758: StyleConstants.setBackground(style, Color.white);
0759: StyleConstants.setForeground(style, Color.red);
0760: StyleConstants.setBold(style, false);
0761: StyleConstants.setItalic(style, false);
0762: styles.put("error", style);
0763:
0764: style = new SimpleAttributeSet();
0765: StyleConstants.setFontFamily(style, "Monospaced");
0766: StyleConstants.setFontSize(style, 12);
0767: StyleConstants.setBackground(style, Color.white);
0768: StyleConstants.setForeground(style, Color.orange);
0769: StyleConstants.setBold(style, false);
0770: StyleConstants.setItalic(style, false);
0771: styles.put("unknown", style);
0772: }
0773:
0774: /**
0775: * Just like a DefaultStyledDocument but intercepts inserts and
0776: * removes to color them.
0777: */
0778: private class HighLightedDocument extends DefaultStyledDocument {
0779: public void insertString(int offs, String str, AttributeSet a)
0780: throws BadLocationException {
0781: synchronized (doclock) {
0782: super .insertString(offs, str, a);
0783: color(offs, str.length());
0784: documentReader.update(offs, str.length());
0785: }
0786: }
0787:
0788: public void remove(int offs, int len)
0789: throws BadLocationException {
0790: synchronized (doclock) {
0791: super .remove(offs, len);
0792: color(offs, -len);
0793: documentReader.update(offs, -len);
0794: }
0795: }
0796: }
0797: }
0798:
0799: /**
0800: * A wrapper for a position in a document appropriate for storing
0801: * in a collection.
0802: */
0803: class DocPosition {
0804:
0805: /**
0806: * The actual position
0807: */
0808: private int position;
0809:
0810: /**
0811: * Get the position represented by this DocPosition
0812: *
0813: * @return the position
0814: */
0815: int getPosition() {
0816: return position;
0817: }
0818:
0819: /**
0820: * Construct a DocPosition from the given offset into the document.
0821: *
0822: * @param position The position this DocObject will represent
0823: */
0824: public DocPosition(int position) {
0825: this .position = position;
0826: }
0827:
0828: /**
0829: * Adjust this position.
0830: * This is useful in cases that an amount of text is inserted
0831: * or removed before this position.
0832: *
0833: * @param adjustment amount (either positive or negative) to adjust this position.
0834: * @return the DocPosition, adjusted properly.
0835: */
0836: public DocPosition adjustPosition(int adjustment) {
0837: position += adjustment;
0838: return this ;
0839: }
0840:
0841: /**
0842: * Two DocPositions are equal iff they have the same internal position.
0843: *
0844: * @return if this DocPosition represents the same position as another.
0845: */
0846: public boolean equals(Object obj) {
0847: if (obj instanceof DocPosition) {
0848: DocPosition d = (DocPosition) (obj);
0849: if (this .position == d.position) {
0850: return true;
0851: } else {
0852: return false;
0853: }
0854: } else {
0855: return false;
0856: }
0857: }
0858:
0859: /**
0860: * A string representation useful for debugging.
0861: *
0862: * @return A string representing the position.
0863: */
0864: public String toString() {
0865: return "" + position;
0866: }
0867: }
0868:
0869: /**
0870: * A comparator appropriate for use with Collections of
0871: * DocPositions.
0872: */
0873: class DocPositionComparator implements Comparator {
0874: /**
0875: * Does this Comparator equal another?
0876: * Since all DocPositionComparators are the same, they
0877: * are all equal.
0878: *
0879: * @return true for DocPositionComparators, false otherwise.
0880: */
0881: public boolean equals(Object obj) {
0882: if (obj instanceof DocPositionComparator) {
0883: return true;
0884: } else {
0885: return false;
0886: }
0887: }
0888:
0889: /**
0890: * Compare two DocPositions
0891: *
0892: * @param o1 first DocPosition
0893: * @param o2 second DocPosition
0894: * @return negative if first < second, 0 if equal, positive if first > second
0895: */
0896: public int compare(Object o1, Object o2) {
0897: if (o1 instanceof DocPosition && o2 instanceof DocPosition) {
0898: DocPosition d1 = (DocPosition) (o1);
0899: DocPosition d2 = (DocPosition) (o2);
0900: return (d1.getPosition() - d2.getPosition());
0901: } else if (o1 instanceof DocPosition) {
0902: return -1;
0903: } else if (o2 instanceof DocPosition) {
0904: return 1;
0905: } else if (o1.hashCode() < o2.hashCode()) {
0906: return -1;
0907: } else if (o2.hashCode() > o1.hashCode()) {
0908: return 1;
0909: } else {
0910: return 0;
0911: }
0912: }
0913: }
0914:
0915: /**
0916: * A reader interface for an abstract document. Since
0917: * the syntax highlighting packages only accept Stings and
0918: * Readers, this must be used.
0919: * Since the close() method does nothing and a seek() method
0920: * has been added, this allows us to get some performance
0921: * improvements through reuse. It can be used even after the
0922: * lexer explicitly closes it by seeking to the place that
0923: * we want to read next, and reseting the lexer.
0924: */
0925: class DocumentReader extends Reader {
0926:
0927: /**
0928: * Modifying the document while the reader is working is like
0929: * pulling the rug out from under the reader. Alerting the
0930: * reader with this method (in a nice thread safe way, this
0931: * should not be called at the same time as a read) allows
0932: * the reader to compensate.
0933: */
0934: public void update(int position, int adjustment) {
0935: if (position < this .position) {
0936: if (this .position < position - adjustment) {
0937: this .position = position;
0938: } else {
0939: this .position += adjustment;
0940: }
0941: }
0942: }
0943:
0944: /**
0945: * Current position in the document. Incremented
0946: * whenever a character is read.
0947: */
0948: private long position = 0;
0949:
0950: /**
0951: * Saved position used in the mark and reset methods.
0952: */
0953: private long mark = -1;
0954:
0955: /**
0956: * The document that we are working with.
0957: */
0958: private AbstractDocument document;
0959:
0960: /**
0961: * Construct a reader on the given document.
0962: *
0963: * @param document the document to be read.
0964: */
0965: public DocumentReader(AbstractDocument document) {
0966: this .document = document;
0967: }
0968:
0969: /**
0970: * Has no effect. This reader can be used even after
0971: * it has been closed.
0972: */
0973: public void close() {
0974: }
0975:
0976: /**
0977: * Save a position for reset.
0978: *
0979: * @param readAheadLimit ignored.
0980: */
0981: public void mark(int readAheadLimit) {
0982: mark = position;
0983: }
0984:
0985: /**
0986: * This reader support mark and reset.
0987: *
0988: * @return true
0989: */
0990: public boolean markSupported() {
0991: return true;
0992: }
0993:
0994: /**
0995: * Read a single character.
0996: *
0997: * @return the character or -1 if the end of the document has been reached.
0998: */
0999: public int read() {
1000: if (position < document.getLength()) {
1001: try {
1002: char c = document.getText((int) position, 1).charAt(0);
1003: position++;
1004: return c;
1005: } catch (BadLocationException x) {
1006: return -1;
1007: }
1008: } else {
1009: return -1;
1010: }
1011: }
1012:
1013: /**
1014: * Read and fill the buffer.
1015: * This method will always fill the buffer unless the end of the document is reached.
1016: *
1017: * @param cbuf the buffer to fill.
1018: * @return the number of characters read or -1 if no more characters are available in the document.
1019: */
1020: public int read(char[] cbuf) {
1021: return read(cbuf, 0, cbuf.length);
1022: }
1023:
1024: /**
1025: * Read and fill the buffer.
1026: * This method will always fill the buffer unless the end of the document is reached.
1027: *
1028: * @param cbuf the buffer to fill.
1029: * @param off offset into the buffer to begin the fill.
1030: * @param len maximum number of characters to put in the buffer.
1031: * @return the number of characters read or -1 if no more characters are available in the document.
1032: */
1033: public int read(char[] cbuf, int off, int len) {
1034: if (position < document.getLength()) {
1035: int length = len;
1036: if (position + length >= document.getLength()) {
1037: length = document.getLength() - (int) position;
1038: }
1039: if (off + length >= cbuf.length) {
1040: length = cbuf.length - off;
1041: }
1042: try {
1043: String s = document.getText((int) position, length);
1044: position += length;
1045: for (int i = 0; i < length; i++) {
1046: cbuf[off + i] = s.charAt(i);
1047: }
1048: return length;
1049: } catch (BadLocationException x) {
1050: return -1;
1051: }
1052: } else {
1053: return -1;
1054: }
1055: }
1056:
1057: /**
1058: * @return true
1059: */
1060: public boolean ready() {
1061: return true;
1062: }
1063:
1064: /**
1065: * Reset this reader to the last mark, or the beginning of the document if a mark has not been set.
1066: */
1067: public void reset() {
1068: if (mark == -1) {
1069: position = 0;
1070: } else {
1071: position = mark;
1072: }
1073: mark = -1;
1074: }
1075:
1076: /**
1077: * Skip characters of input.
1078: * This method will always skip the maximum number of characters unless
1079: * the end of the file is reached.
1080: *
1081: * @param n number of characters to skip.
1082: * @return the actual number of characters skipped.
1083: */
1084: public long skip(long n) {
1085: if (position + n <= document.getLength()) {
1086: position += n;
1087: return n;
1088: } else {
1089: long oldPos = position;
1090: position = document.getLength();
1091: return (document.getLength() - oldPos);
1092: }
1093: }
1094:
1095: /**
1096: * Seek to the given position in the document.
1097: *
1098: * @param n the offset to which to seek.
1099: */
1100: public void seek(long n) {
1101: if (n <= document.getLength()) {
1102: position = n;
1103: } else {
1104: position = document.getLength();
1105: }
1106: }
1107: }
|