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