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"); } }