blob: 94f519cc3826ba72a8991c8f0fca698000006c11 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 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.eclipse.fx.xtext.sample.editor.java.text;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.text.rules.CombinedWordRule;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.IWhitespaceDetector;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WhitespaceRule;
public class JavaCodeScanner extends AbstractJavaScanner {
private static final String INTERFACE= "interface"; //$NON-NLS-1$
private static final String RETURN= "return"; //$NON-NLS-1$
private static String[] fgJava14Keywords= { "assert" }; //$NON-NLS-1$
private static String[] fgJava15Keywords= { "enum" }; //$NON-NLS-1$
static String[] fgKeywords= {
"abstract", //$NON-NLS-1$
"break", //$NON-NLS-1$
"case", "catch", "class", "const", "continue", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
"default", "do", //$NON-NLS-2$ //$NON-NLS-1$
"else", "extends", //$NON-NLS-2$ //$NON-NLS-1$
"final", "finally", "for", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
"goto", //$NON-NLS-1$
"if", "implements", "import", "instanceof", "interface", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
"native", "new", //$NON-NLS-2$ //$NON-NLS-1$
"package", "private", "protected", "public", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
"static", "super", "switch", "synchronized", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
"this", "throw", "throws", "transient", "try", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
"volatile", //$NON-NLS-1$
"while" //$NON-NLS-1$
};
private static String[] fgTypes= { "void", "boolean", "char", "byte", "short", "strictfp", "int", "long", "float", "double" }; //$NON-NLS-1$ //$NON-NLS-5$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-2$
private static String[] fgConstants= { "false", "null", "true" }; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
public JavaCodeScanner() {
initialize();
}
// @Override
// public IToken nextToken() {
// IToken nextToken = super.nextToken();
// System.err.println("FIND TOKEN: " + nextToken.getData());
// return nextToken;
// }
@Override
protected List<IRule> createRules() {
List<IRule> rules= new ArrayList<IRule>();
Token token= getToken(IJavaColorConstants.JAVA_STRING);
rules.add(new SingleLineRule("'", "'", token, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
Token defaultToken= getToken(IJavaColorConstants.JAVA_DEFAULT);
// Add generic whitespace rule.
rules.add(new WhitespaceRule(new JavaWhitespaceDetector(), defaultToken));
token= getToken(IJavaColorConstants.ANNOTATION);
AnnotationRule atInterfaceRule= new AnnotationRule(getToken(IJavaColorConstants.JAVA_KEYWORD), token);
rules.add(atInterfaceRule);
// Add word rule for new keywords, see bug 4077
JavaWordDetector wordDetector= new JavaWordDetector();
CombinedWordRule combinedWordRule= new CombinedWordRule(wordDetector, defaultToken);
// Add rule for operators
token= getToken(IJavaColorConstants.JAVA_OPERATOR);
rules.add(new OperatorRule(token));
// Add rule for brackets
token= getToken(IJavaColorConstants.JAVA_BRACKET);
rules.add(new BracketRule(token));
// Add word rule for keyword 'return'.
CombinedWordRule.WordMatcher returnWordRule= new CombinedWordRule.WordMatcher();
token= getToken(IJavaColorConstants.JAVA_KEYWORD_RETURN);
returnWordRule.addWord(RETURN, token);
combinedWordRule.addWordMatcher(returnWordRule);
// Add word rule for keywords, types, and constants.
CombinedWordRule.WordMatcher wordRule= new CombinedWordRule.WordMatcher();
token= getToken(IJavaColorConstants.JAVA_KEYWORD);
for (int i=0; i<fgJava14Keywords.length; i++)
wordRule.addWord(fgJava14Keywords[i], token);
for (int i=0; i<fgJava15Keywords.length; i++)
wordRule.addWord(fgJava15Keywords[i], token);
for (int i=0; i<fgKeywords.length; i++)
wordRule.addWord(fgKeywords[i], token);
for (int i=0; i<fgTypes.length; i++)
wordRule.addWord(fgTypes[i], token);
for (int i=0; i<fgConstants.length; i++)
wordRule.addWord(fgConstants[i], token);
combinedWordRule.addWordMatcher(wordRule);
rules.add(combinedWordRule);
token = getToken(IJavaColorConstants.JAVA_NUMBER);
rules.add(new NumberRule(token));
setDefaultReturnToken(defaultToken);
return rules;
}
/**
* An annotation rule matches the '@' symbol, any following whitespace and
* optionally a following <code>interface</code> keyword.
*
* It does not match if there is a comment between the '@' symbol and
* the identifier. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=82452
*
* @since 3.1
*/
private static class AnnotationRule implements IRule {
/**
* A resettable scanner supports marking a position in a scanner and
* unreading back to the marked position.
*/
private static final class ResettableScanner implements ICharacterScanner {
private final ICharacterScanner fDelegate;
private int fReadCount;
/**
* Creates a new resettable scanner that will forward calls
* to <code>scanner</code>, but store a marked position.
*
* @param scanner the delegate scanner
*/
public ResettableScanner(final ICharacterScanner scanner) {
fDelegate= scanner;
mark();
}
/*
* @see org.eclipse.jface.text.rules.ICharacterScanner#getColumn()
*/
public int getColumn() {
return fDelegate.getColumn();
}
/*
* @see org.eclipse.jface.text.rules.ICharacterScanner#getLegalLineDelimiters()
*/
public char[][] getLegalLineDelimiters() {
return fDelegate.getLegalLineDelimiters();
}
/*
* @see org.eclipse.jface.text.rules.ICharacterScanner#read()
*/
public int read() {
int ch= fDelegate.read();
if (ch != ICharacterScanner.EOF)
fReadCount++;
return ch;
}
/*
* @see org.eclipse.jface.text.rules.ICharacterScanner#unread()
*/
public void unread() {
if (fReadCount > 0)
fReadCount--;
fDelegate.unread();
}
/**
* Marks an offset in the scanned content.
*/
public void mark() {
fReadCount= 0;
}
/**
* Resets the scanner to the marked position.
*/
public void reset() {
while (fReadCount > 0)
unread();
while (fReadCount < 0)
read();
}
}
private final IWhitespaceDetector fWhitespaceDetector= new JavaWhitespaceDetector();
private final IWordDetector fWordDetector= new JavaWordDetector();
private final IToken fInterfaceToken;
private final IToken fAtToken;
/**
* Creates a new rule.
*
* @param interfaceToken the token to return if
* <code>'@\s*interface'</code> is matched
* @param atToken the token to return if <code>'@'</code>
* is matched, but not <code>'@\s*interface'</code>
* @param version the lowest <code>JavaCore.COMPILER_SOURCE</code>
* version that this rule is enabled
* @param currentVersion the current
* <code>JavaCore.COMPILER_SOURCE</code> version
*/
public AnnotationRule(IToken interfaceToken, Token atToken) {
fInterfaceToken= interfaceToken;
fAtToken= atToken;
}
/*
* @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
*/
public IToken evaluate(ICharacterScanner scanner) {
ResettableScanner resettable= new ResettableScanner(scanner);
if (resettable.read() == '@')
return readAnnotation(resettable);
resettable.reset();
return Token.UNDEFINED;
}
private IToken readAnnotation(ResettableScanner scanner) {
scanner.mark();
skipWhitespace(scanner);
if (readInterface(scanner)) {
return fInterfaceToken;
} else {
while( Character.isJavaIdentifierPart(scanner.read()) ) {
}
scanner.unread();
// scanner.reset();
return fAtToken;
}
}
private boolean readInterface(ICharacterScanner scanner) {
int ch= scanner.read();
int i= 0;
while (i < INTERFACE.length() && INTERFACE.charAt(i) == ch) {
i++;
ch= scanner.read();
}
if (i < INTERFACE.length())
return false;
if (fWordDetector.isWordPart((char) ch))
return false;
if (ch != ICharacterScanner.EOF)
scanner.unread();
return true;
}
private boolean skipWhitespace(ICharacterScanner scanner) {
while (fWhitespaceDetector.isWhitespace((char) scanner.read())) {
// do nothing
}
scanner.unread();
return true;
}
}
private static final class NumberRule implements IRule {
private final IToken fToken;
public NumberRule(IToken fToken) {
this.fToken = fToken;
}
@Override
public IToken evaluate(ICharacterScanner scanner) {
int v = scanner.read();
if( Character.isDigit(v) ) {
while( (v = scanner.read()) != ICharacterScanner.EOF ) {
if( ! Character.isDigit(v) & v != '.' && v != '_' ) {
scanner.unread();
break;
}
}
return fToken;
} else {
return Token.UNDEFINED;
}
}
}
private static final class OperatorRule implements IRule {
/** Java operators */
private final char[] JAVA_OPERATORS= { ';', '.', '=', '/', '\\', '+', '-', '*', '<', '>', ':', '?', '!', ',', '|', '&', '^', '%', '~'};
/** Token to return for this rule */
private final IToken fToken;
/**
* Creates a new operator rule.
*
* @param token Token to use for this rule
*/
public OperatorRule(IToken token) {
fToken= token;
}
/**
* Is this character an operator character?
*
* @param character Character to determine whether it is an operator character
* @return <code>true</code> iff the character is an operator, <code>false</code> otherwise.
*/
public boolean isOperator(char character) {
for (int index= 0; index < JAVA_OPERATORS.length; index++) {
if (JAVA_OPERATORS[index] == character)
return true;
}
return false;
}
/*
* @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
*/
public IToken evaluate(ICharacterScanner scanner) {
int character= scanner.read();
if (isOperator((char) character)) {
do {
character= scanner.read();
} while (isOperator((char) character));
scanner.unread();
return fToken;
} else {
scanner.unread();
return Token.UNDEFINED;
}
}
}
private static final class BracketRule implements IRule {
/** Java brackets */
private final char[] JAVA_BRACKETS= { '(', ')', '{', '}', '[', ']' };
/** Token to return for this rule */
private final IToken fToken;
/**
* Creates a new bracket rule.
*
* @param token Token to use for this rule
*/
public BracketRule(IToken token) {
fToken= token;
}
/**
* Is this character a bracket character?
*
* @param character Character to determine whether it is a bracket character
* @return <code>true</code> iff the character is a bracket, <code>false</code> otherwise.
*/
public boolean isBracket(char character) {
for (int index= 0; index < JAVA_BRACKETS.length; index++) {
if (JAVA_BRACKETS[index] == character)
return true;
}
return false;
}
/*
* @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
*/
public IToken evaluate(ICharacterScanner scanner) {
int character= scanner.read();
if (isBracket((char) character)) {
do {
character= scanner.read();
} while (isBracket((char) character));
scanner.unread();
return fToken;
} else {
scanner.unread();
return Token.UNDEFINED;
}
}
}
}