Link to home
Start Free TrialLog in
Avatar of _MW_
_MW_

asked on

A problem when trying to integrate line numbers with the JavaEditorKit

Hi,

I was trying to include line numbers to the JavaEditorKit and so I added the code for it into JavaContext.java. It works fine except that the colours of the line numbers don't come out correctly. Does anyone know how to fix it?

Below is the source code for it.

=============> JavaDocument.java

/*
 * @(#)JavaDocument.java      1.2 98/05/04
 *
 * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 */
package FGI.GUI.JavaKitEditor;

import java.io.*;
import java.util.Vector;
import java.awt.Color;

import javax.swing.event.*;
import javax.swing.text.*;

/**
 * A document to represent text in the form of the
 * java programming language.  This is quite primitive
 * in that it simply provides support for lexically
 * analyzing the text.
 *
 * @author  Timothy Prinzing
 * @version 1.2 05/04/98
 */
public class JavaDocument extends PlainDocument {

    public JavaDocument() {
      super(new GapContent(1024));
    }

    /**
     * Create a lexical analyzer for this document.
     */
    public Scanner createScanner() {
      Scanner s;
      try {
          s = new Scanner();
      } catch (IOException e) {
          s = null;
      }
      return s;
    }

    /**
     * Fetch a reasonable location to start scanning
     * given the desired start location.  This allows
     * for adjustments needed to accomodate multiline
     * comments.
     */
    public int getScannerStart(int p) {
      Element elem = getDefaultRootElement();
      int lineNum = elem.getElementIndex(p);
      Element line = elem.getElement(lineNum);
      AttributeSet a = line.getAttributes();
      while (a.isDefined(CommentAttribute) && lineNum > 0) {
          lineNum -= 1;
          line = elem.getElement(lineNum);
          a = line.getAttributes();
      }
      return line.getStartOffset();
    }

    // --- AbstractDocument methods ----------------------------

    /**
     * Updates document structure as a result of text insertion.  This
     * will happen within a write lock.  The superclass behavior of
     * updating the line map is executed followed by marking any comment
     * areas that should backtracked before scanning.
     *
     * @param chng the change event
     * @param attr the set of attributes
     */
    protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
      super.insertUpdate(chng, attr);
      
      // update comment marks
      Element root = getDefaultRootElement();
      DocumentEvent.ElementChange ec = chng.getChange(root);
      if (ec != null) {
          Element[] added = ec.getChildrenAdded();
          boolean inComment = false;
          for (int i = 0; i < added.length; i++) {
            Element elem = added[i];
            int p0 = elem.getStartOffset();
            int p1 = elem.getEndOffset();
            String s;
            try {
                s = getText(p0, p1 - p0);
            } catch (BadLocationException bl) {
                s = null;
            }
            if (inComment) {
                MutableAttributeSet a = (MutableAttributeSet) elem.getAttributes();
                a.addAttribute(CommentAttribute, CommentAttribute);
                int index = s.indexOf("*/");
                if (index >= 0) {
                  // found an end of comment, turn off marks
                  inComment = false;
                }
            } else {
                // scan for multiline comment
                int index = s.indexOf("/*");
                if (index >= 0) {
                  // found a start of comment, see if it spans lines
                  index = s.indexOf("*/", index);
                  if (index < 0) {
                      // it spans lines
                      inComment = true;
                  }
                }
            }
          }
      }
    }

    /**
     * Updates any document structure as a result of text removal.
     * This will happen within a write lock.  The superclass behavior of
     * updating the line map is executed followed by placing a lexical
     * update command on the analyzer queue.
     *
     * @param chng the change event
     */
    protected void removeUpdate(DefaultDocumentEvent chng) {
      super.removeUpdate(chng);
      
      // update comment marks
    }

    // --- variables ------------------------------------------------

    /**
     * Key to be used in AttributeSet's holding a value of Token.
     */
    static final Object CommentAttribute = new AttributeKey();

    static class AttributeKey {

        private AttributeKey() {
      }

        public String toString() {
          return "comment";
      }

    }


   
    public class Scanner extends sun.tools.java.Scanner {

        Scanner() throws IOException {
          super(new LocalEnvironment(), new DocumentInputStream(0, getLength()));
          scanComments = true;
      }

      /**
       * Sets the range of the scanner.  This should be called
       * to reinitialize the scanner to the desired range of
       * coverage.
       */
        public void setRange(int p0, int p1) throws IOException {
          useInputStream(new DocumentInputStream(p0, p1));
          this.p0 = p0;
      }

      /**
       * This fetches the starting location of the current
       * token in the document.
       */
        public final int getStartOffset() {
          int begOffs = (int) (pos & MAXFILESIZE);
          return p0 + begOffs;
      }

      /**
       * This fetches the ending location of the current
       * token in the document.
       */
        public final int getEndOffset() {
          int endOffs = (int) (getEndPos() & MAXFILESIZE);
          return p0 + endOffs;
      }

      int p0;
    }

    /**
     * Class to provide InputStream functionality from a portion of a
     * Document.  This really should be a Reader, but not enough
     * things use it yet.
     */
    class DocumentInputStream extends InputStream {

        public DocumentInputStream(int p0, int p1) {
          this.segment = new Segment();
          this.p0 = p0;
          this.p1 = Math.min(getLength(), p1);
          pos = p0;
          try {
            loadSegment();
          } catch (IOException ioe) {
            throw new Error("unexpected: " + ioe);
          }
      }

      /**
       * Reads the next byte of data from this input stream. The value
       * byte is returned as an <code>int</code> in the range
       * <code>0</code> to <code>255</code>. If no byte is available
       * because the end of the stream has been reached, the value
       * <code>-1</code> is returned. This method blocks until input data
       * is available, the end of the stream is detected, or an exception
       * is thrown.
       * <p>
       * A subclass must provide an implementation of this method.
       *
       * @return     the next byte of data, or <code>-1</code> if the end of the
       *             stream is reached.
       * @exception  IOException  if an I/O error occurs.
       * @since      JDK1.0
       */
        public int read() throws IOException {
          if (index >= segment.offset + segment.count) {
            if (pos >= p1) {
                // no more data
                return -1;
            }
            loadSegment();
          }
          return segment.array[index++];
      }

      void loadSegment() throws IOException {
          try {
            int n = Math.min(1024, p1 - pos);
            getText(pos, n, segment);
            pos += n;
            index = segment.offset;
          } catch (BadLocationException e) {
            throw new IOException("Bad location");
          }
      }
      
      Segment segment;
      int p0;    // start position
      int p1;    // end position
      int pos;   // pos in document
      int index; // index into array of the segment
    }

    static class LocalEnvironment extends sun.tools.java.Environment {

        public void error(Object source, int where, String err,
                    Object arg1, Object arg2, Object arg3) {
          // should do something useful...
          System.err.println(err);
          System.err.println("location: " + where);
      }
    }

}


============JavaContext.java
/*
 * @(#)JavaContext.java      1.2 98/05/04
 *
 * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 */
package FGI.GUI.JavaKitEditor;

import java.awt.*;
import java.util.Vector;

import javax.swing.text.*;

/**
 * A collection of styles used to render java text.  
 * This class also acts as a factory for the views used
 * to represent the java documents.  Since the rendering
 * styles are based upon view preferences, the views need
 * a way to gain access to the style settings which is
 * facilitated by implementing the factory in the style
 * storage.  Both functionalities can be widely shared across
 * java document views.
 *
 * @author   Timothy Prinzing
 * @version  1.2 05/04/98
 */
