org.geotoolkit.cql.JCQLTextPane.java Source code

Java tutorial

Introduction

Here is the source code for org.geotoolkit.cql.JCQLTextPane.java

Source

/*
 *    Geotoolkit - An Open Source Java GIS Toolkit
 *    http://www.geotoolkit.org
 *
 *    (C) 2012-2014, Geomatys
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 */
package org.geotoolkit.cql;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.border.EmptyBorder;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.Expression;

/**
 * Swing CQL text pane. highlights syntax.
 * 
 * @author Johann Sorel (Geomatys)
 */
public class JCQLTextPane extends JPanel implements KeyListener {

    private final JTextPane guiText = new JTextPane();
    private final JLabel guiError = new JLabel();

    final Style styleDefault;
    final Style styleComment;
    final Style styleFunction;
    final Style styleLiteral;
    final Style styleParenthese;
    final Style styleOperator;
    final Style styleBinary;
    final Style stylePropertyName;
    final Style styleError;

    public JCQLTextPane() {
        super(new BorderLayout(0, 0));

        guiText.setBackground(Color.WHITE);

        final JScrollPane scroll = new JScrollPane(guiText);
        scroll.setBorder(new EmptyBorder(0, 0, 0, 0));
        guiText.setBorder(new EmptyBorder(0, 0, 0, 0));
        scroll.getInsets().set(0, 0, 0, 0);
        scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0));
        scroll.getViewport().getInsets().set(0, 0, 0, 0);

        add(BorderLayout.CENTER, scroll);
        add(BorderLayout.SOUTH, guiError);
        guiText.addKeyListener(this);

        styleDefault = guiText.addStyle("default", null);
        StyleConstants.setForeground(styleDefault, Color.BLACK);

        styleComment = guiText.addStyle("comment", null);
        StyleConstants.setForeground(styleComment, Color.GRAY);

        styleLiteral = guiText.addStyle("literal", null);
        StyleConstants.setForeground(styleLiteral, new Color(0, 150, 0));

        styleFunction = guiText.addStyle("function", null);
        StyleConstants.setForeground(styleFunction, Color.MAGENTA);

        styleParenthese = guiText.addStyle("parenthese", null);
        StyleConstants.setForeground(styleParenthese, new Color(0, 0, 0));

        styleOperator = guiText.addStyle("operator", null);
        StyleConstants.setForeground(styleOperator, Color.BLACK);
        StyleConstants.setBold(styleOperator, true);

        styleBinary = guiText.addStyle("binary", null);
        StyleConstants.setForeground(styleBinary, Color.BLACK);
        StyleConstants.setBold(styleBinary, true);

        stylePropertyName = guiText.addStyle("property", null);
        StyleConstants.setForeground(stylePropertyName, Color.BLUE);
        StyleConstants.setBold(stylePropertyName, true);

        styleError = guiText.addStyle("error", null);
        StyleConstants.setForeground(styleError, Color.RED);
        StyleConstants.setBold(styleError, true);

    }

    public void setText(String cql) {
        guiText.setText(cql);
        updateHightLight();
    }

    /**
     * Insert text at current caret position
     * @param text 
     */
    public void insertText(String text) {
        final int position = guiText.getCaretPosition();
        final String cql = guiText.getText();
        final StringBuilder sb = new StringBuilder();
        sb.append(cql.substring(0, position));
        sb.append(text);
        sb.append(cql.substring(position));

        guiText.setText(sb.toString());
        guiText.setCaretPosition(position + text.length());
        updateHightLight();
    }

    public void addText(String text) {
        guiText.setText(guiText.getText() + text);
        updateHightLight();
    }

    public String getText() {
        return guiText.getText();
    }

    public void setFilter(Filter filter) {
        setText(CQL.write(filter));
    }

    public void setExpression(Expression exp) {
        setText(CQL.write(exp));
    }

    public Filter getFilter() throws CQLException {
        return CQL.parseFilter(guiText.getText());
    }

    public Expression getExpression() throws CQLException {
        return CQL.parseExpression(guiText.getText());
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
        updateHightLight();
    }

    private void updateHightLight() {
        final StyledDocument doc = (StyledDocument) guiText.getDocument();
        final String txt = guiText.getText();

        final ParseTree tree = CQL.compile(txt);
        doc.setCharacterAttributes(0, txt.length(), styleError, true);
        syntaxHighLight(tree, doc, new AtomicInteger());
        firePropertyChange("content", null, txt);
    }

    private void syntaxHighLight(ParseTree tree, StyledDocument doc, AtomicInteger position) {

        if (tree instanceof ParserRuleContext) {
            final ParserRuleContext prc = (ParserRuleContext) tree;
            if (prc.exception != null) {
                //error nodes
                final Token tokenStart = prc.getStart();
                Token tokenEnd = prc.getStop();
                if (tokenEnd == null)
                    tokenEnd = tokenStart;
                final int offset = tokenStart.getStartIndex();
                final int length = tokenEnd.getStopIndex() - tokenStart.getStartIndex() + 1;
                doc.setCharacterAttributes(offset, length, styleError, true);
                return;
            }

            //special case for functions
            if (prc instanceof CQLParser.ExpressionTermContext) {
                final CQLParser.ExpressionTermContext ctx = (CQLParser.ExpressionTermContext) prc;
                if (ctx.NAME() != null && ctx.LPAREN() != null) {
                    final int nbChild = tree.getChildCount();
                    for (int i = 0; i < nbChild; i++) {
                        final ParseTree pt = tree.getChild(i);
                        if (pt instanceof TerminalNode
                                && ((TerminalNode) pt).getSymbol().getType() == CQLLexer.NAME) {
                            final TerminalNode tn = (TerminalNode) pt;
                            // if index<0 = missing token
                            final Token token = tn.getSymbol();
                            final int offset = token.getStartIndex();
                            final int length = token.getStopIndex() - token.getStartIndex() + 1;
                            position.addAndGet(length);
                            doc.setCharacterAttributes(offset, length, styleFunction, true);
                        } else {
                            syntaxHighLight(pt, doc, position);
                        }
                    }
                    return;
                }
            }

        }

        if (tree instanceof TerminalNode) {
            final TerminalNode tn = (TerminalNode) tree;
            // if index<0 = missing token
            final Token token = tn.getSymbol();
            final int offset = token.getStartIndex();
            final int length = token.getStopIndex() - token.getStartIndex() + 1;
            position.addAndGet(length);

            switch (token.getType()) {

            case CQLLexer.COMMA:
            case CQLLexer.UNARY:
            case CQLLexer.MULT:
                doc.setCharacterAttributes(offset, length, styleDefault, true);
                break;

            // EXpressions -------------------------------------------------
            case CQLLexer.TEXT:
            case CQLLexer.INT:
            case CQLLexer.FLOAT:
            case CQLLexer.DATE:
            case CQLLexer.DURATION_P:
            case CQLLexer.DURATION_T:
            case CQLLexer.POINT:
            case CQLLexer.LINESTRING:
            case CQLLexer.POLYGON:
            case CQLLexer.MPOINT:
            case CQLLexer.MLINESTRING:
            case CQLLexer.MPOLYGON:
                doc.setCharacterAttributes(offset, length, styleLiteral, true);
                break;
            case CQLLexer.PROPERTY_NAME:
                doc.setCharacterAttributes(offset, length, stylePropertyName, true);
                break;
            case CQLLexer.NAME:
                if (tree.getChildCount() == 0) {
                    //property name
                    doc.setCharacterAttributes(offset, length, stylePropertyName, true);
                } else {
                    //function name
                    doc.setCharacterAttributes(offset, length, styleFunction, true);
                }
                break;
            case CQLLexer.RPAREN:
            case CQLLexer.LPAREN:
                doc.setCharacterAttributes(offset, length, styleParenthese, true);
                break;

            case CQLLexer.COMPARE:
            case CQLLexer.LIKE:
            case CQLLexer.IS:
            case CQLLexer.BETWEEN:
            case CQLLexer.IN:
                doc.setCharacterAttributes(offset, length, styleOperator, true);
                break;
            case CQLLexer.AND:
            case CQLLexer.OR:
            case CQLLexer.NOT:
                doc.setCharacterAttributes(offset, length, styleBinary, true);
                break;
            case CQLLexer.BBOX:
            case CQLLexer.BEYOND:
            case CQLLexer.CONTAINS:
            case CQLLexer.CROSSES:
            case CQLLexer.DISJOINT:
            case CQLLexer.DWITHIN:
            case CQLLexer.EQUALS:
            case CQLLexer.INTERSECTS:
            case CQLLexer.OVERLAPS:
            case CQLLexer.TOUCHES:
            case CQLLexer.WITHIN:
                doc.setCharacterAttributes(offset, length, styleBinary, true);
                break;
            default:
                doc.setCharacterAttributes(offset, length, styleError, true);
                break;
            }
        }

        final int nbChild = tree.getChildCount();
        for (int i = 0; i < nbChild; i++) {
            syntaxHighLight(tree.getChild(i), doc, position);
        }
    }

}