/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.pentaho.di.ui.trans.steps.tableinput; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Vector; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.LineStyleEvent; import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.pentaho.di.core.database.SqlScriptStatement; import org.pentaho.di.core.util.Utils; import org.pentaho.di.ui.core.gui.GUIResource; public class SQLValuesHighlight implements LineStyleListener { JavaScanner scanner = new JavaScanner(); int[] tokenColors; Color[] colors; Vector<int[]> blockComments = new Vector<int[]>(); public static final int EOF = -1; public static final int EOL = 10; public static final int WORD = 0; public static final int WHITE = 1; public static final int KEY = 2; public static final int COMMENT = 3; // single line comment: // public static final int STRING = 5; public static final int OTHER = 6; public static final int NUMBER = 7; public static final int FUNCTIONS = 8; public static final int MAXIMUM_TOKEN = 9; private List<SqlScriptStatement> scriptStatements; public SQLValuesHighlight() { initializeColors(); scriptStatements = new ArrayList<SqlScriptStatement>(); scanner = new JavaScanner(); } public SQLValuesHighlight( String[] strArrSQLFunctions ) { initializeColors(); scriptStatements = new ArrayList<SqlScriptStatement>(); scanner = new JavaScanner(); scanner.setSQLKeywords( strArrSQLFunctions ); scanner.initializeSQLFunctions(); } Color getColor( int type ) { if ( type < 0 || type >= tokenColors.length ) { return null; } return colors[tokenColors[type]]; } boolean inBlockComment( int start, int end ) { for ( int i = 0; i < blockComments.size(); i++ ) { int[] offsets = blockComments.elementAt( i ); // start of comment in the line if ( ( offsets[0] >= start ) && ( offsets[0] <= end ) ) { return true; } // end of comment in the line if ( ( offsets[1] >= start ) && ( offsets[1] <= end ) ) { return true; } if ( ( offsets[0] <= start ) && ( offsets[1] >= end ) ) { return true; } } return false; } void initializeColors() { // Display display = Display.getDefault(); colors = new Color[] { GUIResource.getInstance().getColor( 0, 0, 0 ), // black GUIResource.getInstance().getColor( 255, 0, 0 ), // red GUIResource.getInstance().getColor( 63, 127, 95 ), // green GUIResource.getInstance().getColor( 0, 0, 255 ), // blue GUIResource.getInstance().getColor( 255, 0, 255 ) // SQL Functions / Rose }; tokenColors = new int[MAXIMUM_TOKEN]; tokenColors[WORD] = 0; tokenColors[WHITE] = 0; tokenColors[KEY] = 3; tokenColors[COMMENT] = 2; tokenColors[STRING] = 1; tokenColors[OTHER] = 0; tokenColors[NUMBER] = 0; tokenColors[FUNCTIONS] = 4; } /** * Event.detail line start offset (input) Event.text line text (input) LineStyleEvent.styles Enumeration of * StyleRanges, need to be in order. (output) LineStyleEvent.background line background color (output) */ public void lineGetStyle( LineStyleEvent event ) { Vector<StyleRange> styles = new Vector<StyleRange>(); int token; StyleRange lastStyle; if ( inBlockComment( event.lineOffset, event.lineOffset + event.lineText.length() ) ) { styles.addElement( new StyleRange( event.lineOffset, event.lineText.length() + 4, colors[1], null ) ); event.styles = new StyleRange[styles.size()]; styles.copyInto( event.styles ); return; } scanner.setRange( event.lineText ); String xs = ( (StyledText) event.widget ).getText(); if ( xs != null ) { parseBlockComments( xs ); } token = scanner.nextToken(); while ( token != EOF ) { if ( token != OTHER ) { if ( ( token == WHITE ) && ( !styles.isEmpty() ) ) { int start = scanner.getStartOffset() + event.lineOffset; lastStyle = styles.lastElement(); if ( lastStyle.fontStyle != SWT.NORMAL ) { if ( lastStyle.start + lastStyle.length == start ) { // have the white space take on the style before it to minimize font style // changes lastStyle.length += scanner.getLength(); } } } else { Color color = getColor( token ); if ( color != colors[0] ) { // hardcoded default foreground color, black StyleRange style = new StyleRange( scanner.getStartOffset() + event.lineOffset, scanner.getLength(), color, null ); // if ( token == KEY ) { // style.fontStyle = SWT.BOLD; // } if ( styles.isEmpty() ) { styles.addElement( style ); } else { lastStyle = styles.lastElement(); if ( lastStyle.similarTo( style ) && ( lastStyle.start + lastStyle.length == style.start ) ) { lastStyle.length += style.length; } else { styles.addElement( style ); } } } } } token = scanner.nextToken(); } // See which backgrounds to color... // if ( scriptStatements != null ) { for ( SqlScriptStatement statement : scriptStatements ) { // Leave non-executed statements alone. // StyleRange styleRange = new StyleRange(); styleRange.start = statement.getFromIndex(); styleRange.length = statement.getToIndex() - statement.getFromIndex(); if ( statement.isComplete() ) { if ( statement.isOk() ) { // GUIResource.getInstance().getColor(63, 127, 95), // green styleRange.background = GUIResource.getInstance().getColor( 244, 238, 224 ); // honey dew } else { styleRange.background = GUIResource.getInstance().getColor( 250, 235, 215 ); // Antique White } } else { styleRange.background = GUIResource.getInstance().getColorWhite(); } styles.add( styleRange ); } } event.styles = new StyleRange[styles.size()]; styles.copyInto( event.styles ); } public void parseBlockComments( String text ) { blockComments = new Vector<int[]>(); StringReader buffer = new StringReader( text ); int ch; boolean blkComment = false; int cnt = 0; int[] offsets = new int[2]; boolean done = false; try { while ( !done ) { switch ( ch = buffer.read() ) { case -1: { if ( blkComment ) { offsets[1] = cnt; blockComments.addElement( offsets ); } done = true; break; } case '/': { ch = buffer.read(); if ( ( ch == '*' ) && ( !blkComment ) ) { offsets = new int[2]; offsets[0] = cnt; blkComment = true; cnt++; } else { cnt++; } cnt++; break; } case '*': { if ( blkComment ) { ch = buffer.read(); cnt++; if ( ch == '/' ) { blkComment = false; offsets[1] = cnt; blockComments.addElement( offsets ); } } cnt++; break; } default: { cnt++; break; } } } } catch ( IOException e ) { // ignore errors } } /** * A simple fuzzy scanner for Java */ public class JavaScanner { protected Map<String, Integer> fgKeys = null; protected Map<?, ?> fgFunctions = null; protected Map<String, Integer> kfKeys = null; protected Map<?, ?> kfFunctions = null; protected StringBuilder fBuffer = new StringBuilder(); protected String fDoc; protected int fPos; protected int fEnd; protected int fStartToken; protected boolean fEofSeen = false; private String[] kfKeywords = { "getdate", "case", "convert", "left", "right", "isnumeric", "isdate", "isnumber", "number", "finally", "cast", "var", "fetch_status", "isnull", "charindex", "difference", "len", "nchar", "quotename", "replicate", "reverse", "str", "stuff", "unicode", "ascii", "char", "to_char", "to_date", "to_number", "nvl", "sysdate", "corr", "count", "grouping", "max", "min", "stdev", "sum", "concat", "length", "locate", "ltrim", "posstr", "repeat", "replace", "rtrim", "soundex", "space", "substr", "substring", "trunc", "nextval", "currval", "getclobval", "char_length", "compare", "patindex", "sortkey", "uscalar", "current_date", "current_time", "current_timestamp", "current_user", "session_user", "system_user", "curdate", "curtime", "database", "now", "sysdate", "today", "user", "version", "coalesce", "nullif", "octet_length", "datalength", "decode", "greatest", "ifnull", "least", "||", "char_length", "character_length", "collate", "concatenate", "like", "lower", "position", "translate", "upper", "char_octet_length", "character_maximum_length", "character_octet_length", "ilike", "initcap", "instr", "lcase", "lpad", "patindex", "rpad", "ucase", "bit_length", "&", "|", "^", "%", "+", "-", "*", "/", "(", ")", "abs", "asin", "atan", "ceiling", "cos", "cot", "exp", "floor", "ln", "log", "log10", "mod", "pi", "power", "rand", "round", "sign", "sin", "sqrt", "tan", "trunc", "extract", "interval", "overlaps", "adddate", "age", "date_add", "dateformat", "date_part", "date_sub", "datediff", "dateadd", "datename", "datepart", "day", "dayname", "dayofmonth", "dayofweek", "dayofyear", "hour", "last_day", "minute", "month", "month_between", "monthname", "next_day", "second", "sub_date", "week", "year", "dbo", "log", "objectproperty" }; private String[] fgKeywords = { "create", "procedure", "as", "set", "nocount", "on", "declare", "varchar", "print", "table", "int", "tintytext", "select", "from", "where", "and", "or", "insert", "into", "cursor", "read_only", "for", "open", "fetch", "next", "end", "deallocate", "table", "drop", "exec", "begin", "close", "update", "delete", "truncate", "inner", "outer", "join", "union", "all", "float", "when", "nolock", "with", "false", "datetime", "dare", "time", "hour", "array", "minute", "second", "millisecond", "view", "function", "catch", "const", "continue", "compute", "browse", "option", "date", "default", "do", "raw", "auto", "explicit", "xmldata", "elements", "binary", "base64", "read", "outfile", "asc", "desc", "else", "eval", "escape", "having", "limit", "offset", "of", "intersect", "except", "using", "variance", "specific", "language", "body", "returns", "specific", "deterministic", "not", "external", "action", "reads", "static", "inherit", "called", "order", "group", "by", "natural", "full", "exists", "between", "some", "any", "unique", "match", "value", "limite", "minus", "references", "grant", "on", "top", "index", "bigint", "text", "char", "use", "move", "exec", "init", "name", "noskip", "skip", "noformat", "format", "stats", "disk", "from", "to", "rownum", "alter", "add", "remove", "move", "alter", "add", "remove", "lineno", "modify", "if", "else", "in", "is", "new", "Number", "null", "string", "switch", "this", "then", "throw", "true", "false", "try", "return", "with", "while", "start", "connect", "optimize", "first", "only", "rows", "sequence", "blob", "clob", "image", "binary", "column", "decimal", "distinct", "primary", "key", "timestamp", "varbinary", "nvarchar", "nchar", "longnvarchar", "nclob", "numeric", "constraint", "dbcc", "backup", "bit", "clustered", "pad_index", "off", "statistics_norecompute", "ignore_dup_key", "allow_row_locks", "allow_page_locks", "textimage_on", "double", "rollback", "tran", "transaction", "commit" }; public JavaScanner() { initialize(); initializeSQLFunctions(); } /** * Returns the ending location of the current token in the document. */ public final int getLength() { return fPos - fStartToken; } /** * Initialize the lookup table. */ void initialize() { fgKeys = new Hashtable<String, Integer>(); Integer k = new Integer( KEY ); for ( int i = 0; i < fgKeywords.length; i++ ) { fgKeys.put( fgKeywords[i], k ); } } public void setSQLKeywords( String[] kfKeywords ) { this.kfKeywords = kfKeywords; } public String[] getSQLKeywords() { return kfKeywords; } public void initializeSQLFunctions() { kfKeys = new Hashtable<String, Integer>(); Integer k = new Integer( FUNCTIONS ); for ( int i = 0; i < kfKeywords.length; i++ ) { kfKeys.put( kfKeywords[i], k ); } } /** * Returns the starting location of the current token in the document. */ public final int getStartOffset() { return fStartToken; } /** * Returns the next lexical token in the document. */ public int nextToken() { int c; fStartToken = fPos; while ( true ) { switch ( c = read() ) { case EOF: return EOF; case '/': // comment c = read(); if ( c == '/' ) { while ( true ) { c = read(); if ( ( c == EOF ) || ( c == EOL ) ) { unread( c ); return COMMENT; } } } else { unread( c ); } return OTHER; case '-': // comment c = read(); if ( c == '-' ) { while ( true ) { c = read(); if ( ( c == EOF ) || ( c == EOL ) ) { unread( c ); return COMMENT; } } } else { unread( c ); } return OTHER; case '\'': // char const for ( ;; ) { c = read(); switch ( c ) { case '\'': return STRING; case EOF: unread( c ); return STRING; case '\\': c = read(); break; default: break; } } case '"': // string for ( ;; ) { c = read(); switch ( c ) { case '"': return STRING; case EOF: unread( c ); return STRING; case '\\': c = read(); break; default: break; } } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': do { c = read(); } while ( Character.isDigit( (char) c ) ); unread( c ); return NUMBER; default: if ( Character.isWhitespace( (char) c ) ) { do { c = read(); } while ( Character.isWhitespace( (char) c ) ); unread( c ); return WHITE; } if ( Character.isJavaIdentifierStart( (char) c ) ) { fBuffer.setLength( 0 ); do { fBuffer.append( (char) c ); c = read(); } while ( Character.isJavaIdentifierPart( (char) c ) ); unread( c ); Integer i = fgKeys.get( fBuffer.toString() ); if ( i != null ) { return i.intValue(); } i = kfKeys.get( fBuffer.toString() ); if ( i != null ) { return i.intValue(); } return WORD; } return OTHER; } } } /** * Returns next character. */ protected int read() { if ( fPos <= fEnd ) { return fDoc.charAt( fPos++ ); } return EOF; } public void setRange( String text ) { fDoc = text.toLowerCase(); fPos = 0; fEnd = fDoc.length() - 1; } protected void unread( int c ) { if ( c != EOF ) { fPos--; } } } /** * @return the scriptStatements */ public List<SqlScriptStatement> getScriptStatements() { return scriptStatements; } /** * @param scriptStatements * the scriptStatements to set */ public void setScriptStatements( List<SqlScriptStatement> scriptStatements ) { this.scriptStatements = scriptStatements; } public void addKeyWords( String[] reservedWords ) { if ( Utils.isEmpty( reservedWords ) ) { return; } // List<String> keywords = new ArrayList<String>(Arrays.asList(scanner.getSQLKeywords())); // keywords.addAll(Arrays.asList(reservedWords)); scanner.setSQLKeywords( reservedWords ); scanner.initializeSQLFunctions(); } }