package com.esri.geoevent.processor.cacheawarefieldcalculator.expression; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.esri.ges.core.geoevent.FieldExpression; import com.esri.ges.core.geoevent.GeoEventDefinition; import com.esri.ges.util.Validator; public class Tokenizer { private final GeoEventDefinition geoEventDefinition; private final Map<String, Function> functions; private final Map<String, Operator> operators; Tokenizer(GeoEventDefinition geoEventDefinition, Map<String, Function> functions, Map<String, Operator> operators) { super(); this.geoEventDefinition = geoEventDefinition; this.functions = (functions != null) ? functions : new HashMap<String, Function>(); this.operators = (operators != null) ? operators : new HashMap<String, Operator>(); } List<Token> getTokens(final String expression) throws UnparsableExpressionException, UnknownFunctionException { final List<Token> tokens = new ArrayList<Token>(); final char[] chars = expression.toCharArray(); // iterate over the chars and fork on different types of input Token lastToken; for (int i = 0; i < chars.length; i++) { char c = chars[i]; if (c == ' ') continue; if (c == '\'') { final StringBuilder strBuilder = new StringBuilder(1); // handle the literals of the expression strBuilder.append(c); int strLen = 1; while (chars.length > i + strLen && chars[i + strLen] != '\'') { strBuilder.append(chars[i + strLen]); strLen++; } i += strLen; strBuilder.append('\''); lastToken = new ObjectToken(parseValue(strBuilder.toString())); } else if (Character.isDigit(c)) { final StringBuilder valueBuilder = new StringBuilder(1); // handle the numbers of the expression valueBuilder.append(c); int numberLen = 1; while (chars.length > i + numberLen && isDigitOrDecimalSeparator(chars[i + numberLen])) { valueBuilder.append(chars[i + numberLen]); numberLen++; } i += numberLen - 1; lastToken = new ObjectToken(parseValue(valueBuilder.toString())); } else if (Character.isLetter(c) || c == '_') { // can be a variable or function final StringBuilder nameBuilder = new StringBuilder(); nameBuilder.append(c); int offset = 1; char prev = c; while (chars.length > i + offset && isVariableOrFunctionCharacter(prev, chars[i + offset])) { nameBuilder.append(chars[i + offset++]); prev = nameBuilder.charAt(nameBuilder.length()-1); } i += offset-1; String name = nameBuilder.toString(); lastToken = isFunction(name) ? new FunctionToken(name, functions.get(name)) : createVariableToken(name); } else if (c == ',') lastToken = new FunctionSeparatorToken(); else if (isOperatorCharacter(c)) { // might be an operation StringBuilder symbolBuilder = new StringBuilder(); symbolBuilder.append(c); int offset = 1; while (chars.length > i + offset && (isOperatorCharacter(chars[i + offset])) && isOperatorStart(symbolBuilder.toString() + chars[i + offset])) { symbolBuilder.append(chars[i + offset]); offset++; } String symbol = symbolBuilder.toString(); if (operators.containsKey(symbol)) { i += offset-1; lastToken = new OperatorToken(symbol, operators.get(symbol)); } else throw new UnparsableExpressionException(c, i); } else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') lastToken = new ParenthesisToken(String.valueOf(c)); else throw new UnparsableExpressionException(c, i); tokens.add(lastToken); } return validateTokens(tokens); } private List<Token> validateTokens(final List<Token> tokens) throws UnparsableExpressionException { final List<Token> validatedTokens = new ArrayList<Token>(); for (Iterator<Token> i = tokens.iterator(); i.hasNext();) { Token token = i.next(); if (token instanceof FunctionToken) { if (i.hasNext()) { Token nextToken = i.next(); if (nextToken instanceof ParenthesisToken && ((ParenthesisToken)nextToken).isOpen()) { validatedTokens.add(token); validatedTokens.add(nextToken); continue; } else { validatedTokens.add(createVariableToken(token.getValue())); validatedTokens.add(nextToken); continue; } } else { validatedTokens.add(createVariableToken(token.getValue())); continue; } } validatedTokens.add(token); } return validatedTokens; } private VariableToken createVariableToken(String name) throws UnparsableExpressionException { FieldExpression fe = new FieldExpression(name); if (fe.isValid()) if (geoEventDefinition == null || geoEventDefinition.isFieldExist(fe)) return new VariableToken(fe); else throw new UnparsableExpressionException("Field '" + name + "' doesn't exist."); else throw new UnparsableExpressionException(fe.getErrorMessage()); } private Object parseValue(String s) { s = Validator.compactSpaces(s); if (s.startsWith("'") && s.endsWith("'")) return s; try { return Short.parseShort(s); } catch (NumberFormatException e) { ; } try { return Integer.parseInt(s); } catch (NumberFormatException e) { ; } try { return Long.parseLong(s); } catch (NumberFormatException e) { ; } try { return Double.parseDouble(s); } catch (NumberFormatException e) { ; } return s; } private boolean isDigitOrDecimalSeparator(char c) { return Character.isDigit(c) || c == '.'; } private boolean isFunction(String name) { return functions.containsKey(name); } private boolean isVariableOrFunctionCharacter(char prev, char cur) { return Character.isLetter(cur) || Character.isDigit(cur) || cur == '_' || cur == '[' || cur == ']' || (cur == '?' && prev == '[') || (cur == '*' && prev == '[') || cur == '.'; } private boolean isOperatorCharacter(char c) { for (String symbol : operators.keySet()) if (symbol.indexOf(c) != -1) return true; return false; } private boolean isOperatorStart(String op) { for (String operatorName : operators.keySet()) if (operatorName.startsWith(op)) return true; return false; } }