/* Copyright (c) 2007 Pentaho Corporation. All rights reserved.
* This software was developed by Pentaho Corporation and is provided under the terms
* of the GNU Lesser General Public License, Version 2.1. You may not use
* this file except in compliance with the license. If you need a copy of the license,
* please go to http://www.gnu.org/licenses/lgpl-2.1.txt. The Original Code is Pentaho
* Data Integration. The Initial Developer is Pentaho Corporation.
*
* Software distributed under the GNU Lesser Public License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
* the license for the specific language governing your rights and limitations.*/
/**********************************************************************
** **
** This Script has been modified for higher performance **
** and more functionality in December-2006, **
** by proconis GmbH / Germany **
** **
** http://www.proconis.de **
** info@proconis.de **
** **
**********************************************************************/
package com.akretion.kettle.steps.terminatooor.ui;
import java.io.IOException;
import java.io.StringReader;
import java.util.Hashtable;
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.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
class ScriptValuesHighlight 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;
public ScriptValuesHighlight() {
initializeColors();
scanner = new JavaScanner();
}
public ScriptValuesHighlight(String[] strArrETLFunctions) {
initializeColors();
scanner = new JavaScanner();
scanner.setETLKeywords(strArrETLFunctions);
scanner.initializeETLFunctions();
}
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 = (int[])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[] {
new Color(display, new RGB(0, 0, 0)), // black
new Color(display, new RGB(63, 127, 95)), // red
new Color(display, new RGB(0, 0, 192)), // green
new Color(display, new RGB(127, 0, 85)), // blue
new Color(display, new RGB(255, 102, 0)) // Kettle Functions / Orange
};
tokenColors= new int[MAXIMUM_TOKEN];
tokenColors[WORD]= 0;
tokenColors[WHITE]= 0;
tokenColors[KEY]= 3;
tokenColors[COMMENT]= 1;
tokenColors[STRING]= 2;
tokenColors[OTHER]= 0;
tokenColors[NUMBER]= 0;
tokenColors[FUNCTIONS]= 4;
}
void disposeColors() {
for (int i=0;i<colors.length;i++) {
colors[i].dispose();
}
}
/**
* 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[2], 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) {
// do nothing
} else if ((token == WHITE) && (!styles.isEmpty())) {
int start = scanner.getStartOffset() + event.lineOffset;
lastStyle = (StyleRange)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 = (StyleRange)styles.lastElement();
if (lastStyle.similarTo(style) && (lastStyle.start + lastStyle.length == style.start)) {
lastStyle.length += style.length;
} else {
styles.addElement(style);
}
}
}
}
token= scanner.nextToken();
}
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 StringBuffer fBuffer= new StringBuffer();
protected String fDoc;
protected int fPos;
protected int fEnd;
protected int fStartToken;
protected boolean fEofSeen= false;
private String[] kfKeywords ={
"num2str"
};
private String[] fgKeywords= {
"array",
"break",
"case","catch", "const","continue",
"Date", "default", "delete", "do",
"else","eval","escape",
"false", "finally", "float", "for", "function",
"if","in","instanceof", "isFinite","isNaN",
"new", "Number", "null",
"String", "switch",
"this","then","throw", "to", "true","try","typeof",
"parseInt","parseFloat",
"return",
"unescape",
"var","void",
"with", "while"
};
public JavaScanner() {
initialize();
initializeETLFunctions();
}
/**
* 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 = Integer.valueOf(KEY);
for (int i= 0; i < fgKeywords.length; i++)
fgKeys.put(fgKeywords[i], k);
}
public void setETLKeywords(String[]kfKeywords ){
this.kfKeywords = kfKeywords;
}
void initializeETLFunctions(){
kfKeys = new Hashtable<String, Integer>();
Integer k = Integer.valueOf(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 '\'': // char const
for(;;) {
c= read();
switch (c) {
case '\'':
return STRING;
case EOF:
unread(c);
return STRING;
case '\\':
c= read();
break;
}
}
case '"': // string
for (;;) {
c= read();
switch (c) {
case '"':
return STRING;
case EOF:
unread(c);
return STRING;
case '\\':
c= read();
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= (Integer) fgKeys.get(fBuffer.toString());
if (i != null) return i.intValue();
i= (Integer) 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;
fPos= 0;
fEnd= fDoc.length() -1;
}
protected void unread(int c) {
if (c != EOF)
fPos--;
}
}
}