package com.breeze.query;
import java.util.ArrayList;
import java.util.List;
import com.breeze.metadata.DataType;
import com.breeze.metadata.IEntityType;
import com.breeze.metadata.IProperty;
import com.breeze.metadata.MetadataHelper;
// local to this package
class FnExpressionToken {
private StringBuilder _sb;
private int _nextIx;
private boolean _isQuoted;
private List<FnExpressionToken> _fnArgs;
private FnExpressionToken() {
_sb = new StringBuilder();
}
public static FnExpression toExpression(String source, IEntityType entityType) {
FnExpressionToken token = parseToken(source, 0);
return (FnExpression) token.toExpression(entityType, null);
}
private Expression toExpression(IEntityType entityType, DataType returnDataType) {
String text = _sb.toString();
if (this._fnArgs == null) {
if (_isQuoted) {
// TODO: we could check that the returnDataType is a String
return new LitExpression(entityType, DataType.String);
} else {
IProperty prop = MetadataHelper.getPropertyFromPath(text,
entityType);
if (prop == null) {
return new LitExpression(text, returnDataType);
} else {
// TODO: we could check that the PropExpression dataType is compatible with the returnDataType
return new PropExpression(text, entityType);
}
}
} else {
String fnName = text;
DataType[] argTypes = FnExpression.getArgTypes(fnName);
if (argTypes.length != _fnArgs.size()) {
throw new RuntimeException("Incorrect number of arguments to '" + fnName
+ "' function; was expecting " + argTypes.length);
}
List<Expression> exprs = new ArrayList<Expression>();
for (int fnIx = 0; fnIx < _fnArgs.size(); fnIx++) {
FnExpressionToken argToken = _fnArgs.get(fnIx);
Expression expr = argToken.toExpression(entityType, argTypes[fnIx] );
exprs.add(expr);
}
// TODO: we could check that the FnExpression dataType is compatible with the returnDataType
return new FnExpression(text, exprs);
}
}
private static FnExpressionToken parseToken(String source, int ix) {
ix = skipWhitespace(source, ix);
FnExpressionToken token = collectQuotedToken(source, ix);
if (token != null) {
return token;
}
token = new FnExpressionToken();
String badChars = "'\"";
while (ix < source.length()) {
char c = source.charAt(ix);
if (c == '(') {
ix++;
parseFnArgs(token, source, ix);
return token;
}
if (c == ',' || c == ')') {
token._nextIx = ix;
return token;
}
if (badChars.indexOf(c) >= 0) {
throw new RuntimeException("Unable to parse Fn name - encountered: " + c);
}
token._sb.append(c);
ix++;
}
token._nextIx = ix;
return token;
}
private static void parseFnArgs(FnExpressionToken token, String source, int ix) {
token._fnArgs = new ArrayList<FnExpressionToken>();
while (ix < source.length()) {
FnExpressionToken argToken = parseToken(source, ix);
ix = argToken._nextIx;
if (argToken._sb.length() != 0) {
token._fnArgs.add(argToken);
}
char c = source.charAt(ix);
ix++;
if (c == ')') break;
}
token._nextIx = ix;
return;
}
private static int skipWhitespace(String source, int ix) {
while (ix < source.length()) {
char c = source.charAt(ix);
if (c == ' ') {
ix++;
} else {
return ix;
}
}
return ix;
}
private static FnExpressionToken collectQuotedToken(String source, int ix) {
char c = source.charAt(ix);
if (c != '\'' && c != '"') return null;
FnExpressionToken token = new FnExpressionToken();
char quoteChar = c;
ix++;
while (ix < source.length()) {
c = source.charAt(ix);
ix++;
if (c == quoteChar ) {
token._nextIx = ix;
return token;
} else {
token._sb.append(c);
}
}
throw new RuntimeException("Quoted token was not terminated");
}
}