package com.hubspot.jinjava.el.ext;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.google.common.base.Throwables;
import de.odysseus.el.tree.impl.Scanner;
public class ExtendedScanner extends Scanner {
protected ExtendedScanner(String input) {
super(input);
}
@Override
public Token next() throws ScanException {
if (getToken() != null) {
incrPosition(getToken().getSize());
}
int length = getInput().length();
if (isEval()) {
while (getPosition() < length && isWhitespace(getInput().charAt(getPosition()))) {
incrPosition(1);
}
}
Token token = null;
if (getPosition() == length) {
token = fixed(Symbol.EOF);
} else {
token = nextToken();
}
setToken(token);
return token;
}
protected boolean isWhitespace(char c) {
return Character.isWhitespace(c) || Character.isSpaceChar(c);
}
private static final Method ADD_KEY_TOKEN_METHOD;
private static final Field TOKEN_FIELD;
private static final Field POSITION_FIELD;
static {
try {
ADD_KEY_TOKEN_METHOD = Scanner.class.getDeclaredMethod("addKeyToken", Token.class);
ADD_KEY_TOKEN_METHOD.setAccessible(true);
TOKEN_FIELD = Scanner.class.getDeclaredField("token");
TOKEN_FIELD.setAccessible(true);
POSITION_FIELD = Scanner.class.getDeclaredField("position");
POSITION_FIELD.setAccessible(true);
} catch (NoSuchFieldException | SecurityException | NoSuchMethodException e) {
throw Throwables.propagate(e);
}
}
protected static void addKeyToken(Token token) {
try {
ADD_KEY_TOKEN_METHOD.invoke(null, token);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw Throwables.propagate(e);
}
}
protected void setToken(Token token) {
try {
TOKEN_FIELD.set(this, token);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw Throwables.propagate(e);
}
}
@SuppressWarnings("boxing")
protected void incrPosition(int n) {
try {
POSITION_FIELD.set(this, getPosition() + n);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw Throwables.propagate(e);
}
}
@Override
protected Token nextToken() throws ScanException {
if (isEval()) {
if (getInput().charAt(getPosition()) == '}') {
if (getPosition() < getInput().length() - 1) {
return ExtendedParser.LITERAL_DICT_END;
} else {
return fixed(Symbol.END_EVAL);
}
}
return nextEval();
} else {
if (getPosition() + 1 < getInput().length() && getInput().charAt(getPosition() + 1) == '{') {
switch (getInput().charAt(getPosition())) {
case '#':
return fixed(Symbol.START_EVAL_DEFERRED);
case '$':
return fixed(Symbol.START_EVAL_DYNAMIC);
}
}
return nextText();
}
}
@Override
protected Token nextEval() throws ScanException {
char c1 = getInput().charAt(getPosition());
char c2 = getPosition() < getInput().length() - 1 ? getInput().charAt(getPosition() + 1) : (char) 0;
if (c1 == '/' && c2 == '/') {
return ExtendedParser.TRUNC_DIV;
}
if (c1 == '*' && c2 == '*') {
return ExtendedParser.POWER_OF;
}
if (c1 == '|' && c2 != '|') {
return ExtendedParser.PIPE;
}
if (c1 == '+' && Character.isDigit(c2)) {
return AbsOperator.TOKEN;
}
if (c1 == '=' && c2 != '=') {
return NamedParameterOperator.TOKEN;
}
if (c1 == '{') {
return ExtendedParser.LITERAL_DICT_START;
}
if (c1 == '}' && c2 != 0) {
return ExtendedParser.LITERAL_DICT_END;
}
if (c1 == '~') {
return StringConcatOperator.TOKEN;
}
return super.nextEval();
}
@Override
protected Token nextString() throws ScanException {
builder.setLength(0);
char quote = getInput().charAt(getPosition());
int i = getPosition() + 1;
int l = getInput().length();
while (i < l) {
char c = getInput().charAt(i++);
if (c == '\\') {
if (i == l) {
throw new ScanException(getPosition(), "unterminated string", quote + " or \\");
} else {
c = getInput().charAt(i++);
switch (c) {
case '\\':
case '\'':
case '"':
builder.append(c);
break;
case 'n':
builder.append('\n');
break;
case 't':
builder.append('\t');
break;
case 'b':
builder.append('\b');
break;
case 'f':
builder.append('\f');
break;
case 'r':
builder.append('\r');
break;
default:
throw new ScanException(getPosition(), "invalid escape sequence \\" + c, "\\" + quote + " or \\\\");
}
}
} else if (c == quote) {
return token(Symbol.STRING, builder.toString(), i - getPosition());
} else {
builder.append(c);
}
}
throw new ScanException(getPosition(), "unterminated string", String.valueOf(quote));
}
}