public class JavaContext extends StyleContext implements ViewFactory {

    /**
     * Constructs a set of styles to represent java lexical
     * tokens.  By default there are no colors or fonts specified.
     */
    public JavaContext() {
      super();
      Style root = getStyle(DEFAULT_STYLE);
      tokenStyles = new Style[Token.MaximumScanValue + 1];
      Token[] tokens = Token.all;
      int n = tokens.length;
      for (int i = 0; i < n; i++) {
          Token t = tokens[i];
          Style parent = getStyle(t.getCategory());
          if (parent == null) {
            parent = addStyle(t.getCategory(), root);
          }
          Style s = addStyle(null, parent);
          s.addAttribute(Token.TokenAttribute, t);
          tokenStyles[t.getScanValue()] = s;

      }
    }

    /**
     * Fetch the foreground color to use for a lexical
     * token with the given value.
     *
     * @param attr attribute set from a token element
     *  that has a Token in the set.
     */
    public Color getForeground(int code) {
      if (tokenColors == null) {
          tokenColors = new Color[Token.MaximumScanValue + 1];
      }
      if ((code >= 0) && (code < tokenColors.length)) {
          Color c = tokenColors[code];
          if (c == null) {
            Style s = tokenStyles[code];
            c = StyleConstants.getForeground(s);
          }
          return c;
      }
      return Color.black;
    }

    /**
     * Fetch the font to use for a lexical
     * token with the given scan value.
     */
    public Font getFont(int code) {
      if (tokenFonts == null) {
          tokenFonts = new Font[Token.MaximumScanValue + 1];
      }
      if (code < tokenFonts.length) {
          Font f = tokenFonts[code];
          if (f == null) {
            Style s = tokenStyles[code];
            f = getFont(s);
          }
          return f;
      }
      return null;
    }

    /**
     * Fetches the attribute set to use for the given
     * scan code.  The set is stored in a table to
     * facilitate relatively fast access to use in
     * conjunction with the scanner.
     */
    public Style getStyleForScanValue(int code) {
      if (code < tokenStyles.length) {
          return tokenStyles[code];
      }
      return null;
    }

    // --- ViewFactory methods -------------------------------------

    public View create(Element elem) {
      return new JavaView(elem);
    }

    // --- variables -----------------------------------------------

    /**
     * The styles representing the actual token types.
     */
    Style[] tokenStyles;

    /**
     * Cache of foreground colors to represent the
     * various tokens.
     */
    transient Color[] tokenColors;

    /**
     * Cache of fonts to represent the various tokens.
     */
    transient Font[] tokenFonts;

    /**
     * View that uses the lexical information to determine the
     * style characteristics of the text that it renders.  This
     * simply colorizes the various tokens and assumes a constant
     * font family and size.
     */
    class JavaView extends WrappedPlainView {
          
    public short NUMBERS_WIDTH=25;
          
      /**
       * Construct a simple colorized view of java
       * text.
       */
      JavaView(Element elem) {
          super(elem);
          JavaDocument doc = (JavaDocument) getDocument();
          lexer = doc.createScanner();
          lexerValid = false;
          
          
      short top = 0;
      short left = 0;
      short bottom = 0;
      short right = 0;
      this.setInsets(top, left, bottom, right);
          
      }
      
      /**
       * Renders using the given rendering surface and area
       * on that surface.  This is implemented to invalidate
       * the lexical scanner after rendering so that the next
       * request to drawUnselectedText will set a new range
       * for the scanner.
       *
       * @param g the rendering surface to use
       * @param a the allocated region to render into
       *
       * @see View#paint
       */
        public void paint(Graphics g, Shape a) {
          super.paint(g, a);
          lexerValid = false;
      }
      
    protected void setInsets(short top, short left, short bottom,
                             short right)
   {      
         super.setInsets(top,(short)(left+NUMBERS_WIDTH),
                             bottom,right);
    }
                           
    public void paintChild(Graphics g, Rectangle r, int n) {
        super.paintChild(g, r, n);
        int previousLineCount = getPreviousLineCount();
        int numberX = r.x - getLeftInset();
        int numberY = r.y + r.height - 5;
        g.drawString(Integer.toString(previousLineCount + n + 1),
                                      numberX, numberY);
                                     
                                     
    }

    public int getPreviousLineCount() {
        int lineCount = 0;
        View parent = this.getParent();
        int count = parent.getViewCount();
        for (int i = 0; i < count; i++) {
            if (parent.getView(i) == this) {
                break;
            }
            else {
                lineCount += parent.getView(i).getViewCount();
            }
        }
        return lineCount;
    }
                                 
           public void setSize(float f1, float f2) {
          if (getWidth() < f1) {
              // System.out.println("increase");
          }
          //System.out.println("from " + getWidth() + " to " + f1);
          super.setSize(1000, f2);
     }




      /**
       * Renders the given range in the model as normal unselected
       * text.  This is implemented to paint colors based upon the
       * token-to-color translations.  To reduce the number of calls
       * to the Graphics object, text is batched up until a color
       * change is detected or the entire requested range has been
       * reached.
       *
       * @param g the graphics context
       * @param x the starting X coordinate
       * @param y the starting Y coordinate
       * @param p0 the beginning position in the model
       * @param p1 the ending position in the model
       * @returns the location of the end of the range
       * @exception BadLocationException if the range is invalid
       */
        protected int drawUnselectedText(Graphics g, int x, int y,
                               int p0, int p1) throws BadLocationException {
          Document doc = getDocument();
          Color last = null;
          int mark = p0;
          for (; p0 < p1; ) {
            updateScanner(p0);
            int p = Math.min(lexer.getEndOffset(), p1);
            p = (p <= p0) ? p1 : p;
            Color fg = getForeground(lexer.token);
            if (fg != last && last != null) {
                // color change, flush what we have
                g.setColor(last);
                Segment text = getLineBuffer();
                doc.getText(mark, p0 - mark, text);
                x = Utilities.drawTabbedText(text, x, y, g, this, mark);
                mark = p0;
            }
            last = fg;
            p0 = p;
          }
          // flush remaining
          g.setColor(last);
          Segment text = getLineBuffer();
          doc.getText(mark, p1 - mark, text);
          x = Utilities.drawTabbedText(text, x, y, g, this, mark);
          return x;
      }

      /**
       * Update the scanner (if necessary) to point to the appropriate
       * token for the given start position needed for rendering.
       */
      void updateScanner(int p) {
          try {
            if (! lexerValid) {
                JavaDocument doc = (JavaDocument) getDocument();
                lexer.setRange(doc.getScannerStart(p), doc.getLength());
                lexerValid = true;
            }
            while (lexer.getEndOffset() <= p) {
                lexer.scan();
            }
          } catch (Throwable e) {
            // can't adjust scanner... calling logic
            // will simply render the remaining text.
            //e.printStackTrace();
          }
      }
      
      JavaDocument.Scanner lexer;
      boolean lexerValid;
    }

}


================> GapContent.java

      /*
 * @(#)GapContent.java      1.3 98/04/09
 *
 * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 */
//package javax.swing.text;

package FGI.GUI.JavaKitEditor;

import java.util.Vector;
import java.io.Serializable;
import javax.swing.undo.UndoableEdit;
import javax.swing.SwingUtilities;
import javax.swing.text.*;
import javax.swing.*;


