Fast styled JTextPane editor

807606
    Dear all,

    I'm trying to create an editor which adds word style and make autocompletion when there are recognized.
    All is performed correctly nevertheless, when i have a lot a text, each new inserted character or word takes a long time. How to optimize the jtextpane performance ???

    Each time a character is typed, my document notify a update event and then
    i get the content line to add style to the specific word.

    example code :
    //here we need to identify position in line for suggestion in popup menu
    //and need to validate the contents line for adding recognized word style.
    protected void update() {
              
              CommandWordDescriptor wordDescriptor = CommandLineParser.getInstance().getCommandWordDescriptor(getPositionInLine());
              setWordDescriptor(wordDescriptor);
              
              if (document.getLength()>0) {
                   validate(getLineContents().substring(commandPrompt.length()));
              }
              
         }
    
    private String getLineContents() {
              
              String contents = null;
    
              int startOffset = getStartOffset();
              int length = getEndOffset() - startOffset;
              
              try {
                   contents = textPane.getText(startOffset, length);
              } catch (BadLocationException e) {
                   e.printStackTrace();
              }
              
              return contents;
         }
    
    protected int getNumberOfLines() {
              return document.getDefaultRootElement().getElementIndex(document.getLength());
         }
         
         protected int getCurrentLineNumber() {
              
              int dot = textPane.getCaretPosition();
              //get current line
              int line = document.getDefaultRootElement().getElementIndex(dot);
              
              return line;
         }
         
         protected int getPositionInLine() {
              
              int dot = textPane.getCaretPosition();
              //get current line
              int line = document.getDefaultRootElement().getElementIndex(dot);
              
              //caret position : 0 = value is before prompt
              //                       1 = value is prompt
              //                       2 = value is space after prompt....
              int caretPositionOnLine = dot - document.getDefaultRootElement().getElement(line).getStartOffset();
              //text position after prompt sign from 0 to end of command line
              int textPositionOnLine = caretPositionOnLine - commandPrompt.length();
              
              return textPositionOnLine;
         }
         
         public int getCaretPositionOnLine() {
              
              int dot = textPane.getCaretPosition();
              //get current line
              int line = document.getDefaultRootElement().getElementIndex(dot);
              
              //caret position : 0 = value is before prompt
              //                       1 = value is prompt
              //                       2 = value is space after prompt....
              int caretPositionOnLine = dot - document.getDefaultRootElement().getElement(line).getStartOffset();
              
              return caretPositionOnLine;
         }
         
         private int getStartOffset(){
              
              int dot = textPane.getCaretPosition();
              //get current line
              int line = document.getDefaultRootElement().getElementIndex(dot);
              int startOffset = document.getDefaultRootElement().getElement(line).getStartOffset();
              
              return startOffset;
         }
         
         private int getEndOffset(){
              
              int dot = textPane.getCaretPosition();
              //get current line
              int line = document.getDefaultRootElement().getElementIndex(dot);
              int endOffset = document.getDefaultRootElement().getElement(line).getEndOffset() - 1;
              
              return endOffset;
         }
         
         protected int getAbsolutePosition(int positionInLine) {
              //startoffset in current line
              return getStartOffset() + positionInLine + commandPrompt.length();
         }
    Thanks in advance for any helps.....
      • 1. Re: Fast styled JTextPane editor
        camickr
        In the future, Swing related questions should be posted in the Swing forum.

        Based on the code posted I don't see any reason why the performance should be noticeably slower for a larger Document vs a smaller Document. I don't see any loops or anything that would cause performance to slow down as more lines are added. Sorry.
        • 2. Re: Fast styled JTextPane editor
          807606
          Sorry for swing forum, i'm just finished with this post and in the future i will try to not send message here...


          I have forgotten to specify that each time a character is typed, i modify the attribuset for managing text color..

          Example code :
          protected void setWordLookAndFeel(CommandWordDescriptor commandWordDescriptor) {
                    AttributeSet attributeSet = addStyle(commandWordDescriptor);
                    int startPosition = getAbsolutePosition(commandWordDescriptor.getStartPosition());
                    document.setCharacterAttributes(startPosition,commandWordDescriptor.getText().length(),attributeSet,true);
               }
          Is it possible that colored text in document when you have a lot of character could reduced the performance...

          I have seen in java technique site that you can create ElementSpec and perform super.insert(offs, ElementSpec[]) instead of using super.insertString.... in order to increase loading performance ....

          What do you thing about this ????

          Thanks you ...
          • 3. Re: Fast styled JTextPane editor
            camickr
            Is it possible that colored text in document when you have a lot of character could reduced the performance...
            Again your code seems to only operate on a single line of code, so it should not show a performance impact based on the number of lines in the file.

            Here is my example program that does Java syntax highlighting. The keyword highlighting is the same whether the source file is 500 or 5,000 lines long:

            http://www.discoverteenergy.com/files/SyntaxDocument.java
            I have seen in java technique site that you can create ElementSpec
            I have never used an ElementSpec so I can't help here either.
            • 4. Re: Fast styled JTextPane editor
              807606
              I have used your example for my application, i have only modify some font and color.After i have typed 30 lines with different color, it goes slower and slower...

              lines content : abstract gogo boolean toto = break tata byte gaga byvalue . I write this 30 times.
              package swing.editor.document;
              
              import java.awt.BorderLayout;
              import java.awt.Color;
              import java.awt.event.ActionEvent;
              import java.awt.event.ActionListener;
              import java.io.FileInputStream;
              import java.util.HashSet;
              
              import javax.swing.JButton;
              import javax.swing.JEditorPane;
              import javax.swing.JFrame;
              import javax.swing.JScrollPane;
              import javax.swing.text.AttributeSet;
              import javax.swing.text.BadLocationException;
              import javax.swing.text.DefaultEditorKit;
              import javax.swing.text.DefaultStyledDocument;
              import javax.swing.text.Document;
              import javax.swing.text.EditorKit;
              import javax.swing.text.Element;
              import javax.swing.text.MutableAttributeSet;
              import javax.swing.text.SimpleAttributeSet;
              import javax.swing.text.StyleConstants;
              import javax.swing.text.StyledEditorKit;
              
              class SyntaxDocument extends DefaultStyledDocument
              {
                   private DefaultStyledDocument doc;
                   private Element rootElement;
              
                   private boolean multiLineComment;
                   private MutableAttributeSet normal;
                   private MutableAttributeSet keyword;
                   private MutableAttributeSet comment;
                   private MutableAttributeSet quote;
              
                   private HashSet keywords;
              
                   public SyntaxDocument()
                   {
                        doc = this;
                        rootElement = doc.getDefaultRootElement();
                        putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );
              
                        normal = new SimpleAttributeSet();
                        StyleConstants.setBold(normal, true);
                        StyleConstants.setForeground(normal, Color.black);
              
                        comment = new SimpleAttributeSet();
                        StyleConstants.setForeground(comment, Color.gray);
                        StyleConstants.setItalic(comment, true);
              
                        keyword = new SimpleAttributeSet();
                        StyleConstants.setBold(keyword, true);
                        StyleConstants.setForeground(keyword, Color.blue);
              
                        quote = new SimpleAttributeSet();
                        StyleConstants.setForeground(quote, Color.red);
                        StyleConstants.setBold(quote, true);
                        
                        keywords = new HashSet();
                        keywords.add( "abstract" );
                        keywords.add( "boolean" );
                        keywords.add( "break" );
                        keywords.add( "byte" );
                        keywords.add( "byvalue" );
                        keywords.add( "case" );
                        keywords.add( "cast" );
                        keywords.add( "catch" );
                        keywords.add( "char" );
                        keywords.add( "class" );
                        keywords.add( "const" );
                        keywords.add( "continue" );
                        keywords.add( "default" );
                        keywords.add( "do" );
                        keywords.add( "double" );
                        keywords.add( "else" );
                        keywords.add( "extends" );
                        keywords.add( "false" );
                        keywords.add( "final" );
                        keywords.add( "finally" );
                        keywords.add( "float" );
                        keywords.add( "for" );
                        keywords.add( "future" );
                        keywords.add( "generic" );
                        keywords.add( "goto" );
                        keywords.add( "if" );
                        keywords.add( "implements" );
                        keywords.add( "import" );
                        keywords.add( "inner" );
                        keywords.add( "instanceof" );
                        keywords.add( "int" );
                        keywords.add( "interface" );
                        keywords.add( "long" );
                        keywords.add( "native" );
                        keywords.add( "new" );
                        keywords.add( "null" );
                        keywords.add( "operator" );
                        keywords.add( "outer" );
                        keywords.add( "package" );
                        keywords.add( "private" );
                        keywords.add( "protected" );
                        keywords.add( "public" );
                        keywords.add( "rest" );
                        keywords.add( "return" );
                        keywords.add( "short" );
                        keywords.add( "static" );
                        keywords.add( "super" );
                        keywords.add( "switch" );
                        keywords.add( "synchronized" );
                        keywords.add( "this" );
                        keywords.add( "throw" );
                        keywords.add( "throws" );
                        keywords.add( "transient" );
                        keywords.add( "true" );
                        keywords.add( "try" );
                        keywords.add( "var" );
                        keywords.add( "void" );
                        keywords.add( "volatile" );
                        keywords.add( "while" );
                   }
              
                   /*
                    *  Override to apply syntax highlighting after the document has been updated
                    */
                   public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
                   {
                        if (str.equals("{"))
                             str = addMatchingBrace(offset);
              
                        super.insertString(offset, str, a);
                        processChangedLines(offset, str.length());
                   }
              
                   /*
                    *  Override to apply syntax highlighting after the document has been updated
                    */
                   public void remove(int offset, int length) throws BadLocationException
                   {
                        super.remove(offset, length);
                        processChangedLines(offset, 0);
                   }
              
                   /*
                    *  Determine how many lines have been changed,
                    *  then apply highlighting to each line
                    */
                   public void processChangedLines(int offset, int length)
                        throws BadLocationException
                   {
                        String content = doc.getText(0, doc.getLength());
              
                        //  The lines affected by the latest document update
              
                        int startLine = rootElement.getElementIndex( offset );
                        int endLine = rootElement.getElementIndex( offset + length );
              
                        //  Make sure all comment lines prior to the start line are commented
                        //  and determine if the start line is still in a multi line comment
              
                        setMultiLineComment( commentLinesBefore( content, startLine ) );
              
                        //  Do the actual highlighting
              
                        for (int i = startLine; i <= endLine; i++)
                        {
                             applyHighlighting(content, i);
                        }
              
                        //  Resolve highlighting to the next end multi line delimiter
              
                        if (isMultiLineComment())
                             commentLinesAfter(content, endLine);
                        else
                             highlightLinesAfter(content, endLine);
                   }
              
                   /*
                    *  Highlight lines when a multi line comment is still 'open'
                    *  (ie. matching end delimiter has not yet been encountered)
                    */
                   private boolean commentLinesBefore(String content, int line)
                   {
                        int offset = rootElement.getElement( line ).getStartOffset();
              
                        //  Start of comment not found, nothing to do
              
                        int startDelimiter = lastIndexOf( content, getStartDelimiter(), offset - 2 );
              
                        if (startDelimiter < 0)
                             return false;
              
                        //  Matching start/end of comment found, nothing to do
              
                        int endDelimiter = indexOf( content, getEndDelimiter(), startDelimiter );
              
                        if (endDelimiter < offset & endDelimiter != -1)
                             return false;
              
                        //  End of comment not found, highlight the lines
              
                        doc.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1, comment, false);
                        return true;
                   }
              
                   /*
                    *  Highlight comment lines to matching end delimiter
                    */
                   private void commentLinesAfter(String content, int line)
                   {
                        int offset = rootElement.getElement( line ).getEndOffset();
              
                        //  End of comment not found, nothing to do
              
                        int endDelimiter = indexOf( content, getEndDelimiter(), offset );
              
                        if (endDelimiter < 0)
                             return;
              
                        //  Matching start/end of comment found, comment the lines
              
                        int startDelimiter = lastIndexOf( content, getStartDelimiter(), endDelimiter );
              
                        if (startDelimiter < 0 || startDelimiter <= offset)
                        {
                             doc.setCharacterAttributes(offset, endDelimiter - offset + 1, comment, false);
                        }
                   }
              
                   /*
                    *  Highlight lines to start or end delimiter
                    */
                   private void highlightLinesAfter(String content, int line)
                        throws BadLocationException
                   {
                        int offset = rootElement.getElement( line ).getEndOffset();
              
                        //  Start/End delimiter not found, nothing to do
              
                        int startDelimiter = indexOf( content, getStartDelimiter(), offset );
                        int endDelimiter = indexOf( content, getEndDelimiter(), offset );
              
                        if (startDelimiter < 0)
                             startDelimiter = content.length();
              
                        if (endDelimiter < 0)
                             endDelimiter = content.length();
              
                        int delimiter = Math.min(startDelimiter, endDelimiter);
              
                        if (delimiter < offset)
                             return;
              
                        //     Start/End delimiter found, reapply highlighting
              
                        int endLine = rootElement.getElementIndex( delimiter );
              
                        for (int i = line + 1; i < endLine; i++)
                        {
                             Element branch = rootElement.getElement( i );
                             Element leaf = doc.getCharacterElement( branch.getStartOffset() );
                             AttributeSet as = leaf.getAttributes();
              
                             if ( as.isEqual(comment) )
                                  applyHighlighting(content, i);
                        }
                   }
              
                   /*
                    *  Parse the line to determine the appropriate highlighting
                    */
                   private void applyHighlighting(String content, int line)
                        throws BadLocationException
                   {
                        int startOffset = rootElement.getElement( line ).getStartOffset();
                        int endOffset = rootElement.getElement( line ).getEndOffset() - 1;
              
                        int lineLength = endOffset - startOffset;
                        int contentLength = content.length();
              
                        if (endOffset >= contentLength)
                             endOffset = contentLength - 1;
              
                        //  check for multi line comments
                        //  (always set the comment attribute for the entire line)
              
                        if (endingMultiLineComment(content, startOffset, endOffset)
                        ||  isMultiLineComment()
                        ||  startingMultiLineComment(content, startOffset, endOffset) )
                        {
                             doc.setCharacterAttributes(startOffset, endOffset - startOffset + 1, comment, false);
                             return;
                        }
              
                        //  set normal attributes for the line
              
                        doc.setCharacterAttributes(startOffset, lineLength, normal, true);
              
                        //  check for single line comment
              
                        int index = content.indexOf(getSingleLineDelimiter(), startOffset);
              
                        if ( (index > -1) && (index < endOffset) )
                        {
                             doc.setCharacterAttributes(index, endOffset - index + 1, comment, false);
                             endOffset = index - 1;
                        }
              
                        //  check for tokens
              
                        checkForTokens(content, startOffset, endOffset);
                   }
              
                   /*
                    *  Does this line contain the start delimiter
                    */
                   private boolean startingMultiLineComment(String content, int startOffset, int endOffset)
                        throws BadLocationException
                   {
                        int index = indexOf( content, getStartDelimiter(), startOffset );
              
                        if ( (index < 0) || (index > endOffset) )
                             return false;
                        else
                        {
                             setMultiLineComment( true );
                             return true;
                        }
                   }
              
                   /*
                    *  Does this line contain the end delimiter
                    */
                   private boolean endingMultiLineComment(String content, int startOffset, int endOffset)
                        throws BadLocationException
                   {
                        int index = indexOf( content, getEndDelimiter(), startOffset );
              
                        if ( (index < 0) || (index > endOffset) )
                             return false;
                        else
                        {
                             setMultiLineComment( false );
                             return true;
                        }
                   }
              
                   /*
                    *  We have found a start delimiter
                    *  and are still searching for the end delimiter
                    */
                   private boolean isMultiLineComment()
                   {
                        return multiLineComment;
                   }
              
                   private void setMultiLineComment(boolean value)
                   {
                        multiLineComment = value;
                   }
              
                   /*
                    *     Parse the line for tokens to highlight
                    */
                   private void checkForTokens(String content, int startOffset, int endOffset)
                   {
                        while (startOffset <= endOffset)
                        {
                             //  skip the delimiters to find the start of a new token
              
                             while ( isDelimiter( content.substring(startOffset, startOffset + 1) ) )
                             {
                                  if (startOffset < endOffset)
                                       startOffset++;
                                  else
                                       return;
                             }
              
                             //  Extract and process the entire token
              
                             if ( isQuoteDelimiter( content.substring(startOffset, startOffset + 1) ) )
                                  startOffset = getQuoteToken(content, startOffset, endOffset);
                             else
                                  startOffset = getOtherToken(content, startOffset, endOffset);
                        }
                   }
              
                   /*
                    *
                    */
                   private int getQuoteToken(String content, int startOffset, int endOffset)
                   {
                        String quoteDelimiter = content.substring(startOffset, startOffset + 1);
                        String escapeString = getEscapeString(quoteDelimiter);
              
                        int index;
                        int endOfQuote = startOffset;
              
                        //  skip over the escape quotes in this quote
              
                        index = content.indexOf(escapeString, endOfQuote + 1);
              
                        while ( (index > -1) && (index < endOffset) )
                        {
                             endOfQuote = index + 1;
                             index = content.indexOf(escapeString, endOfQuote);
                        }
              
                        // now find the matching delimiter
              
                        index = content.indexOf(quoteDelimiter, endOfQuote + 1);
              
                        if ( (index < 0) || (index > endOffset) )
                             endOfQuote = endOffset;
                        else
                             endOfQuote = index;
              
                        doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, quote, false);
              
                        return endOfQuote + 1;
                   }
              
                   /*
                    *
                    */
                   private int getOtherToken(String content, int startOffset, int endOffset)
                   {
                        int endOfToken = startOffset + 1;
              
                        while ( endOfToken <= endOffset )
                        {
                             if ( isDelimiter( content.substring(endOfToken, endOfToken + 1) ) )
                                  break;
              
                             endOfToken++;
                        }
              
                        String token = content.substring(startOffset, endOfToken);
              
                        if ( isKeyword( token ) )
                        {
                             doc.setCharacterAttributes(startOffset, endOfToken - startOffset, keyword, false);
                        }else{
                             doc.setCharacterAttributes(startOffset, endOfToken - startOffset, quote, false);
                        }
              
                        return endOfToken + 1;
                   }
              
                   /*
                    *  Assume the needle will the found at the start/end of the line
                    */
                   private int indexOf(String content, String needle, int offset)
                   {
                        int index;
              
                        while ( (index = content.indexOf(needle, offset)) != -1 )
                        {
                             String text = getLine( content, index ).trim();
              
                             if (text.startsWith(needle) || text.endsWith(needle))
                                  break;
                             else
                                  offset = index + 1;
                        }
              
                        return index;
                   }
              
                   /*
                    *  Assume the needle will the found at the start/end of the line
                    */
                   private int lastIndexOf(String content, String needle, int offset)
                   {
                        int index;
              
                        while ( (index = content.lastIndexOf(needle, offset)) != -1 )
                        {
                             String text = getLine( content, index ).trim();
              
                             if (text.startsWith(needle) || text.endsWith(needle))
                                  break;
                             else
                                  offset = index - 1;
                        }
              
                        return index;
                   }
              
                   private String getLine(String content, int offset)
                   {
                        int line = rootElement.getElementIndex( offset );
                        Element lineElement = rootElement.getElement( line );
                        int start = lineElement.getStartOffset();
                        int end = lineElement.getEndOffset();
                        return content.substring(start, end - 1);
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected boolean isDelimiter(String character)
                   {
                        String operands = ";:{}()[]+-/%<=>!&|^~*";
              
                        if (Character.isWhitespace( character.charAt(0) ) ||
                             operands.indexOf(character) != -1 )
                             return true;
                        else
                             return false;
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected boolean isQuoteDelimiter(String character)
                   {
                        String quoteDelimiters = "\"'";
              
                        if (quoteDelimiters.indexOf(character) < 0)
                             return false;
                        else
                             return true;
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected boolean isKeyword(String token)
                   {
                        return keywords.contains( token );
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected String getStartDelimiter()
                   {
                        return "/*";
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected String getEndDelimiter()
                   {
                        return "*/";
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected String getSingleLineDelimiter()
                   {
                        return "//";
                   }
              
                   /*
                    *  Override for other languages
                    */
                   protected String getEscapeString(String quoteDelimiter)
                   {
                        return "\\" + quoteDelimiter;
                   }
              
                   /*
                    *
                    */
                   protected String addMatchingBrace(int offset) throws BadLocationException
                   {
                        StringBuffer whiteSpace = new StringBuffer();
                        int line = rootElement.getElementIndex( offset );
                        int i = rootElement.getElement(line).getStartOffset();
              
                        while (true)
                        {
                             String temp = doc.getText(i, 1);
              
                             if (temp.equals(" ") || temp.equals("\t"))
                             {
                                  whiteSpace.append(temp);
                                  i++;
                             }
                             else
                                  break;
                        }
              
                        return "{\n" + whiteSpace.toString() + "\t\n" + whiteSpace.toString() + "}";
                   }
              
              
                   public static void main(String a[])
                   {
              
                        EditorKit editorKit = new StyledEditorKit()
                        {
                             public Document createDefaultDocument()
                             {
                                  return new SyntaxDocument();
                             }
                        };
              
                        final JEditorPane edit = new JEditorPane();
                        edit.setEditorKitForContentType("text/java", editorKit);
                        edit.setContentType("text/java");
              //          edit.setEditorKit(new StyledEditorKit());
              //          edit.setDocument(new SyntaxDocument());
              
              //          JButton button = new JButton("Load SyntaxDocument.java");
              //          button.addActionListener( new ActionListener()
              //          {
              //               public void actionPerformed(ActionEvent e)
              //               {
              //                    try
              //                    {
              ////                         FileInputStream fis = new FileInputStream( "swing\\editor\\SyntaxDocument.java" );
              ////                         FileInputStream fis = new FileInputStream( "C:\\Java\\jdk1.4.1\\src\\javax\\swing\\JComponent.java" );
              //                         edit.read( fis, null );
              //                         edit.requestFocus();
              //                    }
              //                    catch(Exception e2) {}
              //               }
              //          });
              
                        JFrame frame = new JFrame("Syntax Highlighting");
                        frame.getContentPane().add( new JScrollPane(edit) );
              //          frame.getContentPane().add(button, BorderLayout.SOUTH);
                        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                        frame.setSize(800,300);
                        frame.setVisible(true);
                   }
              }
              Thans for your helps because i need for my editor....
              • 5. Re: Fast styled JTextPane editor (syntax highlighter)
                807606
                camickr:

                Thanks very much for the link to your Syntax Highlighter code. It was almost exactly what I was looking for -- a simple way to just do some basic coloring of code.

                I augmented your example code so that users of the class can set format of the various MutableAttributeSet 's through their code vice modifying the class itself.

                I also added the ability to set the size of a tab -- mostly using the tab stop code you wrote in a different post on these forums.

                Finally, I made a new class MultiSyntaxHighlighter.java which expanded on your class to allow the user to associate any number of keywords with any number of different styles vice being limited to just one style for keywords.

                I also created two classes for showing how each of the highlighters based on your original main method, but adapted to work with the new stuff I mentioned above.

                Thanks for your help.

                The files:

                http://www.dound.com/src/SyntaxDocument.java
                http://www.dound.com/src/Main.java

                http://www.dound.com/src/MultiSyntaxDocument.java
                http://www.dound.com/src/MainForMulti.java
                • 6. Re: Fast styled JTextPane editor (syntax highlighter)
                  807606
                  //Filename: MultiSyntaxDocument.java
                  
                  import java.awt.*;
                  import java.awt.event.*;
                  import java.io.*;
                  import java.net.*;
                  import java.util.*;
                  import javax.swing.*;
                  import javax.swing.text.*;
                  
                  /**
                   * Highlights syntax in a DefaultStyledDocument.  Allows any number of keywords to 
                   * be formatted in any number of user-defined styles.
                   *
                   * @author camickr (primary author; java sun forums user)
                   * @author David Underhill
                   */
                  class MultiSyntaxDocument extends DefaultStyledDocument
                  {
                      
                      //<editor-fold defaultstate="collapsed" desc="        Defaults         ">
                      
                      public static final String DEFAULT_FONT_FAMILY = "Courier New";
                      public static final int    DEFAULT_FONT_SIZE   = 11;
                             
                      public static final SimpleAttributeSet DEFAULT_NORMAL;
                      public static final SimpleAttributeSet DEFAULT_COMMENT;
                      public static final SimpleAttributeSet DEFAULT_STRING;
                      public static final SimpleAttributeSet DEFAULT_KEYWORD;
                      
                      static {
                          DEFAULT_NORMAL = new SimpleAttributeSet();
                            StyleConstants.setForeground( DEFAULT_NORMAL, Color.BLACK);
                          StyleConstants.setFontFamily( DEFAULT_NORMAL, DEFAULT_FONT_FAMILY );
                          StyleConstants.setFontSize(   DEFAULT_NORMAL, DEFAULT_FONT_SIZE );
                          
                            DEFAULT_COMMENT = new SimpleAttributeSet();
                            StyleConstants.setForeground(DEFAULT_COMMENT, new java.awt.Color( 51, 102, 0 ) ); //dark green
                            StyleConstants.setFontFamily( DEFAULT_COMMENT, DEFAULT_FONT_FAMILY );
                          StyleConstants.setFontSize(   DEFAULT_COMMENT, DEFAULT_FONT_SIZE );
                          
                            DEFAULT_STRING = new SimpleAttributeSet();
                            StyleConstants.setForeground( DEFAULT_STRING, new java.awt.Color( 153, 0, 107 ) ); //dark pink
                          StyleConstants.setFontFamily( DEFAULT_STRING, DEFAULT_FONT_FAMILY );
                          StyleConstants.setFontSize(   DEFAULT_STRING, DEFAULT_FONT_SIZE );
                          
                          //default style for new keyword types
                          DEFAULT_KEYWORD = new SimpleAttributeSet();
                            StyleConstants.setForeground( DEFAULT_KEYWORD, new java.awt.Color( 0, 0, 153 ) ); //dark blue
                          StyleConstants.setBold(       DEFAULT_KEYWORD, true );
                          StyleConstants.setFontFamily( DEFAULT_KEYWORD, DEFAULT_FONT_FAMILY );
                          StyleConstants.setFontSize(   DEFAULT_KEYWORD, DEFAULT_FONT_SIZE );
                      }
                      
                      //</editor-fold>
                      
                      
                      //<editor-fold defaultstate="collapsed" desc="         Fields          ">
                      
                       private DefaultStyledDocument doc;
                       private Element rootElement;
                  
                       private boolean multiLineComment;
                       private MutableAttributeSet normal  = DEFAULT_NORMAL;
                       private MutableAttributeSet comment = DEFAULT_COMMENT;
                       private MutableAttributeSet quote   = DEFAULT_STRING;
                  
                       private HashMap<String, MutableAttributeSet> keywords;
                      
                      private int fontSize    = DEFAULT_FONT_SIZE;
                      private String fontName = DEFAULT_FONT_FAMILY;
                      
                      //</editor-fold>
                  
                      
                      //<editor-fold defaultstate="collapsed" desc="      Constructors       ">
                      
                       public MultiSyntaxDocument( final HashMap<String, MutableAttributeSet> keywords ) {
                            doc = this;
                            rootElement = doc.getDefaultRootElement();
                            putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );
                          
                            this.keywords = keywords;
                       }
                      
                      //</editor-fold>
                  
                      
                      //<editor-fold defaultstate="collapsed" desc="  Highlighting Setters  ">
                      
                      public enum ATTR_TYPE { Normal, Comment, Quote; }
                          
                      /**
                       * Sets the font of the specified attribute
                       * @param attr   the attribute to apply this font to (normal, comment, string)
                       * @param style  font style (Font.BOLD, Font.ITALIC, Font.PLAIN)
                       */
                      public void setAttributeFont( ATTR_TYPE attr, int style ) {
                          Font f = new Font( fontName, style, fontSize );
                          
                          if(      attr == ATTR_TYPE.Comment ) setAttributeFont( comment, f );
                          else if( attr == ATTR_TYPE.Quote   ) setAttributeFont( quote,   f );
                          else                                 setAttributeFont( normal,  f );
                      }
                      
                      /**
                       * Sets the font of the specified attribute
                       * @param attr  attribute to apply this font to
                       * @param f     the font to use 
                       */
                      public static void setAttributeFont( MutableAttributeSet attr, Font f ) {
                          StyleConstants.setBold(         attr, f.isBold()                      );
                          StyleConstants.setItalic(       attr, f.isItalic()                    );
                          StyleConstants.setFontFamily(   attr, f.getFamily()                   );
                          StyleConstants.setFontSize(     attr, f.getSize()                     );
                      }
                      
                      /**
                       * Sets the foreground (font) color of the specified attribute
                       * @param attr  the attribute to apply this font to (normal, comment, string)
                       * @param c     the color to use 
                       */
                      public void setAttributeColor( ATTR_TYPE attr, Color c ) {
                          if(      attr == ATTR_TYPE.Comment ) setAttributeColor( comment, c );
                          else if( attr == ATTR_TYPE.Quote   ) setAttributeColor( quote,   c );
                          else                                 setAttributeColor( normal,  c );
                      }
                      
                      /**
                       * Sets the foreground (font) color of the specified attribute
                       * @param attr  attribute to apply this color to
                       * @param c  the color to use 
                       */
                      public static void setAttributeColor( MutableAttributeSet attr, Color c ) {
                            StyleConstants.setForeground( attr, c );
                      }
                      
                      //</editor-fold>
                      
                      
                      //<editor-fold defaultstate="collapsed" desc="  Keyword Specification ">
                      
                      /**
                       * Associates a keyword with a particular formatting style
                       *
                       * @param keyword  the token or word to format
                       * @param attr     how to format keyword
                       */
                      public void addKeyword( String keyword, MutableAttributeSet attr ) {
                          keywords.put( keyword, attr );
                      }
                      
                      /**
                       * Gets the formatting for a keyword
                       *
                       * @param keyword  the token or word to stop formatting
                       *
                       * @return how keyword is formatted, or null if no formatting is applied to it
                       */
                      public MutableAttributeSet getKeywordFormatting( String keyword ) {
                          return keywords.get( keyword );
                      }
                      
                      /**
                       * Removes an association between a keyword with a particular formatting style
                       *
                       * @param keyword  the token or word to stop formatting
                       */
                      public void removeKeyword( String keyword ) {
                          keywords.remove( keyword );
                      }
                      
                      //</editor-fold>
                      
                      
                      //<editor-fold defaultstate="collapsed" desc="     Tab Size Setup     ">
                      
                      /** sets the number of characters per tab */
                      public void setTabs( int charactersPerTab) {
                          Font f = new Font( fontName, Font.PLAIN, fontSize );
                          
                            FontMetrics fm = java.awt.Toolkit.getDefaultToolkit().getFontMetrics( f );
                            int charWidth = fm.charWidth( 'w' );
                            int tabWidth = charWidth * charactersPerTab;
                   
                            TabStop[] tabs = new TabStop[35];
                   
                            for (int j = 0; j < tabs.length; j++)
                            {
                                 int tab = j + 1;
                                 tabs[j] = new TabStop( tab * tabWidth );
                            }
                   
                            TabSet tabSet = new TabSet(tabs);
                            SimpleAttributeSet attributes = new SimpleAttributeSet();
                            StyleConstants.setTabSet(attributes, tabSet);
                            int length = this.getLength();
                            this.setParagraphAttributes(0, length, attributes, false);
                       }
                      
                      //</editor-fold>
                      
                      
                      //<editor-fold defaultstate="collapsed" desc=" Syntax Highlighting   ">
                      
                       /*
                        *  Override to apply syntax highlighting after the document has been updated
                        */
                       public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
                       {
                            if (str.equals("{"))
                                 str = addMatchingBrace(offset);
                  
                            super.insertString(offset, str, a);
                          processChangedLines(offset, str.length());
                       }
                  
                       /*
                        *  Override to apply syntax highlighting after the document has been updated
                        */
                       public void remove(int offset, int length) throws BadLocationException
                       {
                            super.remove(offset, length);
                            processChangedLines(offset, 0);
                       }
                  
                       /*
                        *  Determine how many lines have been changed,
                        *  then apply highlighting to each line
                        */
                       public void processChangedLines(int offset, int length)
                            throws BadLocationException
                       {
                            String content = doc.getText(0, doc.getLength());
                  
                            //  The lines affected by the latest document update
                  
                            int startLine = rootElement.getElementIndex( offset );
                            int endLine = rootElement.getElementIndex( offset + length );
                  
                            //  Make sure all comment lines prior to the start line are commented
                            //  and determine if the start line is still in a multi line comment
                  
                            setMultiLineComment( commentLinesBefore( content, startLine ) );
                  
                            //  Do the actual highlighting
                  
                            for (int i = startLine; i <= endLine; i++)
                            {
                                 applyHighlighting(content, i);
                            }
                  
                            //  Resolve highlighting to the next end multi line delimiter
                  
                            if (isMultiLineComment())
                                 commentLinesAfter(content, endLine);
                            else
                                 highlightLinesAfter(content, endLine);
                       }
                  
                       /*
                        *  Highlight lines when a multi line comment is still 'open'
                        *  (ie. matching end delimiter has not yet been encountered)
                        */
                       private boolean commentLinesBefore(String content, int line)
                       {
                            int offset = rootElement.getElement( line ).getStartOffset();
                  
                            //  Start of comment not found, nothing to do
                  
                            int startDelimiter = lastIndexOf( content, getStartDelimiter(), offset - 2 );
                  
                            if (startDelimiter < 0)
                                 return false;
                  
                            //  Matching start/end of comment found, nothing to do
                  
                            int endDelimiter = indexOf( content, getEndDelimiter(), startDelimiter );
                  
                            if (endDelimiter < offset & endDelimiter != -1)
                                 return false;
                  
                            //  End of comment not found, highlight the lines
                  
                            doc.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1, comment, false);
                            return true;
                       }
                  
                       /*
                        *  Highlight comment lines to matching end delimiter
                        */
                       private void commentLinesAfter(String content, int line)
                       {
                            int offset = rootElement.getElement( line ).getEndOffset();
                  
                            //  End of comment not found, nothing to do
                  
                            int endDelimiter = indexOf( content, getEndDelimiter(), offset );
                  
                            if (endDelimiter < 0)
                                 return;
                  
                            //  Matching start/end of comment found, comment the lines
                  
                            int startDelimiter = lastIndexOf( content, getStartDelimiter(), endDelimiter );
                  
                            if (startDelimiter < 0 || startDelimiter <= offset)
                            {
                                 doc.setCharacterAttributes(offset, endDelimiter - offset + 1, comment, false);
                            }
                       }
                  
                       /*
                        *  Highlight lines to start or end delimiter
                        */
                       private void highlightLinesAfter(String content, int line)
                            throws BadLocationException
                       {
                            int offset = rootElement.getElement( line ).getEndOffset();
                  
                            //  Start/End delimiter not found, nothing to do
                  
                            int startDelimiter = indexOf( content, getStartDelimiter(), offset );
                            int endDelimiter = indexOf( content, getEndDelimiter(), offset );
                  
                            if (startDelimiter < 0)
                                 startDelimiter = content.length();
                  
                            if (endDelimiter < 0)
                                 endDelimiter = content.length();
                  
                            int delimiter = Math.min(startDelimiter, endDelimiter);
                  
                            if (delimiter < offset)
                                 return;
                  
                            //     Start/End delimiter found, reapply highlighting
                  
                            int endLine = rootElement.getElementIndex( delimiter );
                  
                            for (int i = line + 1; i < endLine; i++)
                            {
                                 Element branch = rootElement.getElement( i );
                                 Element leaf = doc.getCharacterElement( branch.getStartOffset() );
                                 AttributeSet as = leaf.getAttributes();
                  
                                 if ( as.isEqual(comment) )
                                      applyHighlighting(content, i);
                            }
                       }
                  
                       /*
                        *  Parse the line to determine the appropriate highlighting
                        */
                       private void applyHighlighting(String content, int line)
                            throws BadLocationException
                       {
                            int startOffset = rootElement.getElement( line ).getStartOffset();
                            int endOffset = rootElement.getElement( line ).getEndOffset() - 1;
                  
                            int lineLength = endOffset - startOffset;
                            int contentLength = content.length();
                  
                            if (endOffset >= contentLength)
                                 endOffset = contentLength - 1;
                  
                            //  check for multi line comments
                            //  (always set the comment attribute for the entire line)
                  
                            if (endingMultiLineComment(content, startOffset, endOffset)
                            ||  isMultiLineComment()
                            ||  startingMultiLineComment(content, startOffset, endOffset) )
                            {
                                 doc.setCharacterAttributes(startOffset, endOffset - startOffset + 1, comment, false);
                                 return;
                            }
                  
                            //  set normal attributes for the line
                  
                            doc.setCharacterAttributes(startOffset, lineLength, normal, true);
                  
                            //  check for single line comment
                  
                            int index = content.indexOf(getSingleLineDelimiter(), startOffset);
                  
                            if ( (index > -1) && (index < endOffset) )
                            {
                                 doc.setCharacterAttributes(index, endOffset - index + 1, comment, false);
                                 endOffset = index - 1;
                            }
                  
                            //  check for tokens
                  
                            checkForTokens(content, startOffset, endOffset);
                       }
                  
                       /*
                        *  Does this line contain the start delimiter
                        */
                       private boolean startingMultiLineComment(String content, int startOffset, int endOffset)
                            throws BadLocationException
                       {
                            int index = indexOf( content, getStartDelimiter(), startOffset );
                  
                            if ( (index < 0) || (index > endOffset) )
                                 return false;
                            else
                            {
                                 setMultiLineComment( true );
                                 return true;
                            }
                       }
                  
                       /*
                        *  Does this line contain the end delimiter
                        */
                       private boolean endingMultiLineComment(String content, int startOffset, int endOffset)
                            throws BadLocationException
                       {
                            int index = indexOf( content, getEndDelimiter(), startOffset );
                  
                            if ( (index < 0) || (index > endOffset) )
                                 return false;
                            else
                            {
                                 setMultiLineComment( false );
                                 return true;
                            }
                       }
                  
                       /*
                        *  We have found a start delimiter
                        *  and are still searching for the end delimiter
                        */
                       private boolean isMultiLineComment()
                       {
                            return multiLineComment;
                       }
                  
                       private void setMultiLineComment(boolean value)
                       {
                            multiLineComment = value;
                       }
                  
                       /*
                        *     Parse the line for tokens to highlight
                        */
                       private void checkForTokens(String content, int startOffset, int endOffset)
                       {
                            while (startOffset <= endOffset)
                            {
                                 //  skip the delimiters to find the start of a new token
                  
                                 while ( isDelimiter( content.substring(startOffset, startOffset + 1) ) )
                                 {
                                      if (startOffset < endOffset)
                                           startOffset++;
                                      else
                                           return;
                                 }
                  
                                 //  Extract and process the entire token
                  
                                 if ( isQuoteDelimiter( content.substring(startOffset, startOffset + 1) ) )
                                      startOffset = getQuoteToken(content, startOffset, endOffset);
                                 else
                                      startOffset = getOtherToken(content, startOffset, endOffset);
                            }
                       }
                  
                       /*
                        *
                        */
                       private int getQuoteToken(String content, int startOffset, int endOffset)
                       {
                            String quoteDelimiter = content.substring(startOffset, startOffset + 1);
                            String escapeString = getEscapeString(quoteDelimiter);
                  
                            int index;
                            int endOfQuote = startOffset;
                  
                            //  skip over the escape quotes in this quote
                  
                            index = content.indexOf(escapeString, endOfQuote + 1);
                  
                            while ( (index > -1) && (index < endOffset) )
                            {
                                 endOfQuote = index + 1;
                                 index = content.indexOf(escapeString, endOfQuote);
                            }
                  
                            // now find the matching delimiter
                  
                            index = content.indexOf(quoteDelimiter, endOfQuote + 1);
                  
                            if ( (index < 0) || (index > endOffset) )
                                 endOfQuote = endOffset;
                            else
                                 endOfQuote = index;
                  
                            doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, quote, false);
                  
                            return endOfQuote + 1;
                       }
                  
                       /*
                        *
                        */
                       private int getOtherToken(String content, int startOffset, int endOffset)
                       {
                            int endOfToken = startOffset + 1;
                  
                            while ( endOfToken <= endOffset )
                            {
                                 if ( isDelimiter( content.substring(endOfToken, endOfToken + 1) ) )
                                      break;
                  
                                 endOfToken++;
                            }
                  
                            String token = content.substring(startOffset, endOfToken);
                  
                          //see if this token has a highlighting format associated with it
                          MutableAttributeSet attr = keywords.get( token );
                            if( attr != null )
                            {
                                 doc.setCharacterAttributes(startOffset, endOfToken - startOffset, attr, false);
                            }
                  
                            return endOfToken + 1;
                       }
                  
                       /*
                        *  Assume the needle will the found at the start/end of the line
                        */
                       private int indexOf(String content, String needle, int offset)
                       {
                            int index;
                  
                            while ( (index = content.indexOf(needle, offset)) != -1 )
                            {
                                 String text = getLine( content, index ).trim();
                  
                                 if (text.startsWith(needle) || text.endsWith(needle))
                                      break;
                                 else
                                      offset = index + 1;
                            }
                  
                            return index;
                       }
                  
                       /*
                        *  Assume the needle will the found at the start/end of the line
                        */
                       private int lastIndexOf(String content, String needle, int offset)
                       {
                            int index;
                  
                            while ( (index = content.lastIndexOf(needle, offset)) != -1 )
                            {
                                 String text = getLine( content, index ).trim();
                  
                                 if (text.startsWith(needle) || text.endsWith(needle))
                                      break;
                                 else
                                      offset = index - 1;
                            }
                  
                            return index;
                       }
                  
                       private String getLine(String content, int offset)
                       {
                            int line = rootElement.getElementIndex( offset );
                            Element lineElement = rootElement.getElement( line );
                            int start = lineElement.getStartOffset();
                            int end = lineElement.getEndOffset();
                            return content.substring(start, end - 1);
                       }
                  
                       /*
                        *  Override for other languages
                        */
                       protected boolean isDelimiter(String character)
                       {
                            String operands = ";:{}()[]+-/%<=>!&|^~*";
                  
                            if (Character.isWhitespace( character.charAt(0) ) ||
                                 operands.indexOf(character) != -1 )
                                 return true;
                            else
                                 return false;
                       }
                  
                       /*
                        *  Override for other languages
                        */
                       protected boolean isQuoteDelimiter(String character)
                       {
                            String quoteDelimiters = "\"'";
                  
                            if (quoteDelimiters.indexOf(character) < 0)
                                 return false;
                            else
                                 return true;
                       }
                  
                       /*
                        *  Override for other languages
                        */
                       protected String getStartDelimiter()
                       {
                            return "/*";
                       }
                  
                       /*
                        *  Override for other languages
                        */
                       protected String getEndDelimiter()
                       {
                            return "*/";
                       }
                  
                       /*
                        *  Override for other languages
                        */
                       protected String getSingleLineDelimiter()
                       {
                            return "//";
                       }
                  
                       /*
                        *  Override for other languages
                        */
                       protected String getEscapeString(String quoteDelimiter)
                       {
                            return "\\" + quoteDelimiter;
                       }
                  
                       /*
                        *
                        */
                       protected String addMatchingBrace(int offset) throws BadLocationException
                       {
                            StringBuffer whiteSpace = new StringBuffer();
                            int line = rootElement.getElementIndex( offset );
                            int i = rootElement.getElement(line).getStartOffset();
                  
                            while (true)
                            {
                                 String temp = doc.getText(i, 1);
                  
                                 if (temp.equals(" ") || temp.equals("\t"))
                                 {
                                      whiteSpace.append(temp);
                                      i++;
                                 }
                                 else
                                      break;
                            }
                  
                            return "{\n" + whiteSpace.toString() + "\t\n" + whiteSpace.toString() + "}";
                       }
                  
                      //</editor-fold>
                  
                      
                      //<editor-fold defaultstate="collapsed" desc="  Accessors/Mutators   ">
                      
                      /** gets the current font size */
                      public int getFontSize() { return fontSize; }
                  
                      /** sets the current font size (affects all built-in styles) */
                      public void setFontSize(int fontSize) {
                          this.fontSize = fontSize;
                          StyleConstants.setFontSize(   normal,  fontSize );
                          StyleConstants.setFontSize(   quote,   fontSize );
                          StyleConstants.setFontSize(   comment, fontSize );
                      }
                  
                      /** gets the current font family */
                      public String getFontName() { return fontName; }
                  
                      /** sets the current font family (affects all built-in styles) */
                      public void setFontName(String fontName) {
                          this.fontName = fontName;
                          StyleConstants.setFontFamily( normal,  fontName );
                          StyleConstants.setFontFamily( quote,   fontName );
                          StyleConstants.setFontFamily( comment, fontName );
                      }
                      
                      
                      //</editor-fold>
                      
                  }