Package net.sourceforge.squirrel_sql.plugins.syntax.oster

Source Code of net.sourceforge.squirrel_sql.plugins.syntax.oster.OsterTextControl

package net.sourceforge.squirrel_sql.plugins.syntax.oster;
/*
* Copyright (C) 2003 Colin Bell
* colbell@users.sourceforge.net
*
* This is based on the text editor demonstration class that comes with
* the Ostermiller Syntax Highlighter Copyright (C) 2001 Stephen Ostermiller
* http://ostermiller.org/contact.pl?regarding=Syntax+Highlighting
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
import com.Ostermiller.Syntax.Lexer.Lexer;
import com.Ostermiller.Syntax.Lexer.SQLLexer;
import com.Ostermiller.Syntax.Lexer.Token;
import net.sourceforge.squirrel_sql.client.session.ExtendedColumnInfo;
import net.sourceforge.squirrel_sql.client.session.ISession;
import net.sourceforge.squirrel_sql.client.session.SQLTokenListener;
import net.sourceforge.squirrel_sql.client.session.schemainfo.SchemaInfo;
import net.sourceforge.squirrel_sql.client.session.parser.ParserEventsAdapter;
import net.sourceforge.squirrel_sql.client.session.parser.kernel.ErrorInfo;
import net.sourceforge.squirrel_sql.fw.gui.FontInfo;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
import net.sourceforge.squirrel_sql.fw.id.IIdentifier;
import net.sourceforge.squirrel_sql.plugins.syntax.IConstants;
import net.sourceforge.squirrel_sql.plugins.syntax.SyntaxPreferences;
import net.sourceforge.squirrel_sql.plugins.syntax.SyntaxStyle;
import net.sourceforge.squirrel_sql.plugins.syntax.KeyManager;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.Reader;
import java.util.*;

public class OsterTextControl extends JTextPane
{
    private static final long serialVersionUID = 1L;

    /** Logger for this class. */
  private static final ILogger s_log = LoggerController.createLogger(OsterTextControl.class);

  /** Current session. */
  private final ISession _session;

  /**
   * A lock for modifying the document, or for
   * actions that depend on the document not being
   * modified.
   */
  private Object doclock = new Object();

  /**
   * The styled document that is the model for
   * the textPane.
   */
  private HighLightedDocument document;

  /**
   * A reader wrapped around the document
   * so that the document can be fed into
   * the lexer.
   */
  private DocumentReader documentReader;

  /**
   * The lexer that tells us what colors different
   * words should be.
   */
  private Lexer syntaxLexer;

  /**
   * A thread that handles the actual coloring.
   */
  private Colorer colorer;

  /**
   * A hash table containing the text styles.
   * Simple attribute sets are hashed by name (String)
   */
  private Hashtable<String, SimpleAttributeSet> styles =
      new Hashtable<String, SimpleAttributeSet>();

  /** Preferences for this plugin. */
  private final SyntaxPreferences _syntaxPrefs;

  private Vector<SQLTokenListener> _sqlTokenListeners =
      new Vector<SQLTokenListener>();
  private Vector<ErrorInfo> _currentErrorInfos = new Vector<ErrorInfo>();
   private Vector<ErrorInfo> _oldErrorInfos = new Vector<ErrorInfo>();


   OsterTextControl(ISession session, SyntaxPreferences prefs, final IIdentifier sqlEntryPanelIdentifier)
  {
    super();
    _session = session;
    _syntaxPrefs = prefs;

    document = new HighLightedDocument();
    setDocument(document);

    // Start the thread that does the coloring
    colorer = new Colorer();
    colorer.start();

    // Set up the hash table that contains the styles.
    initStyles();

    // create the new document.
    documentReader = new DocumentReader(document);

    // Put the initial text into the text pane and
    // set it's initial coloring style.
    initDocument();

      setToolTipText("Just to make getToolTiptext() to be called");

    updateFromPreferences();

      SwingUtilities.invokeLater(new Runnable()
      {
         public void run()
         {
            initParser(_session, sqlEntryPanelIdentifier);
         }
      });

      new KeyManager(this);
  }

   private void initParser(ISession session, IIdentifier sqlEntryPanelIdentifier)
   {
      session.getParserEventsProcessor(sqlEntryPanelIdentifier).addParserEventsListener(new ParserEventsAdapter()
    {
      public void errorsFound(ErrorInfo[] errorInfos)
      {
        onErrorsFound(errorInfos);
      }
    });
   }

   private void onErrorsFound(ErrorInfo[] errorInfos)
  {
      boolean errorsChanged = false;
      if(_currentErrorInfos.size() == errorInfos.length)
      {
         for (int i = 0; i < errorInfos.length; i++)
         {
            if(false == errorInfos[i].equals(_currentErrorInfos.get(i)))
            {
               errorsChanged = true;
               break;
            }
         }
      }
      else
      {
         errorsChanged = true;
      }

      if(errorsChanged)
      {
         _oldErrorInfos.clear();
         _oldErrorInfos.addAll(_currentErrorInfos);

         _currentErrorInfos.clear();
         _currentErrorInfos.addAll(Arrays.asList(errorInfos));

         int heuristicDist = 20;

         for (int i = 0; i < errorInfos.length; i++)
         {
            int colBegin = Math.max(errorInfos[i].beginPos - heuristicDist, 0);
            int colLen = Math.min(errorInfos[i].endPos - errorInfos[i].beginPos + 2*heuristicDist, getDocument().getLength() - colBegin);
            color(colBegin, colLen, false, null);
         }

         for (int i = 0; i < _oldErrorInfos.size(); i++)
         {
            ErrorInfo errorInfo = _oldErrorInfos.elementAt(i);
            int colBegin = Math.max(errorInfo.beginPos - heuristicDist, 0);
            int colLen = Math.min(errorInfo.endPos - errorInfo.beginPos + 2*heuristicDist, getDocument().getLength() - colBegin);
            color(colBegin, colLen, false, null);
         }
      }
  }

   public void endColorerThread()
   {
      colorer.endThread();
   }

  // This stops the text control from line wrapping.
  public boolean getScrollableTracksViewportWidth()
  {
    final Component parent = getParent();
    final ComponentUI ui = getUI();

    if (parent != null)
    {
      return (ui.getPreferredSize(this).width <= parent.getSize().width);
    }
    return true;
  }

  void updateFromPreferences()
  {
    synchronized (doclock)
    {
      final FontInfo fi = _session.getProperties().getFontInfo();
      SyntaxStyle style;
      SimpleAttributeSet attribs;

      style = _syntaxPrefs.getColumnStyle();
      attribs = getMyStyle(IConstants.IStyleNames.COLUMN);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getCommentStyle();
      attribs = getMyStyle(IConstants.IStyleNames.COMMENT);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getDataTypeStyle();
      attribs = getMyStyle(IConstants.IStyleNames.DATA_TYPE);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getErrorStyle();
      attribs = getMyStyle(IConstants.IStyleNames.ERROR);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getFunctionStyle();
      attribs = getMyStyle(IConstants.IStyleNames.FUNCTION);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getIdentifierStyle();
      attribs = getMyStyle(IConstants.IStyleNames.IDENTIFIER);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getLiteralStyle();
      attribs = getMyStyle(IConstants.IStyleNames.LITERAL);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getOperatorStyle();
      attribs = getMyStyle(IConstants.IStyleNames.OPERATOR);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getReservedWordStyle();
      attribs = getMyStyle(IConstants.IStyleNames.RESERVED_WORD);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getSeparatorStyle();
      attribs = getMyStyle(IConstants.IStyleNames.SEPARATOR);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getTableStyle();
      attribs = getMyStyle(IConstants.IStyleNames.TABLE);
      applyStyle(attribs, style, fi);

      style = _syntaxPrefs.getWhiteSpaceStyle();
      attribs = getMyStyle(IConstants.IStyleNames.WHITESPACE);
      applyStyle(attribs, style, fi);

      colorAll();
    }
  }

  /**
   * Color or recolor the entire document
   */
  public void colorAll()
  {
    color(0, document.getLength(), false, null);
  }

  /**
   * Color a section of the document.
   * The actual coloring will start somewhere before
   * the requested position and continue as long
   * as needed.
   *
   * @param position    the starting point for the coloring.
   * @param adjustment  amount of text inserted or removed
   *            at the starting point.
   */
  public void color(int position, int adjustment, boolean fireTableOrViewFoundEvent, String change)
  {
    colorer.color(position, adjustment, fireTableOrViewFoundEvent, change);
  }

  /**
   * retrieve the style for the given type of text.
   *
   * @param styleName  the label for the type of text ("tag" for example)
   *          or null if the styleName is not known.
   * @return the style
   */
  private SimpleAttributeSet getMyStyle(String styleName)
  {
    return styles.get(styleName);
  }

  private Token getNextToken() throws IOException
  {
      return syntaxLexer.getNextToken();
  }

  private void applyStyle(SimpleAttributeSet attribs, SyntaxStyle style,
                FontInfo fi)
  {
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, new Color(style.getBackgroundRGB()));
    StyleConstants.setForeground(attribs, new Color(style.getTextRGB()));
    StyleConstants.setBold(attribs, style.isBold());
    StyleConstants.setItalic(attribs, style.isItalic());
  }

  /**
   * Set the initial type of syntax highlighting.
   */
  private void initDocument()
  {
    syntaxLexer = new SQLLexer(documentReader);
    try
    {
      document.insertString(document.getLength(), "", getMyStyle("text"));
    }
    catch (BadLocationException ex)
    {
      s_log.error("Error setting initial document style", ex);
    }
  }

  /**
   * Create the styles and place them in the hash table.
   */
  private void initStyles()
  {
    final FontInfo fi = _session.getProperties().getFontInfo();

    SyntaxStyle style;
    SimpleAttributeSet attribs;

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, Color.black);
    StyleConstants.setBold(attribs, false);
    StyleConstants.setItalic(attribs, false);
    styles.put("body", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, Color.blue);
    StyleConstants.setBold(attribs, true);
    StyleConstants.setItalic(attribs, false);
    styles.put("tag", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, Color.blue);
    StyleConstants.setBold(attribs, false);
    StyleConstants.setItalic(attribs, false);
    styles.put("endtag", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, Color.black);
    StyleConstants.setBold(attribs, false);
    StyleConstants.setItalic(attribs, false);
    styles.put("reference", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, new Color(0xB03060)/*Color.maroon*/);
    StyleConstants.setBold(attribs, true);
    StyleConstants.setItalic(attribs, false);
    styles.put("name", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, new Color(0xB03060)/*Color.maroon*/);
    StyleConstants.setBold(attribs, false);
    StyleConstants.setItalic(attribs, true);
    styles.put("value", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, Color.black);
    StyleConstants.setBold(attribs, true);
    StyleConstants.setItalic(attribs, false);
    styles.put("text", attribs);

    style = _syntaxPrefs.getColumnStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.COLUMN, attribs);

    style = _syntaxPrefs.getCommentStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.COMMENT, attribs);

    style = _syntaxPrefs.getDataTypeStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.DATA_TYPE, attribs);

    style = _syntaxPrefs.getErrorStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.ERROR, attribs);

    style = _syntaxPrefs.getFunctionStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.FUNCTION, attribs);

    style = _syntaxPrefs.getIdentifierStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.IDENTIFIER, attribs);

    style = _syntaxPrefs.getLiteralStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.LITERAL, attribs);

    style = _syntaxPrefs.getOperatorStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.OPERATOR, attribs);

    style = _syntaxPrefs.getReservedWordStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.RESERVED_WORD, attribs);

    style = _syntaxPrefs.getSeparatorStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.SEPARATOR, attribs);

    style = _syntaxPrefs.getTableStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.TABLE, attribs);

    style = _syntaxPrefs.getWhiteSpaceStyle();
    attribs = new SimpleAttributeSet();
    applyStyle(attribs, style, fi);
    styles.put(IConstants.IStyleNames.WHITESPACE, attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, new Color(0xA020F0).darker());
    StyleConstants.setBold(attribs, false);
    StyleConstants.setItalic(attribs, false);
    styles.put("preprocessor", attribs);

    // TODO: Do we need this one. */
    attribs = new SimpleAttributeSet();
    StyleConstants.setFontFamily(attribs, fi.getFamily());
    StyleConstants.setFontSize(attribs, fi.getSize());
    StyleConstants.setBackground(attribs, Color.white);
    StyleConstants.setForeground(attribs, Color.orange);
    StyleConstants.setBold(attribs, false);
    StyleConstants.setItalic(attribs, false);
    styles.put("unknown", attribs);
  }

  private class Colorer extends Thread
  {

      private boolean _endThread;
      private Vector<int[]> _currentLiteralAndCommentIntervals =
          new Vector<int[]>();
      private Hashtable<String, String> _knownTables =
          new Hashtable<String, String>();

      /**
     * A simple wrapper representing something that needs to be colored.
     * Placed into an object so that it can be stored in a Vector.
     */
    private class RecolorEvent
    {
      private int position;
      private int adjustment;
         private boolean fireTableOrViewFoundEvent;
         private String change;

         public RecolorEvent(int position, int adjustment, boolean fireTableOrViewFoundEvent, String change)
      {
        this.position = position;
        this.adjustment = adjustment;
            this.fireTableOrViewFoundEvent = fireTableOrViewFoundEvent;
            this.change = change;
      }
    }

    /**
     * Vector that stores the communication between the two threads.
     */
    private volatile Vector<RecolorEvent> recolorEventQueue =
        new Vector<RecolorEvent>();


    /**
     * The last position colored
     */

    private volatile boolean asleep = false;

    /**
     * When accessing the vector, we need to create a critical section.
     * we will synchronize on this object to ensure that we don't get
     * unsafe thread behavior.
     */
    private Object lock = new Object();

    /**
     * Tell the Syntax Highlighting thread to take another look at this
     * section of the document. It will process this as a FIFO.
     * This method should be done inside a doclock.
     */
    public void color(int position, int adjustment, boolean fireTableOrViewFoundEvent, String change)
    {
      synchronized (lock)
      {
        recolorEventQueue.add(new RecolorEvent(position, adjustment, fireTableOrViewFoundEvent, change));
        if (asleep)
        {
          this.interrupt();
        }
      }
    }

      public void endThread()
      {
         _endThread = true;
         if (asleep)
         {
            this.interrupt();
         }
      }


    /**
     * The colorer runs forever and may sleep for long
     * periods of time. It should be interrupted every
     * time there is something for it to do.
     */
    public void run()
    {
      int colorStartPos = -1;
      int colorLen = 0;
         boolean fireTableOrViewFoundEvent = true;
      // if we just finish, we can't go to sleep until we
      // ensure there is nothing else for us to do.
      // use try again to keep track of this.
      boolean tryAgain = false;
      for (;;)
      { // forever
        synchronized (lock)
        {
          if (recolorEventQueue.size() > 0)
          {
            RecolorEvent re = recolorEventQueue.elementAt(0);
            recolorEventQueue.removeElementAt(0);

                  if(null != re.change)
                  {
                     // Only if the text did really changed (null != re.change) Intervals
                     // must be adjusted.
                     // If the text did not change there is nothing to adjust.
                     adjustIntervalsToAdjustment(re);
                  }

                  colorStartPos = getColorStartPos(re);
                  colorLen = getColorLen(colorStartPos, re);


                  if(null != re.change)
                  {
                     if(   -1 != re.change.indexOf('\'')
                        || -1 != re.change.indexOf('/')
                        || -1 != re.change.indexOf('*')
                        || -1 != re.change.indexOf('-')
                        || null != getInvolvedLiteralOrCommentInterval(re.position))
                     {
                        reinitLiteralAndCommentIntervals();

                        int colorStartPos2 = getColorStartPos(re);
                        int colorLen2 = getColorLen(colorStartPos, re);

                        int newStartPos = Math.min(colorStartPos, colorStartPos2);
                        int newEndPos = Math.max(colorStartPos2 + colorLen2, colorStartPos + colorLen);

                        colorStartPos = newStartPos;
                        colorLen = newEndPos - newStartPos;
                     }
                  }


                  fireTableOrViewFoundEvent = re.fireTableOrViewFoundEvent;
          }
          else
          {
            tryAgain = false;
            colorStartPos = -1;
            colorLen = 0;
                  fireTableOrViewFoundEvent = false;

          }
        }
        if (colorStartPos != -1)
        {
          try
          {
            final SchemaInfo si = _session.getSchemaInfo();
            Token t;
            synchronized (doclock)
            {
              // we are playing some games with the lexer for efficiency.
              // we could just create a new lexer each time here, but instead,
              // we will just reset it so that it thinks it is starting at the
              // beginning of the document but reporting a funny start colorStartPos.
              // Reseting the lexer causes the close() method on the reader
              // to be called but because the close() method has no effect on the
              // DocumentReader, we can do this.
              syntaxLexer.reset(
                documentReader,
                0,
                colorStartPos,
                0);
              // After the lexer has been set up, scroll the reader so that it
              // is in the correct spot as well.
              documentReader.seek(colorStartPos);
              // we will highlight tokens until we reach a good stopping place.
              // the first obvious stopping place is the end of the document.
              // the lexer will return null at the end of the document and wee
              // need to stop there.
              t = getNextToken();
            }
                  SimpleAttributeSet errStyle = getMyStyle(IConstants.IStyleNames.ERROR);
                  ErrorInfo[] errInfoClone = _currentErrorInfos.toArray(new ErrorInfo[0]);
            while (t != null && t.getCharEnd() <= colorStartPos + colorLen + 1)
            {
              // this is the actual command that colors the stuff.
              // Color stuff with the description of the style matched
              // to the hash table that has been set up ahead of time.
              synchronized (doclock)
              {
                if (t.getCharEnd() <= document.getLength())
                {
                  String type = t.getDescription();
                  if (type.equals(IConstants.IStyleNames.IDENTIFIER))
                  {
                    final String data = t.getContents();
                    if (si.isTable(data))
                    {
                      type = IConstants.IStyleNames.TABLE;
                                 if(fireTableOrViewFoundEvent)
                                 {
                         fireTableOrViewFound(t.getContents());
                       }

                                 String upperCaseTableName = data.toUpperCase();
                                 if(false == _knownTables.contains(upperCaseTableName))
                                 {
                                    _knownTables.put(upperCaseTableName, upperCaseTableName);
                                    recolorColumns(upperCaseTableName);
                                 }

                    }
                    else if (si.isColumn(data))
                    {
                      type = IConstants.IStyleNames.COLUMN;
                    }
                    else if (si.isDataType(data))
                    {
                      type = IConstants.IStyleNames.DATA_TYPE;
                    }
                    else if (si.isKeyword(data))
                    {
                      type = IConstants.IStyleNames.RESERVED_WORD;
                    }
                  }

                           int begin = t.getCharBegin();
                           int len = t.getCharEnd() - t.getCharBegin();

                           SimpleAttributeSet myStyle = null;
                           for (int i = 0; i < errInfoClone.length; i++)
                           {
                              if (    isBetween(errInfoClone[i].beginPos, errInfoClone[i].endPos, begin)
                                   && isBetween(errInfoClone[i].beginPos, errInfoClone[i].endPos, (begin + len - 1)) )
                              {
                                 myStyle = errStyle;
                              }
                           }

                           if(null == myStyle)
                           {
                              myStyle = getMyStyle(type);
                           }

                           setCharacterAttributes(
                    begin,
                    len,
                    myStyle, true);
                  // record the colorStartPos of the last bit of text that we colored
                }
              }
              synchronized (doclock)
              {
                t = getNextToken();
              }
            }

          }
          catch (IOException x)
          {
          }
          // since we did something, we should check that there is
          // nothing else to do before going back to sleep.
          tryAgain = true;
        }

            if(_endThread)
            {
               break;
            }

        asleep = true;
        if (!tryAgain)
        {
          try
          {
            sleep(0xffffff);
          }
          catch (InterruptedException x)
          {
          }

        }
            if(_endThread)
            {
               break;
            }

        asleep = false;
      }
    }

      private void adjustIntervalsToAdjustment(RecolorEvent re)
      {
         for (int i = 0; i < _currentLiteralAndCommentIntervals.size(); i++)
         {
            int[] interval = _currentLiteralAndCommentIntervals.elementAt(i);

            if(re.position < interval[0])
            {
               interval[0] += re.adjustment;
            }
            if(re.position < interval[1])
            {
               interval[1] += re.adjustment;
            }
         }
      }

      private void recolorColumns(String tableName)
      {
         String text = getText().toUpperCase();

         ExtendedColumnInfo[] cols = _session.getSchemaInfo().getExtendedColumnInfos(tableName);

         for (int i = 0; i < cols.length; i++)
         {
            String upperCaseColName = cols[i].getColumnName().toUpperCase();

            int fromIndex = 0;
            for (;;)
            {
               fromIndex = text.indexOf(upperCaseColName, fromIndex);

               if(-1 == fromIndex)
               {
                  break;
               }

               color(fromIndex, upperCaseColName.length(), false, null);
               ++fromIndex;


            }
         }
      }

      private void reinitLiteralAndCommentIntervals()
      {
         try
         {
            _currentLiteralAndCommentIntervals.clear();

            Document doc = getDocument();
            int docLen = doc.getLength();

            int[] curInterval = null;
            boolean inMultiLineComment = false;
            boolean inSinglLineComment = false;
            boolean inLiteral = false;
            for(int i=0; i < docLen; ++i)
            {
               if(i < docLen + 1 && "/*".equals(doc.getText(i,2))
                  && false == inMultiLineComment && false == inSinglLineComment && false == inLiteral)
               {
                  curInterval = new int[2];
                  curInterval[0] = i;
                  curInterval[1] = docLen;
                  inMultiLineComment = true;
                  ++i;
                  continue;
               }

               if(i < docLen + 1 && "--".equals(doc.getText(i,2))
                  && false == inMultiLineComment && false == inSinglLineComment && false == inLiteral)
               {
                  curInterval = new int[2];
                  curInterval[0] = i;
                  curInterval[1] = docLen;
                  inSinglLineComment = true;
                  ++i;
                  continue;
               }

               if('\'' == doc.getText(i,1).charAt(0)
                  && false == inMultiLineComment && false == inSinglLineComment && false == inLiteral)
               {
                  curInterval = new int[2];
                  curInterval[0] = i;
                  curInterval[1] = docLen;
                  inLiteral = true;
                  continue;
               }



               if(i < docLen + 1 && "*/".equals(doc.getText(i,2)) && inMultiLineComment)
               {
                  curInterval[1] = i+1;
                  _currentLiteralAndCommentIntervals.add(curInterval);
                  curInterval = null;
                  inMultiLineComment = false;
                  ++i;
               }

               if('\n' == doc.getText(i,1).charAt(0) && inSinglLineComment)
               {
                  curInterval[1] = i;
                  _currentLiteralAndCommentIntervals.add(curInterval);
                  curInterval = null;
                  inSinglLineComment = false;
               }

               if('\'' == doc.getText(i,1).charAt(0) && inLiteral)
               {
                  if( i < docLen + 1 && '\'' == doc.getText(i+1,1).charAt(0))
                  {
                     ++i;
                  }
                  else
                  {
                     curInterval[1] = i;
                     _currentLiteralAndCommentIntervals.add(curInterval);
                     curInterval = null;
                     inLiteral = false;
                  }

               }
            }

            if(null != curInterval)
            {
               _currentLiteralAndCommentIntervals.add(curInterval);
            }
         }
         catch (BadLocationException e)
         {
            throw new RuntimeException(e);
         }
      }

      private int getColorLen(int begin, RecolorEvent re)
      {
         try
         {
            int reBegin = Math.min(re.position, re.position + re.adjustment);
            reBegin = Math.max(0, reBegin);

            int end = begin + (reBegin - begin) + Math.max(0, re.adjustment);

            int docLen = getDocument().getLength();

            if(end > docLen -1)
            {
               return docLen - begin;
            }

            for(;end < docLen -1; ++end)
            {
               if(Character.isWhitespace(getDocument().getText(end,1).charAt(0)))
               {
                  break;
               }
            }

            int[] interval = getInvolvedLiteralOrCommentInterval(end);

            if(null != interval)
            {
               return Math.max(end-begin, interval[1] - begin);
            }
            else
            {
               return end - begin;
            }
         }
         catch (BadLocationException e)
         {
            throw new RuntimeException(e);
         }
      }

      private int getColorStartPos(RecolorEvent re)
      {
         try
         {
            int startPos = Math.min(re.position, re.position + re.adjustment);

            if(0 > startPos)
            {
               return 0;
            }

            for (; startPos > 0; --startPos)
            {
               if(Character.isWhitespace(getDocument().getText(startPos-1,1).charAt(0)))
               {
                  break;
               }
            }

            int[] interval = getInvolvedLiteralOrCommentInterval(startPos);

            if(null != interval)
            {
               return Math.min(startPos, interval[0]);
            }
            else
            {
               return startPos;
            }



         }
         catch (BadLocationException e)
         {
            throw new RuntimeException(e);
         }
      }

      private int[] getInvolvedLiteralOrCommentInterval(int pos)
      {
         for (int i = 0; i < _currentLiteralAndCommentIntervals.size(); i++)
         {
            int[] interval = _currentLiteralAndCommentIntervals.elementAt(i);

            if(interval[0]-1 <= pos && pos <= interval[1]+1)
            {
               // The Interval is involved even if pos lied one point before
               // or after the interval.
               // This way for example we get
               // -- Select ...
               // out of comment coloring when the first "-" is removed.

               return interval;
            }
         }
         return null;
      }


      private boolean isBetween(int beg, int end, int p)
      {
         return beg <= p && p <= end;
      }


      private void setCharacterAttributes(final int offset, final int length, final AttributeSet s, final boolean replace)
      {
         SwingUtilities.invokeLater(new Runnable()
         {
            public void run()
            {
               // Though in API-Doc they say setCharacterAttributes() is thread save we
               // received observed java.lang.Errors  from Swing as well as dead locks.
               // That's why we do changes synchron now.
               document.setCharacterAttributes(offset, length, s, replace);
            }
         });
      }



  }

  public void addSQLTokenListener(SQLTokenListener l)
  {
    _sqlTokenListeners.add(l);
  }

  public void removeSQLTokenListener(SQLTokenListener l)
  {
    _sqlTokenListeners.remove(l);
  }

  private void fireTableOrViewFound(String name)
  {
    Vector<SQLTokenListener> buf;
    synchronized(_sqlTokenListeners)
    {
      buf = new Vector<SQLTokenListener>(_sqlTokenListeners);
    }

    for(int i=0; i < buf.size(); ++i)
    {
      buf.get(i).tableOrViewFound(name);
    }
  }

   public String getToolTipText(MouseEvent event)
   {
      int pos = viewToModel(event.getPoint());

      for (int i = 0; i < _currentErrorInfos.size(); i++)
      {
         ErrorInfo errInfo = _currentErrorInfos.elementAt(i);

         if(errInfo.beginPos-1 <= pos && pos <= errInfo.endPos)
         {
            return errInfo.message;
         }
      }

      return null;
   }


  /**
   * Just like a DefaultStyledDocument but intercepts inserts and
   * removes to color them.
   */
  private class HighLightedDocument extends DefaultStyledDocument
  {
        private static final long serialVersionUID = 1L;

        public HighLightedDocument()
    {
      super();
      putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");
    }

    public void insertString(int offs, String str, AttributeSet a)
      throws BadLocationException
    {
//      synchronized (doclock)
//      {
        super.insertString(offs, str, a);
        color(offs, str.length(), true, str);
        documentReader.update(offs, str.length());
//      }
    }

    public void remove(int offs, int len) throws BadLocationException
    {
//      synchronized (doclock)
//      {
            String change = getText(offs, len);
        super.remove(offs, len);
        color(offs, -len, true, change);
        documentReader.update(offs, -len);
//      }
    }
   }

  class DocumentReader extends Reader
  {

    /**
     * Modifying the document while the reader is working is like
     * pulling the rug out from under the reader. Alerting the
     * reader with this method (in a nice thread safe way, this
     * should not be called at the same time as a read) allows
     * the reader to compensate.
     */
    public void update(int position, int adjustment)
    {
      if (position < this.position)
      {
        if (this.position < position - adjustment)
        {
          this.position = position;
        }
        else
        {
          this.position += adjustment;
        }
      }
    }

    /**
     * Current position in the document. Incremented
     * whenever a character is read.
     */
    private long position = 0;

    /**
     * Saved position used in the mark and reset methods.
     */
    private long mark = -1;

    /**
     * The document that we are working with.
     */
    private AbstractDocument document;

    /**
     * Construct a reader on the given document.
     *
     * @param document the document to be read.
     */
    public DocumentReader(AbstractDocument document)
    {
      this.document = document;
    }

    /**
     * Has no effect. This reader can be used even after
     * it has been closed.
     */
    public void close()
    {
    }

    /**
     * Save a position for reset.
     *
     * @param readAheadLimit ignored.
     */
    public void mark(int readAheadLimit)
    {
      mark = position;
    }

    /**
     * This reader support mark and reset.
     *
     * @return true
     */
    public boolean markSupported()
    {
      return true;
    }

    /**
     * Read a single character.
     *
     * @return the character or -1 if the end of the document has been reached.
     */
    public int read()
    {
      if (position < document.getLength())
      {
        try
        {
          char c = document.getText((int) position, 1).charAt(0);
          position++;
          return c;
        }
        catch (BadLocationException x)
        {
          return -1;
        }
      }
      else
      {
        return -1;
      }
    }

    /**
     * Read and fill the buffer.
     * This method will always fill the buffer unless the end of the document is reached.
     *
     * @param cbuf the buffer to fill.
     * @return the number of characters read or -1 if no more characters are available in the document.
     */
    public int read(char[] cbuf)
    {
      return read(cbuf, 0, cbuf.length);
    }

    /**
     * Read and fill the buffer.
     * This method will always fill the buffer unless the end of the document is reached.
     *
     * @param cbuf the buffer to fill.
     * @param off offset into the buffer to begin the fill.
     * @param len maximum number of characters to put in the buffer.
     * @return the number of characters read or -1 if no more characters are available in the document.
     */
    public int read(char[] cbuf, int off, int len)
    {
      if (position < document.getLength())
      {
        int length = len;
        if (position + length >= document.getLength())
        {
          length = document.getLength() - (int) position;
        }
        if (off + length >= cbuf.length)
        {
          length = cbuf.length - off;
        }
        try
        {
          String s = document.getText((int) position, length);
          position += length;
          for (int i = 0; i < length; i++)
          {
                  // The Ostermiller SQLLexer crashes with an ArrayIndexOutOfBoundsException
                  // if the char is greater then 255. So we prevent the char from being greater.
                  // This is surely not a proper Unicode treatment but it doesn't seem
                  // to do no harm and it keeps the SQLLexer working.
            cbuf[off + i] = (char)((s.charAt(i)) % 256);
          }
          return length;
        }
        catch (BadLocationException x)
        {
          return -1;
        }
      }
      else
      {
        return -1;
      }
    }

    /**
     * @return true
     */
    public boolean ready()
    {
      return true;
    }

    /**
     * Reset this reader to the last mark, or the beginning of the document if a mark has not been set.
     */
    public void reset()
    {
      if (mark == -1)
      {
        position = 0;
      }
      else
      {
        position = mark;
      }
      mark = -1;
    }

    /**
     * Skip characters of input.
     * This method will always skip the maximum number of characters unless
     * the end of the file is reached.
     *
     * @param n number of characters to skip.
     * @return the actual number of characters skipped.
     */
    public long skip(long n)
    {
      if (position + n <= document.getLength())
      {
        position += n;
        return n;
      }
      else
      {
        long oldPos = position;
        position = document.getLength();
        return (document.getLength() - oldPos);
      }
    }

    /**
     * Seek to the given position in the document.
     *
     * @param n the offset to which to seek.
     */
    public void seek(long n)
    {
      if (n <= document.getLength())
      {
        position = n;
      }
      else
      {
        position = document.getLength();
      }
    }
  }

}
TOP

Related Classes of net.sourceforge.squirrel_sql.plugins.syntax.oster.OsterTextControl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.