/**
 * An implementation of the AbstractDocument.Content interface
 * implemented using a gapped buffer similar to that used by emacs.
 * The underlying storage is a array of unicode characters with
 * a gap somewhere.  The gap is moved to the location of changes
 * to take advantage of common behavior where most changes are
 * in the same location.  Changes that occur at a gap boundry are
 * generally cheap and moving the gap is generally cheaper than
 * moving the array contents directly to accomodate the change.
 * <p>
 * The positions tracking change are also generally cheap to
 * maintain.  The Position implementations (marks) store the array
 * index and can easily calculate the sequential position from
 * the current gap location.  Changes only require update to the
 * the marks between the old and new gap boundries when the gap
 * is moved, so generally updating the marks is pretty cheap.
 * The marks are stored sorted so they can be located quickly
 * with a binary search.  This increases the cost of adding a
 * mark, and decreases the cost of keeping the mark updated.
 *
 * @author  Timothy Prinzing
 * @version 1.3 04/09/98
 */
public final class GapContent implements AbstractDocument.Content, Serializable {


    /**
     * Creates a new GapContent object.  Initial size defaults to 10.
     */
    public GapContent() {
      this(10);
    }

    /**
     * Creates a new GapContent object, with the initial
     * size specified.
     *
     * @param initialLength the initial size
     */
    public GapContent(int initialLength) {
      array = new char[initialLength];
      array[0] = '\n';
      g0 = 1;
      g1 = initialLength;
    }

    // --- AbstractDocument.Content methods -------------------------

    /**
     * Returns the length of the content.
     *
     * @return the length >= 1
     * @see AbstractDocument.Content#length
     */
    public int length() {
      int len = array.length - (g1 - g0);
      return len;
    }

    /**
     * Inserts a string into the content.
     *
     * @param where the starting position >= 0, < length()
     * @param str the non-null string to insert
     * @return an UndoableEdit object for undoing
     * @exception BadLocationException if the specified position is invalid
     * @see AbstractDocument.Content#insertString
     */
    public UndoableEdit insertString(int where, String str) throws BadLocationException {
      if (where >= length()) {
          throw new BadLocationException("Invalid insert", length());
      }
      char[] chars = str.toCharArray();
      replace(where, 0, chars);
      return null;
    }

    /**
     * Removes part of the content.
     *
     * @param where the starting position >= 0, where + nitems < length()
     * @param nitems the number of characters to remove >= 0
     * @return an UndoableEdit object for undoing
     * @exception BadLocationException if the specified position is invalid
     * @see AbstractDocument.Content#remove
     */
    public UndoableEdit remove(int where, int nitems) throws BadLocationException {
      if (where + nitems >= length()) {
          throw new BadLocationException("Invalid insert", length() + 1);
      }
      replace(where, nitems, empty);
      return null;
      
    }

    /**
     * Retrieves a portion of the content.
     *
     * @param where the starting position >= 0
     * @param len the length to retrieve >= 0
     * @return a string representing the content
     * @exception BadLocationException if the specified position is invalid
     * @see AbstractDocument.Content#getString
     */
    public String getString(int where, int len) throws BadLocationException {
      Segment s = new Segment();
      getChars(where, len, s);
      return new String(s.array, s.offset, s.count);
    }

    /**
     * Retrieves a portion of the content.  If the desired content spans
     * the gap, we copy the content.  If the desired content does not
     * span the gap, the actual store is returned to avoid the copy since
     * it is contiguous.
     *
     * @param where the starting position >= 0, where + len <= length()
     * @param len the number of characters to retrieve >= 0
     * @param chars the Segment object to return the characters in
     * @exception BadLocationException if the specified position is invalid
     * @see AbstractDocument.Content#getChars
     */
    public void getChars(int where, int len, Segment chars) throws BadLocationException {
      if (where < 0) {
          throw new BadLocationException("Invalid location", -1);
      }
      if ((where + len) > length()) {
          throw new BadLocationException("Invalid location", length() + 1);
      }
      if ((where + len) <= g0) {
          // below gap
          chars.array = array;
          chars.offset = where;
      } else if (where >= g0) {
          // above gap
          chars.array = array;
          chars.offset = g1 + where - g0;
      } else {
          // spans the gap, must copy
          chars.array = new char[len];
          chars.offset = 0;
          int before = g0 - where;
          System.arraycopy(array, where, chars.array, 0, before);
          System.arraycopy(array, g1, chars.array, before, len - before);
      }
      chars.count = len;
    }

    /**
     * Creates a position within the content that will
     * track change as the content is mutated.
     *
     * @param offset the offset to track >= 0
     * @return the position
     * @exception BadLocationException if the specified position is invalid
     */
    public Position createPosition(int offset) throws BadLocationException {
      if (marks == null) {
          marks = new Vector();
          search = new MarkData(0);
      }
      if (unusedMarks > Math.max(5, (marks.size() / 10))) {
          removeUnusedMarks();
      }
      int index = (offset < g0) ? offset : offset + (g1 - g0);
      MarkData m = new MarkData(index);
      int sortIndex = findSortIndex(m);
      marks.insertElementAt(m, sortIndex);
      return new StickyPosition(m);
    }

    /**
     * Holds the data for a mark... seperately from
     * the real mark so that the real mark (Position
     * that the caller of createPosition holds) can be
     * collected if there are no more references to
     * it.  The update table holds only a reference
     * to this data.
     */
    final class MarkData {

      MarkData(int index) {
          this.index = index;
      }

      /**
       * Fetch the location in the contiguous sequence
       * being modeled.  The index in the gap array
       * is held by the mark, so it is adjusted according
       * to it's relationship to the gap.
       */
        public final int getOffset() {
          int offs = (index < g0) ? index : index - (g1 - g0);
          return Math.max(offs, 0);
      }

        public final void dispose() {
          unused = true;
          unusedMarks += 1;
      }

      int index;
      boolean unused;
    }

    /**
     * This really wants to be a weak reference but
     * in 1.1 we don't have a 100% pure solution for
     * this... so this class trys to hack a solution
     * to causing the marks to be collected.
     */
    final class StickyPosition implements Position {

      StickyPosition(MarkData mark) {
          this.mark = mark;
      }

        public final int getOffset() {
          return mark.getOffset();
      }

      protected void finalize() throws Throwable {
          // schedule the record to be removed later
          // on another thread.
          mark.dispose();
      }

        public String toString() {
          return Integer.toString(getOffset());
      }

      MarkData mark;
    }

    // --- variables --------------------------------------

    private static final char[] empty = new char[0];
    private transient Vector marks;

    /**
     * Record used for searching for the place to
     * start updating mark indexs when the gap
     * boundries are moved.
     */
    private transient MarkData search;

    /**
     * The number of unused mark entries
     */
    private transient int unusedMarks;

    /**
     * The array of unicode characters that store
     * the content.
     */
    char[] array;

    /**
     * start of gap in the array
     */
    int g0;

    /**
     * end of gap in the array
     */
    int g1;


    // --- gap management -------------------------------

