All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrasstools.spatialite.SqlDocument Maven / Gradle / Ivy

package org.jgrasstools.spatialite;

import java.awt.Color;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Scanner;

import javax.swing.event.DocumentEvent;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import org.jgrasstools.gears.utils.StringUtilities;

public class SqlDocument 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 keywordsMap;
    private HashSet splKeywordsMap;
    private SimpleAttributeSet splKeyword;

    public SqlDocument() {

        doc = this;
        rootElement = doc.getDefaultRootElement();
        putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");

        normal = new SimpleAttributeSet();
        StyleConstants.setForeground(normal, Color.black);

        comment = new SimpleAttributeSet();
        Color green = new Color(0, 140, 0);
        StyleConstants.setForeground(comment, green);
        StyleConstants.setItalic(comment, true);

        StyleConstants.setForeground(comment, green);
        StyleConstants.setItalic(comment, true);
        keyword = new SimpleAttributeSet();
        Color blue = new Color(0, 0, 140);
        StyleConstants.setForeground(keyword, blue);
        StyleConstants.setBold(keyword, true);

        StyleConstants.setForeground(comment, green);
        StyleConstants.setItalic(comment, true);
        splKeyword = new SimpleAttributeSet();
        StyleConstants.setForeground(splKeyword, new Color(0, 128, 0));
        StyleConstants.setBold(splKeyword, true);

        quote = new SimpleAttributeSet();
        Color red = new Color(255, 54, 246);
        StyleConstants.setForeground(quote, red);
        StyleConstants.setBold(quote, true);

        InputStream keywordsStream = getClass().getResourceAsStream("/jgt_sql_keywords.txt");
        Scanner keywordsScanner = StringUtilities.streamToString(keywordsStream, "\n");
        keywordsMap = new HashSet<>();
        while( keywordsScanner.hasNext() ) {
            String keyword = keywordsScanner.next();
            keywordsMap.add(keyword.toUpperCase().trim());
        }

        keywordsStream = getClass().getResourceAsStream("/spl_sql_keywords.txt");
        keywordsScanner = StringUtilities.streamToString(keywordsStream, "\n");
        splKeywordsMap = new HashSet<>();
        while( keywordsScanner.hasNext() ) {
            String keyword = keywordsScanner.next();
            splKeywordsMap.add(keyword.toUpperCase().trim());
        }
    }

    /*
     * 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
     */
    private 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);
        }
    }

    /*
     * Parse the line to get the quotes and highlight it
     */
    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);
        if (isSpatialiteKeyword(token))
            doc.setCharacterAttributes(startOffset, endOfToken - startOffset, splKeyword, false);
        return endOfToken + 1;
    }

    /*
     * This updates the colored text and prepares for undo event
     */
    protected void fireInsertUpdate( DocumentEvent evt ) {

        super.fireInsertUpdate(evt);

        try {
            processChangedLines(evt.getOffset(), evt.getLength());
        } catch (BadLocationException ex) {
            System.out.println("" + ex);
        }
    }

    /*
     * This updates the colored text and does the undo operation
     */
    protected void fireRemoveUpdate( DocumentEvent evt ) {

        super.fireRemoveUpdate(evt);

        try {
            processChangedLines(evt.getOffset(), evt.getLength());
        } catch (BadLocationException ex) {
            System.out.println("" + ex);
        }
    }

    /*
     * 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 keywordsMap.contains(token.toUpperCase());
    }

    /*
     * Override for other languages
     */
    protected boolean isSpatialiteKeyword( String token ) {
        return splKeywordsMap.contains(token.toUpperCase());
    }

    /*
     * 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;
    }

    /*
     * Overide bracket matching for other languages
     */
    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() + whiteSpace.toString() + "\n" + whiteSpace.toString() + "}";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy