blob: cda33b82e2510848866efd5764185ab89ed156f6 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.text.core.rules;
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.Token;
/**
*
*/
public class OperatorRule implements IRule {
// Internal Classes for search-tree ---------------------------------------
private class CharLevel {
private CharLeaf[] list= new CharLeaf[0];
private CharLeaf getChild(final char c) {
for (int i= 0; i < this.list.length; i++) {
if (this.list[i].leafChar == c) {
return this.list[i];
}
}
return null;
}
private void add(final String id, final char[] carray, final IToken token) {
final CharLeaf leaf= getChild(carray[0]);
if (leaf == null) {
final CharLeaf[] extList= new CharLeaf[this.list.length+1];
System.arraycopy(this.list, 0, extList, 0, this.list.length);
extList[this.list.length]= new CharLeaf(id, carray, token);
this.list= extList;
} else {
leaf.add(id, carray, token);
}
}
}
private class CharLeaf {
private final char leafChar;
private IToken leafToken;
private CharLevel nextLevel;
private String leafId;
private CharLeaf(final String id, final char[] chars, final IToken token) {
this.leafChar= chars[0];
add(id, chars, token);
}
private void add(final String id, final char[] chars, final IToken token) {
// leafChar == chars[0];
if (chars.length == 1) {
this.leafId= id;
this.leafToken= token;
} else {
final char[] nextChars= new char[chars.length-1];
System.arraycopy(chars, 1, nextChars, 0, nextChars.length);
if (this.nextLevel == null) {
this.nextLevel= new CharLevel();
}
this.nextLevel.add(id, nextChars, token);
}
}
}
// ------------------------------------------------------------------------
private final CharLevel firstLevel;
/**
* Creates new ROpRule.
*/
public OperatorRule(final char[] init) {
this.firstLevel= new CharLevel();
this.firstLevel.list= new CharLeaf[init.length];
for (int i= 0; i < init.length; i++) {
this.firstLevel.list[i]= new CharLeaf(null, new char[] { init[i] }, null);
}
}
/**
* Adds an operator, linked with given token.
*
* @param op the operator as <code>String</code> to detect.
* @param token the token, which should be returned, if operator is detected.
*/
public void addOp(final String op, final IToken token) {
final char[] cOp= op.toCharArray();
this.firstLevel.add(op, cOp, token);
}
public void addOps(final String[] ops, final IToken token) {
for (int i= 0; i < ops.length; i++) {
addOp(ops[i], token);
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
*/
@Override
public IToken evaluate(final ICharacterScanner scanner) {
final CharLeaf matchLeaf= searchLeaf(scanner);
if (matchLeaf != null) {
return matchLeaf.leafToken;
}
return Token.UNDEFINED;
}
public String searchString(final ICharacterScanner scanner) {
final CharLeaf matchLeaf= searchLeaf(scanner);
if (matchLeaf != null) {
return matchLeaf.leafId;
}
return null;
}
private CharLeaf searchLeaf(final ICharacterScanner scanner) {
CharLevel searchLevel= this.firstLevel;
int level= 1;
CharLeaf matchLeaf= null;
int matchLevel= 0;
for(;; level++) {
final int c= scanner.read();
CharLeaf leaf= null;
if (c < 0) {
level--;
break;
}
if ((searchLevel == null) || (leaf= searchLevel.getChild((char)c)) == null) {
break;
}
if (leaf.leafId != null) {
matchLeaf= leaf;
matchLevel= level;
}
searchLevel= leaf.nextLevel;
}
for (; level > matchLevel; level--) {
scanner.unread();
}
return matchLeaf;
}
}