    /**
     * Replace the given logical position in the storage with
     * the given new items.  This will move the gap to the area
     * being changed if the gap is not currently located at the
     * change location.
     *
     * @param position the location to make the replacement.  This
     *  is not the location in the underlying storage array, but
     *  the location in the contiguous space being modeled.
     * @param rmSize the number of items to remove
     * @param addItems the new items to place in storage.
     */
    void replace(int position, int rmSize, char[] addItems) {
      int addSize = addItems.length;
      int addOffset = 0;
      if (addSize == 0) {
          close(position, rmSize);
          return;
      } else if (rmSize > addSize) {
          /* Shrink the end. */
          close(position+addSize, rmSize-addSize);
      } else {
          /* Grow the end, do two chunks. */
          int endSize = addSize - rmSize;
          int end = open(position + rmSize, endSize);
          System.arraycopy(addItems, rmSize, array, end, endSize);
          addSize = rmSize;
      }
      System.arraycopy(addItems, addOffset, array, position, addSize);
    }

    /**
     * Delete nItems at position.  Squeezes any marks
     * within the deleted area to position.  This moves
     * the gap to the best place by minimizing it's
     * overall movement.  The gap must intersect the
     * target block.
     */
    void close(int position, int nItems) {
      if (nItems == 0)  return;

      int end = position + nItems;
      int new_gs = (g1 - g0) + nItems;
      if (end <= g0) {
          // Move gap to end of block.
          if (g0 != end) {
            shiftGap(end);
          }
          // Adjust g0.
          shiftGapStartDown(g0 - nItems);
      } else if (position >= g0) {
          // Move gap to beginning of block.
          if (g0 != position) {
            shiftGap(position);
          }
          // Adjust g1.
          shiftGapEndUp(g0 + new_gs);
      } else {
          // The gap is properly inside the target block.
          // No data movement necessary, simply move both gap pointers.
          shiftGapStartDown(position);
          shiftGapEndUp(g0 + new_gs);
      }
    }

    /**
     * Make space for the given number of items at the given
     * location.  
     *
     * @returns the location that the caller should fill in.
     */
    int open(int position, int nItems) {
      int gapSize = g1 - g0;
      if (nItems == 0) {
          if (position > g0)  
            position += gapSize;
          return position;
      }

      // Expand the array if the gap is too small.
      shiftGap(position);
      if (nItems >= gapSize) {
          // Pre-shift the gap, to reduce total movement.
          shiftEnd(array.length - gapSize + nItems);
          gapSize = g1 - g0;
      }

      g0 = g0 + nItems;
      return position;
    }

    /**
     * resize the underlying storage array to the
     * given new size
     */
    void resize(int nsize) {
      char[] narray = new char[nsize];
      System.arraycopy(array, 0, narray, 0, Math.min(nsize, array.length));
      array = narray;
    }

    /**
     * Make the gap bigger, moving any necessary data and updating
     * the appropriate marks
     */
    void shiftEnd(int newSize) {
      int oldSize = array.length;
      int oldGapEnd = g1;
      int upperSize = oldSize - oldGapEnd;
      int newGapEnd;
      long dg;

      if (newSize < oldSize) {
          if (oldSize <= array.length) {
            // No more downsizing.
            return;
          }
          if (upperSize > 0) {
            /* When contracting, move vector contents to front. */
            shiftGap(oldSize - (g1 - g0));
            oldGapEnd = oldSize;
            upperSize = 0;
          }
      }

      resize(newSize);
      newGapEnd = array.length - upperSize;
      g1 = newGapEnd;
      dg = newGapEnd - oldGapEnd;

      // Adjust marks.
      int adjustIndex = findMarkAdjustIndex(oldGapEnd);
      int n = marks.size();
      for (int i = adjustIndex; i < n; i++) {
          MarkData mark = (MarkData) marks.elementAt(i);
          mark.index += dg;
      }
      
      if (upperSize != 0) {
          // Copy array items to new end of array.
          System.arraycopy(array, oldGapEnd, array, newGapEnd, upperSize);
      }
    }

    /**
     * Move the start of the gap to a new location,
     * without changing the size of the gap.  This
     * moves the data in the array and updates the
     * marks accordingly.
     */
    void shiftGap(int newGapStart) {
      if (newGapStart == g0) {
          return;
      }
      int oldGapStart = g0;
      int dg = newGapStart - oldGapStart;
      int oldGapEnd = g1;
      int newGapEnd = oldGapEnd + dg;
      int gapSize = oldGapEnd - oldGapStart;

      g0 = newGapStart;
      g1 = newGapEnd;
      if (dg > 0) {
          // Move gap up, move data and marks down.
          int adjustIndex = findMarkAdjustIndex(oldGapStart);
          int n = marks.size();
          for (int i = adjustIndex; i < n; i++) {
            MarkData mark = (MarkData) marks.elementAt(i);
            if (mark.index >= newGapEnd) {
                break;
            }
            mark.index -= gapSize;
          }
          System.arraycopy(array, oldGapEnd, array, oldGapStart, dg);
      } else if (dg < 0) {
          // Move gap down, move data and marks up.
          int adjustIndex = findMarkAdjustIndex(newGapStart);
          int n = marks.size();
          for (int i = adjustIndex; i < n; i++) {
            MarkData mark = (MarkData) marks.elementAt(i);
            if (mark.index >= oldGapEnd) {
                break;
            }
            mark.index += gapSize;
          }
          System.arraycopy(array, newGapStart, array, newGapEnd, -dg);
      }
    }

    /**
     * Adjust the gap end downward.  This doesn't move
     * any data, but it does update any marks affected
     * by the boundry change.  All marks from the old
     * gap start down to the new gap start are squeezed
     * to the end of the gap (their location has been
     * removed).
     */
    void shiftGapStartDown(int newGapStart) {
      // Push aside all marks from oldGapStart down to newGapStart.
      int adjustIndex = findMarkAdjustIndex(newGapStart);
      int n = marks.size();
      for (int i = adjustIndex; i < n; i++) {
          MarkData mark = (MarkData) marks.elementAt(i);
          if (mark.index > g0) {
            // no more marks to adjust
            break;
          }
          mark.index = g1;
      }
      g0 = newGapStart;
    }

    /**
     * Adjust the gap end upward.  This doesn't move
     * any data, but it does update any marks affected
     * by the boundry change. All marks from the old
     * gap end up to the new gap end are squeezed
     * to the end of the gap (their location has been
     * removed).
     */
    void shiftGapEndUp(int newGapEnd) {
      int adjustIndex = findMarkAdjustIndex(g1);
      int n = marks.size();
      for (int i = adjustIndex; i < n; i++) {
          MarkData mark = (MarkData) marks.elementAt(i);
          if (mark.index >= newGapEnd) {
            break;
          }
          mark.index = newGapEnd;
      }
      g1 = newGapEnd;
    }

    /**
     * Compares two marks.
     *
     * @param o1 the first object
     * @param o2 the second object
     * @return < 0 if o1 < o2, 0 if the same, > 0 if o1 > o2
     */
    final int compare(MarkData o1, MarkData o2) {
      if (o1.index < o2.index) {
          return -1;
      } else if (o1.index > o2.index) {
          return 1;
      } else {
          return 0;
      }
    }

    /**
     * Finds the index to start mark adjustments given
     * some search index.
     */
    final int findMarkAdjustIndex(int searchIndex) {
      search.index = Math.max(searchIndex, 1);
      int index = findSortIndex(search);

      // return the first in the series
      // (ie. there may be duplicates).
      for (int i = index - 1; i >= 0; i--) {
          MarkData d = (MarkData) marks.elementAt(i);
          if (d.index != search.index) {
            break;
          }
          index -= 1;
      }
      return index;
    }

    /**
     * Finds the index of where to insert a new mark.
     *
     * @param o the mark to insert
     * @return the index
     */
    final int findSortIndex(MarkData o) {
      int lower = 0;
      int upper = marks.size() - 1;
      int mid = 0;
      
      if (upper == -1) {
          return 0;
      }

      int cmp = 0;
      MarkData last = (MarkData) marks.elementAt(upper);
      cmp = compare(o, last);
      if (cmp > 0)
          return upper + 1;
      
      while (lower <= upper) {
          mid = lower + ((upper - lower) / 2);
          MarkData entry = (MarkData) marks.elementAt(mid);
          cmp = compare(o, entry);

          if (cmp == 0) {
            // found a match
            return mid;
          } else if (cmp < 0) {        
            upper = mid - 1;
          } else {
            lower = mid + 1;
          }
      }

      // didn't find it, but we indicate the index of where it would belong.
      return (cmp < 0) ? mid : mid + 1;
    }

    /**
     * Remove all unused marks out of the sorted collection
     * of marks.  
     */
    final void removeUnusedMarks() {
      int n = marks.size();
      Vector cleaned = new Vector(n);
      for (int i = 0; i < n; i++) {
          MarkData mark = (MarkData) marks.elementAt(i);
          if (mark.unused == false) {
            cleaned.addElement(mark);
          }
      }
      marks = cleaned;
      unusedMarks = 0;
    }

}

==============>Token.java

/*
 * @(#)Token.java      1.2 98/05/04
 *
 * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 */
package FGI.GUI.JavaKitEditor;

import java.io.Serializable;
import sun.tools.java.Constants;


/**
 * Simple class to represent a lexical token.  This
 * wraps the Constants used by the scanner to provide
 * a convenient class that can be stored as a attribute
 * value.
 *
 * @author  Timothy Prinzing
 * @version 1.2 05/04/98
 */
public class Token implements Serializable {

    Token(String representation, int scanValue) {
      this.representation = representation;
      this.scanValue = scanValue;
    }
   
    /**
     * A human presentable form of the token, useful
     * for things like lists, debugging, etc.
     */
    public String toString() {
      return representation;
    }

    /**
     * Numeric value of this token.  This is the value
     * returned by the scanner and is the tie between
     * the lexical scanner and the tokens.
     */
    public int getScanValue() {
      return scanValue;
    }

    /**
     * Specifies the category of the token as a
     * string that can be used as a label.
     */
    public String getCategory() {
      String nm = getClass().getName();
      int nmStart = nm.lastIndexOf('.') + 1; // not found results in 0
      return nm.substring(nmStart, nm.length());
    }

    /**
     * Returns a hashcode for this set of attributes.
     * @return     a hashcode value for this set of attributes.
     */
    public final int hashCode() {
      return scanValue;
    }

    /**
     * Compares this object to the specifed object.
     * The result is <code>true</code> if and only if the argument is not
     * <code>null</code> and is a <code>Font</code> object with the same
     * name, style, and point size as this font.
     * @param     obj   the object to compare this font with.
     * @return    <code>true</code> if the objects are equal;
     *            <code>false</code> otherwise.
     */
    public final boolean equals(Object obj) {
      if (obj instanceof Token) {
          Token t = (Token) obj;
          return (scanValue == t.scanValue);
      }
      return false;
    }

    // --- variables -------------------------------------

    public static final int MaximumScanValue = Constants.INLINENEWINSTANCE + 1;

    /**
     * Key to be used in AttributeSet's holding a value of Token.
     */
    public static final Object TokenAttribute = new AttributeKey();

    String representation;
    int scanValue;

    public static class Operator extends Token {

      Operator(String representation, int scanValue) {
          super(representation, scanValue);
      }

    }
   
    public static class Value extends Token {

      Value(String representation, int scanValue) {
          super(representation, scanValue);
      }

    }
   
    public static class Type extends Token {

      Type(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    public static class Expression extends Token {

      Expression(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    public static class Statement extends Token {

      Statement(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    public static class Declaration extends Token {

      Declaration(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    public static class Modifier extends Token {

      Modifier(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    public static class Punctuation extends Token {

      Punctuation(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    public static class Special extends Token {

      Special(String representation, int scanValue) {
          super(representation, scanValue);
      }
    }
   
    static class AttributeKey {

        private AttributeKey() {
      }

        public String toString() {
          return "token";
      }

    }

    /*
     * Operators
     */
    public static final Token COMMA =       new Operator(Constants.opNames[Constants.COMMA],
                                           Constants.COMMA);
    public static final Token ASSIGN =      new Operator(Constants.opNames[Constants.ASSIGN],
                                           Constants.ASSIGN);
    public static final Token ASGMUL =      new Operator(Constants.opNames[Constants.ASGMUL],
                                           Constants.ASGMUL);
    public static final Token ASGDIV =      new Operator(Constants.opNames[Constants.ASGDIV],
                                           Constants.ASGDIV);
    public static final Token ASGREM =      new Operator(Constants.opNames[Constants.ASGREM],
                                           Constants.ASGREM);
    public static final Token ASGADD =      new Operator(Constants.opNames[Constants.ASGADD],
                                           Constants.ASGADD);
    public static final Token ASGSUB =      new Operator(Constants.opNames[Constants.ASGSUB],
                                           Constants.ASGSUB);
    public static final Token ASGLSHIFT =   new Operator(Constants.opNames[Constants.ASGLSHIFT],
                                           Constants.ASGLSHIFT);
    public static final Token ASGRSHIFT =   new Operator(Constants.opNames[Constants.ASGRSHIFT],
                                           Constants.ASGRSHIFT);
    public static final Token ASGURSHIFT =  new Operator(Constants.opNames[Constants.ASGURSHIFT],
                                           Constants.ASGURSHIFT);
    public static final Token ASGBITAND =   new Operator(Constants.opNames[Constants.ASGBITAND],
                                           Constants.ASGBITAND);
    public static final Token ASGBITOR =    new Operator(Constants.opNames[Constants.ASGBITOR],
                                           Constants.ASGBITOR);
    public static final Token ASGBITXOR =   new Operator(Constants.opNames[Constants.ASGBITOR],
                                           Constants.ASGBITOR);
    public static final Token COND =        new Operator(Constants.opNames[Constants.COND],
                                           Constants.COND);
    public static final Token OR =          new Operator(Constants.opNames[Constants.OR],
                                           Constants.OR);
    public static final Token AND =         new Operator(Constants.opNames[Constants.AND],
                                           Constants.AND);
    public static final Token BITOR =       new Operator(Constants.opNames[Constants.BITOR],
                                           Constants.BITOR);
    public static final Token BITXOR =      new Operator(Constants.opNames[Constants.BITXOR],
                                           Constants.BITXOR);
    public static final Token BITAND =      new Operator(Constants.opNames[Constants.BITAND],
                                           Constants.BITAND);
    public static final Token NE =          new Operator(Constants.opNames[Constants.NE],
                                           Constants.NE);
    public static final Token EQ =          new Operator(Constants.opNames[Constants.EQ],
                                           Constants.EQ);
    public static final Token GE =          new Operator(Constants.opNames[Constants.GE],
                                           Constants.GE);
    public static final Token GT =          new Operator(Constants.opNames[Constants.GT],
                                           Constants.GT);
    public static final Token LE =          new Operator(Constants.opNames[Constants.LE],
                                           Constants.LE);
    public static final Token LT =          new Operator(Constants.opNames[Constants.LT],
                                           Constants.LT);
    public static final Token INSTANCEOF =  new Operator(Constants.opNames[Constants.INSTANCEOF],
                                           Constants.INSTANCEOF);
    public static final Token LSHIFT =      new Operator(Constants.opNames[Constants.LSHIFT],
                                           Constants.LSHIFT);
    public static final Token RSHIFT =      new Operator(Constants.opNames[Constants.RSHIFT],
                                           Constants.RSHIFT);
    public static final Token URSHIFT =     new Operator(Constants.opNames[Constants.URSHIFT],
                                           Constants.URSHIFT);
    public static final Token ADD =         new Operator(Constants.opNames[Constants.ADD],
                                           Constants.ADD);
    public static final Token SUB =         new Operator(Constants.opNames[Constants.SUB],
                                           Constants.SUB);
    public static final Token DIV =         new Operator(Constants.opNames[Constants.DIV],
                                           Constants.DIV);
    public static final Token REM =         new Operator(Constants.opNames[Constants.REM],
                                           Constants.REM);
    public static final Token MUL =         new Operator(Constants.opNames[Constants.MUL],
                                           Constants.MUL);
    public static final Token CAST =        new Operator(Constants.opNames[Constants.CAST],
                                           Constants.CAST);
    public static final Token POS =         new Operator(Constants.opNames[Constants.POS],
                                           Constants.POS);
    public static final Token NEG =         new Operator(Constants.opNames[Constants.NEG],
                                           Constants.NEG);
    public static final Token NOT =         new Operator(Constants.opNames[Constants.NOT],
                                           Constants.NOT);
    public static final Token BITNOT =      new Operator(Constants.opNames[Constants.BITNOT],
                                           Constants.BITNOT);
    public static final Token PREINC =      new Operator(Constants.opNames[Constants.PREINC],
                                           Constants.PREINC);
    public static final Token PREDEC =      new Operator(Constants.opNames[Constants.PREDEC],
                                           Constants.PREDEC);
    public static final Token NEWARRAY =    new Operator(Constants.opNames[Constants.NEWARRAY],
                                           Constants.NEWARRAY);
    public static final Token NEWINSTANCE = new Operator(Constants.opNames[Constants.NEWINSTANCE],
                                           Constants.NEWINSTANCE);
    public static final Token NEWFROMNAME = new Operator(Constants.opNames[Constants.NEWFROMNAME],
                                           Constants.NEWFROMNAME);
    public static final Token POSTINC =     new Operator(Constants.opNames[Constants.POSTINC],
                                           Constants.POSTINC);
    public static final Token POSTDEC =     new Operator(Constants.opNames[Constants.POSTDEC],
                                           Constants.POSTDEC);
    public static final Token FIELD =       new Operator(Constants.opNames[Constants.FIELD],
                                           Constants.FIELD);
    public static final Token METHOD =      new Operator(Constants.opNames[Constants.METHOD],
                                           Constants.METHOD);
    public static final Token ARRAYACCESS = new Operator(Constants.opNames[Constants.ARRAYACCESS],
                                           Constants.ARRAYACCESS);
    public static final Token NEW =         new Operator(Constants.opNames[Constants.NEW],
                                           Constants.NEW);
    public static final Token INC =         new Operator(Constants.opNames[Constants.INC],
                                           Constants.INC);
    public static final Token DEC =         new Operator(Constants.opNames[Constants.DEC],
                                           Constants.DEC);
    public static final Token CONVERT =     new Operator(Constants.opNames[Constants.CONVERT],
                                           Constants.CONVERT);
    public static final Token EXPR =        new Operator(Constants.opNames[Constants.EXPR],
                                           Constants.EXPR);
    public static final Token ARRAY =       new Operator(Constants.opNames[Constants.ARRAY],
                                           Constants.ARRAY);
    public static final Token GOTO =        new Operator(Constants.opNames[Constants.GOTO],
                                           Constants.GOTO);
    /*
     * Value tokens
     */
    public static final Token IDENT =       new Value(Constants.opNames[Constants.IDENT],
                                          Constants.IDENT);
    public static final Token BOOLEANVAL =  new Value(Constants.opNames[Constants.BOOLEANVAL],
                                          Constants.BOOLEANVAL);
    public static final Token BYTEVAL =     new Value(Constants.opNames[Constants.BYTEVAL],
                                          Constants.BYTEVAL);
    public static final Token CHARVAL =     new Value(Constants.opNames[Constants.CHARVAL],
                                          Constants.CHARVAL);
    public static final Token SHORTVAL =    new Value(Constants.opNames[Constants.SHORTVAL],
                                          Constants.SHORTVAL);
    public static final Token INTVAL =      new Value(Constants.opNames[Constants.INTVAL],
                                          Constants.INTVAL);
    public static final Token LONGVAL =     new Value(Constants.opNames[Constants.LONGVAL],
                                          Constants.LONGVAL);
    public static final Token FLOATVAL =    new Value(Constants.opNames[Constants.FLOATVAL],
                                          Constants.FLOATVAL);
    public static final Token DOUBLEVAL =   new Value(Constants.opNames[Constants.DOUBLEVAL],
                                          Constants.DOUBLEVAL);
    public static final Token STRINGVAL =   new Value(Constants.opNames[Constants.STRINGVAL],
                                          Constants.STRINGVAL);
    /*
     * Type keywords
     */
    public static final Token BYTE =        new Type(Constants.opNames[Constants.BYTE],
                                         Constants.BYTE);
    public static final Token CHAR =        new Type(Constants.opNames[Constants.CHAR],
                                         Constants.CHAR);
    public static final Token SHORT =       new Type(Constants.opNames[Constants.SHORT],
                                         Constants.SHORT);
    public static final Token INT =         new Type(Constants.opNames[Constants.INT],
                                         Constants.INT);
    public static final Token LONG =        new Type(Constants.opNames[Constants.LONG],
                                         Constants.LONG);
    public static final Token FLOAT =       new Type(Constants.opNames[Constants.FLOAT],
                                         Constants.FLOAT);
    public static final Token DOUBLE =      new Type(Constants.opNames[Constants.DOUBLE],
                                         Constants.DOUBLE);
    public static final Token VOID =        new Type(Constants.opNames[Constants.VOID],
                                         Constants.VOID);
    public static final Token BOOLEAN =     new Type(Constants.opNames[Constants.BOOLEAN],
                                         Constants.BOOLEAN);
    /*
     * Expression keywords
     */
    public static final Token TRUE =        new Expression(Constants.opNames[Constants.TRUE],
                                             Constants.TRUE);
    public static final Token FALSE =       new Expression(Constants.opNames[Constants.FALSE],
                                             Constants.FALSE);
    public static final Token THIS =        new Expression(Constants.opNames[Constants.THIS],
                                             Constants.THIS);
    public static final Token SUPER =       new Expression(Constants.opNames[Constants.SUPER],
                                             Constants.SUPER);
    public static final Token NULL =        new Expression(Constants.opNames[Constants.NULL],
                                             Constants.NULL);
    /*
     * Statement keywords
     */
    public static final Token IF =             new Statement(Constants.opNames[Constants.IF],
                                               Constants.IF);
    public static final Token ELSE =           new Statement(Constants.opNames[Constants.ELSE],
                                               Constants.ELSE);
    public static final Token FOR =            new Statement(Constants.opNames[Constants.FOR],
                                               Constants.FOR);
    public static final Token WHILE =          new Statement(Constants.opNames[Constants.WHILE],
                                               Constants.WHILE);
    public static final Token DO =             new Statement(Constants.opNames[Constants.DO],
                                               Constants.DO);
    public static final Token SWITCH =         new Statement(Constants.opNames[Constants.SWITCH],
                                               Constants.SWITCH);
    public static final Token CASE =           new Statement(Constants.opNames[Constants.CASE],
                                               Constants.CASE);
    public static final Token DEFAULT =        new Statement(Constants.opNames[Constants.DEFAULT],
                                               Constants.DEFAULT);
    public static final Token BREAK =          new Statement(Constants.opNames[Constants.BREAK],
                                               Constants.BREAK);
    public static final Token CONTINUE =       new Statement(Constants.opNames[Constants.CONTINUE],
                                               Constants.CONTINUE);
    public static final Token RETURN =         new Statement(Constants.opNames[Constants.RETURN],
                                               Constants.RETURN);
    public static final Token TRY =            new Statement(Constants.opNames[Constants.TRY],
                                               Constants.TRY);
    public static final Token CATCH =          new Statement(Constants.opNames[Constants.CATCH],
                                               Constants.CATCH);
    public static final Token FINALLY =        new Statement(Constants.opNames[Constants.FINALLY],
                                               Constants.FINALLY);
    public static final Token THROW =          new Statement(Constants.opNames[Constants.THROW],
                                               Constants.THROW);
    public static final Token STAT =           new Statement(Constants.opNames[Constants.STAT],
                                               Constants.STAT);
    public static final Token EXPRESSION =     new Statement(Constants.opNames[Constants.EXPRESSION],
                                               Constants.EXPRESSION);
    public static final Token DECLARATION =    new Statement(Constants.opNames[Constants.DECLARATION],
                                               Constants.DECLARATION);
    public static final Token VARDECLARATION = new Statement(Constants.opNames[Constants.VARDECLARATION],
                                               Constants.VARDECLARATION);
    /*
     * Declaration keywords
     */
    public static final Token IMPORT =         new Declaration(Constants.opNames[Constants.IMPORT],
                                                 Constants.IMPORT);
    public static final Token CLASS =          new Declaration(Constants.opNames[Constants.CLASS],
                                                 Constants.CLASS);
    public static final Token EXTENDS =        new Declaration(Constants.opNames[Constants.EXTENDS],
                                                 Constants.EXTENDS);
    public static final Token IMPLEMENTS =     new Declaration(Constants.opNames[Constants.IMPLEMENTS],
                                                 Constants.IMPLEMENTS);
    public static final Token INTERFACE =      new Declaration(Constants.opNames[Constants.INTERFACE],
                                                 Constants.INTERFACE);
    public static final Token PACKAGE =        new Declaration(Constants.opNames[Constants.PACKAGE],
                                                 Constants.PACKAGE);
    /*
     * Modifier keywords
     */
    public static final Token PRIVATE =        new Modifier(Constants.opNames[Constants.PRIVATE],
                                              Constants.PRIVATE);
    public static final Token PUBLIC =         new Modifier(Constants.opNames[Constants.PUBLIC],
                                              Constants.PUBLIC);
    public static final Token PROTECTED =      new Modifier(Constants.opNames[Constants.PROTECTED],
                                              Constants.PROTECTED);
    public static final Token CONST =          new Modifier(Constants.opNames[Constants.CONST],
                                              Constants.CONST);
    public static final Token STATIC =         new Modifier(Constants.opNames[Constants.STATIC],
                                              Constants.STATIC);
    public static final Token TRANSIENT =      new Modifier(Constants.opNames[Constants.TRANSIENT],
                                              Constants.TRANSIENT);
    public static final Token SYNCHRONIZED =   new Modifier(Constants.opNames[Constants.SYNCHRONIZED],
                                              Constants.SYNCHRONIZED);
    public static final Token NATIVE =         new Modifier(Constants.opNames[Constants.NATIVE],
                                              Constants.NATIVE);
    public static final Token FINAL =          new Modifier(Constants.opNames[Constants.FINAL],
                                              Constants.FINAL);
    public static final Token VOLATILE =       new Modifier(Constants.opNames[Constants.VOLATILE],
                                              Constants.VOLATILE);
    public static final Token ABSTRACT =       new Modifier(Constants.opNames[Constants.ABSTRACT],
                                              Constants.ABSTRACT);

    /*
     * Punctuation
     */
    public static final Token SEMICOLON =      new Punctuation(Constants.opNames[Constants.SEMICOLON],
                                                 Constants.SEMICOLON);
    public static final Token COLON =          new Punctuation(Constants.opNames[Constants.COLON],
                                                 Constants.COLON);
    public static final Token QUESTIONMARK =   new Punctuation(Constants.opNames[Constants.QUESTIONMARK],
                                                 Constants.QUESTIONMARK);
    public static final Token LBRACE =         new Punctuation(Constants.opNames[Constants.LBRACE],
                                                 Constants.LBRACE);
    public static final Token RBRACE =         new Punctuation(Constants.opNames[Constants.RBRACE],
                                                 Constants.RBRACE);
    public static final Token LPAREN =         new Punctuation(Constants.opNames[Constants.LPAREN],
                                                 Constants.LPAREN);
    public static final Token RPAREN =         new Punctuation(Constants.opNames[Constants.RPAREN],
                                                 Constants.RPAREN);
    public static final Token LSQBRACKET =     new Punctuation(Constants.opNames[Constants.LSQBRACKET],
                                                 Constants.LSQBRACKET);
    public static final Token RSQBRACKET =     new Punctuation(Constants.opNames[Constants.RSQBRACKET],
                                                 Constants.RSQBRACKET);
    public static final Token THROWS =         new Punctuation(Constants.opNames[Constants.THROWS],
                                                 Constants.THROWS);

    /*
     * Special tokens
     */
    public static final Token ERROR =             new Special(Constants.opNames[Constants.ERROR],
                                                Constants.ERROR);
    public static final Token COMMENT =           new Special(Constants.opNames[Constants.COMMENT],
                                                Constants.COMMENT);
    public static final Token TYPE =              new Special(Constants.opNames[Constants.TYPE],
                                                Constants.TYPE);
    public static final Token LENGTH =            new Special(Constants.opNames[Constants.LENGTH],
                                                Constants.LENGTH);
    public static final Token INLINERETURN =      new Special(Constants.opNames[Constants.INLINERETURN],
                                                Constants.INLINERETURN);
    public static final Token INLINEMETHOD =      new Special(Constants.opNames[Constants.INLINEMETHOD],
                                                Constants.INLINEMETHOD);
    public static final Token INLINENEWINSTANCE = new Special(Constants.opNames[Constants.INLINENEWINSTANCE],
                                                Constants.INLINENEWINSTANCE);
    public static final Token UNSCANNED =         new Special("unscanned", MaximumScanValue);

    static Token[] operators = {
      COMMA, ASSIGN, ASGMUL, ASGDIV, ASGREM, ASGADD, ASGSUB, ASGLSHIFT,
      ASGRSHIFT, ASGURSHIFT, ASGBITAND, ASGBITOR, ASGBITXOR, COND, OR, AND,
      BITOR, BITXOR, BITAND, NE, EQ, GE, GT, LE, LT, INSTANCEOF, LSHIFT,
      RSHIFT, URSHIFT, ADD, SUB, DIV, REM, MUL, CAST, POS, NEG, NOT, BITNOT,
      PREINC, PREDEC, NEWARRAY, NEWINSTANCE, NEWFROMNAME, POSTINC, POSTDEC,
      FIELD, METHOD, ARRAYACCESS, NEW, INC, DEC, CONVERT, EXPR, ARRAY, GOTO
    };

    static Token[] values = {
      IDENT, BOOLEANVAL, BYTEVAL, CHARVAL, SHORTVAL, INTVAL, LONGVAL,
      FLOATVAL, DOUBLEVAL, STRINGVAL
    };

    static Token[] types = {
      BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, VOID, BOOLEAN
    };

    static Token[] expressions = {
      TRUE, FALSE, THIS, SUPER, NULL
    };

    static Token[] statements = {
      IF, ELSE, FOR, WHILE, DO, SWITCH, CASE, DEFAULT, BREAK,
      CONTINUE, RETURN, TRY, CATCH, FINALLY, THROW, STAT, EXPRESSION,
      DECLARATION, VARDECLARATION
    };

    static Token[] declarations = {
      IMPORT, CLASS, EXTENDS, IMPLEMENTS, INTERFACE, PACKAGE
    };

    static Token[] modifiers = {
      PRIVATE, PUBLIC, PROTECTED, CONST, STATIC, TRANSIENT, SYNCHRONIZED,
      NATIVE, FINAL, VOLATILE, ABSTRACT
    };

    static Token[] punctuations = {
      SEMICOLON, COLON, QUESTIONMARK, LBRACE, RBRACE, LPAREN,
      RPAREN, LSQBRACKET, RSQBRACKET, THROWS
    };

    static Token[] specials = {
      ERROR, COMMENT, TYPE, LENGTH, INLINERETURN, INLINEMETHOD, INLINENEWINSTANCE, UNSCANNED
    };

    static Token[] all = {
      COMMA, ASSIGN, ASGMUL, ASGDIV, ASGREM, ASGADD, ASGSUB, ASGLSHIFT,
      ASGRSHIFT, ASGURSHIFT, ASGBITAND, ASGBITOR, ASGBITXOR, COND, OR, AND,
      BITOR, BITXOR, BITAND, NE, EQ, GE, GT, LE, LT, INSTANCEOF, LSHIFT,
      RSHIFT, URSHIFT, ADD, SUB, DIV, REM, MUL, CAST, POS, NEG, NOT, BITNOT,
      PREINC, PREDEC, NEWARRAY, NEWINSTANCE, NEWFROMNAME, POSTINC, POSTDEC,
      FIELD, METHOD, ARRAYACCESS, NEW, INC, DEC, CONVERT, EXPR, ARRAY, GOTO,
      IDENT, BOOLEANVAL, BYTEVAL, CHARVAL, SHORTVAL, INTVAL, LONGVAL,
      FLOATVAL, DOUBLEVAL, STRINGVAL,
      BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, VOID, BOOLEAN,
      TRUE, FALSE, THIS, SUPER, NULL,
      IF, ELSE, FOR, WHILE, DO, SWITCH, CASE, DEFAULT, BREAK,
      CONTINUE, RETURN, TRY, CATCH, FINALLY, THROW, STAT, EXPRESSION,
      DECLARATION, VARDECLARATION,
      IMPORT, CLASS, EXTENDS, IMPLEMENTS, INTERFACE, PACKAGE,
      PRIVATE, PUBLIC, PROTECTED, CONST, STATIC, TRANSIENT, SYNCHRONIZED,
      NATIVE, FINAL, VOLATILE, ABSTRACT,
      SEMICOLON, COLON, QUESTIONMARK, LBRACE, RBRACE, LPAREN,
      RPAREN, LSQBRACKET, RSQBRACKET, THROWS,
      ERROR, COMMENT, TYPE, LENGTH, INLINERETURN, INLINEMETHOD, INLINENEWINSTANCE, UNSCANNED
    };

}

=============> JavaEditorKit.java

/*
 * @(#)JavaEditorKit.java      1.2 98/05/04
 *
 * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 */
package FGI.GUI.JavaKitEditor;

import javax.swing.text.*;

/**
 * This kit supports a fairly minimal handling of
 * editing java text content.  It supports syntax
 * highlighting and produces the lexical structure
 * of the document as best it can.
 *
 * @author  Timothy Prinzing
 * @version 1.2 05/04/98
 */
public class JavaEditorKit extends DefaultEditorKit {

    public JavaEditorKit() {
      super();
    }

    public JavaContext getStylePreferences() {
      if (preferences == null) {
          preferences = new JavaContext();
      }
      return preferences;
    }

    public void setStylePreferences(JavaContext prefs) {
      preferences = prefs;
    }

    // --- EditorKit methods -------------------------

    /**
     * Get the MIME type of the data that this
     * kit represents support for.  This kit supports
     * the type <code>text/java</code>.
     */
    public String getContentType() {
      return "text/java";
    }

    /**
     * Create a copy of the editor kit.  This
     * allows an implementation to serve as a prototype
     * for others, so that they can be quickly created.
     */
    public Object clone() {
      JavaEditorKit kit = new JavaEditorKit();
      kit.preferences = preferences;
      return kit;
    }

    /**
     * Creates an uninitialized text storage model
     * that is appropriate for this type of editor.
     *
     * @return the model
     */
    public Document createDefaultDocument() {
      return new JavaDocument();
    }

    /**
     * Fetches a factory that is suitable for producing
     * views of any models that are produced by this
     * kit.  The default is to have the UI produce the
     * factory, so this method has no implementation.
     *
     * @return the view factory
     */
    public final ViewFactory getViewFactory() {
      return getStylePreferences();

    }

    JavaContext preferences;
}

To execute it, you need to have something like this:

    JEditorPane editorPane = new JEditorPane(); //the editor pane
    editorPane.setEditable(false); //the document cannot be edited

    JavaEditorKit kit = new JavaEditorKit();
    editorPane.setEditorKitForContentType("text/java", kit);
    editorPane.setContentType("text/java");
    editorPane.setBackground(Color.white);
    editorPane.setFont(new Font("Courier", 0, 12));    
    JavaContext styles = kit.getStylePreferences();
    Style s;
    s = styles.getStyleForScanValue(Token.COMMENT.getScanValue());
    StyleConstants.setForeground(s, new Color(102, 153, 153));
    s = styles.getStyleForScanValue(Token.STRINGVAL.getScanValue());
    StyleConstants.setForeground(s, new Color(102, 153, 102));
    Color keyword = new Color(102, 102, 255);
    for (int code = 70; code <= 130; code++) {
      s = styles.getStyleForScanValue(code);
      if (s != null) {
        StyleConstants.setForeground(s, keyword);
      }
    }


So now my problem is how do I make the colour of the line numbers black?


Thank you!

ASKER CERTIFIED SOLUTION
Avatar of Javatm
Javatm
Flag of Singapore image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of _MW_
_MW_

ASKER

ahh...didn't notice that that method existed...thanks dude
Avatar of _MW_

ASKER

I just noticed that there is another problem. When I scroll up and down the document that is opened, the colour for the tokens for example comments which should be green, actually does not refresh fast enough and will actually change to black or blue. Any idea how to fix that?


Thanks..