/** * Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT * All rights reserved. Use is subject to license terms. See LICENSE.TXT */ package org.diirt.datasource.formula; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.diirt.datasource.expression.DesiredRateExpression; import org.diirt.datasource.ReadFunction; import org.diirt.datasource.expression.DesiredRateExpressionImpl; import org.diirt.datasource.expression.DesiredRateExpressionList; import org.diirt.datasource.expression.DesiredRateExpressionListImpl; import org.diirt.datasource.expression.DesiredRateReadWriteExpression; import org.diirt.datasource.expression.DesiredRateReadWriteExpressionImpl; import org.diirt.datasource.expression.Expressions; import org.diirt.datasource.expression.WriteExpression; import org.diirt.vtype.ValueUtil; /** * Support for formula expressions. * * @author carcassi */ public class ExpressionLanguage { private ExpressionLanguage() { // No instances } /** * If the formula represents a single channels it returns the name, * null otherwise. * * @param formula the formula to parse * @return the channel it represents or null */ public static String channelFromFormula(String formula) { FormulaAst ast = FormulaAst.singleChannel(formula); if (ast == null) { return null; } else { return (String) ast.getValue(); } } /** * Returns the expression that will return the live value of the * given formula. * * @param formula the formula to parse * @return an expression for the formula */ public static DesiredRateReadWriteExpression<?, Object> formula(String formula) { DesiredRateExpression<?> exp = parseFormula(formula); if (exp instanceof LastOfChannelExpression) { return new DesiredRateReadWriteExpressionImpl<>(exp, org.diirt.datasource.vtype.ExpressionLanguage.vType(exp.getName())); } else if (exp instanceof ErrorDesiredRateExpression) { return new DesiredRateReadWriteExpressionImpl<>(exp, readOnlyWriteExpression("Parsing error")); } else { return new DesiredRateReadWriteExpressionImpl<>(exp, readOnlyWriteExpression("Read-only formula")); } } /** * Returns the expression corresponding to the formula represented by the * given Abstract Syntax Tree. * * @param ast a formula abstract syntax tree * @return an expression for the formula */ public static DesiredRateReadWriteExpression<?, Object> formula(FormulaAst ast) { DesiredRateExpression<?> exp = ast.toExpression(); if (exp instanceof LastOfChannelExpression) { return new DesiredRateReadWriteExpressionImpl<>(exp, org.diirt.datasource.vtype.ExpressionLanguage.vType(exp.getName())); } else if (exp instanceof ErrorDesiredRateExpression) { return new DesiredRateReadWriteExpressionImpl<>(exp, readOnlyWriteExpression("Parsing error")); } else { return new DesiredRateReadWriteExpressionImpl<>(exp, readOnlyWriteExpression("Read-only formula")); } } private static DesiredRateExpression<?> parseFormula(String formula) { try { return FormulaAst.formula(formula).toExpression(); } catch(RuntimeException ex) { return errorDesiredRateExpression(ex); } } /** * An expression that returns the value of the formula and return null * for empty or null formula. * <p> * Some expressions allow for null expression arguments to handle * optional elements. In those cases, using this method makes * undeclared arguments fall through. * * @param formula the formula, can be null * @return an expression of the given type; null if formula is null or empty */ public static DesiredRateExpression<?> formulaArg(String formula) { if (formula == null || formula.trim().isEmpty()) { return null; } return parseFormula(formula); } /** * An expression that returns the value of the formula making sure * it's of the given type. * * @param <T> the type to read * @param formula the formula * @param readType the type to read * @return an expression of the given type */ public static <T> DesiredRateExpression<T> formula(String formula, Class<T> readType) { DesiredRateExpression<?> exp = parseFormula(formula); return checkReturnType(readType, "Value", exp); } static DesiredRateExpression<?> cachedPv(String channelName) { return new LastOfChannelExpression<>(channelName, Object.class); } static DesiredRateExpression<?> namedConstant(String constantName) { Object value = FormulaRegistry.getDefault().findNamedConstant(constantName); if (value == null) { throw new IllegalArgumentException("No constant named '" + constantName + "' is defined"); } return org.diirt.datasource.ExpressionLanguage.constant(value, constantName); } static <T> DesiredRateExpression<T> cast(Class<T> clazz, DesiredRateExpression<?> arg1) { if (arg1 instanceof LastOfChannelExpression) { return ((LastOfChannelExpression<?>)arg1).cast(clazz); } @SuppressWarnings("unchecked") DesiredRateExpression<T> op1 = (DesiredRateExpression<T>) arg1; return op1; } static <T> DesiredRateExpressionList<T> cast(Class<T> clazz, DesiredRateExpressionList<?> args) { for (DesiredRateExpression<? extends Object> desiredRateExpression : args.getDesiredRateExpressions()) { cast(clazz, desiredRateExpression); } @SuppressWarnings("unchecked") DesiredRateExpressionList<T> op1 = (DesiredRateExpressionList<T>) args; return op1; } static String opName(String op, DesiredRateExpression<?> arg1, DesiredRateExpression<?> arg2) { return "(" + arg1.getName() + op + arg2.getName() + ")"; } static String opName(String op, DesiredRateExpression<?> arg) { return op + arg.getName(); } static String funName(String fun, DesiredRateExpression<?> arg) { return fun + "(" + arg.getName()+ ")"; } static DesiredRateExpression<?> powCast(DesiredRateExpression<?> arg1, DesiredRateExpression<?> arg2) { return function("^", new DesiredRateExpressionListImpl<Object>().and(arg1).and(arg2)); } static DesiredRateExpression<?> threeArgOp(String opName, DesiredRateExpression<?> arg1, DesiredRateExpression<?> arg2, DesiredRateExpression<?> arg3) { return function(opName, new DesiredRateExpressionListImpl<Object>().and(arg1).and(arg2).and(arg3)); } static DesiredRateExpression<?> twoArgOp(String opName, DesiredRateExpression<?> arg1, DesiredRateExpression<?> arg2) { return function(opName, new DesiredRateExpressionListImpl<Object>().and(arg1).and(arg2)); } static DesiredRateExpression<?> oneArgOp(String opName, DesiredRateExpression<?> arg) { return function(opName, new DesiredRateExpressionListImpl<Object>().and(arg)); } static DesiredRateExpression<?> function(String function, DesiredRateExpressionList<?> args) { Collection<FormulaFunction> matchedFunctions = FormulaRegistry.getDefault().findFunctions(function, args.getDesiredRateExpressions().size()); FormulaReadFunction readFunction = new FormulaReadFunction(Expressions.functionsOf(args), matchedFunctions, function); List<String> argNames = new ArrayList<>(args.getDesiredRateExpressions().size()); for (DesiredRateExpression<? extends Object> arg : args.getDesiredRateExpressions()) { argNames.add(arg.getName()); } return new FormulaFunctionReadExpression(args, readFunction, FormulaFunctions.format(function, argNames)); } static <T> WriteExpression<T> readOnlyWriteExpression(String errorMessage) { return new ReadOnlyWriteExpression<>(errorMessage, ""); } static <T> DesiredRateExpression<T> errorDesiredRateExpression(RuntimeException error) { return new ErrorDesiredRateExpression<>(error, ""); } static <T> DesiredRateExpression<T> checkReturnType(final Class<T> clazz, final String argName, final DesiredRateExpression<?> arg1) { return new DesiredRateExpressionImpl<T>(arg1, new ReadFunction<T>() { @Override public T readValue() { Object obj = arg1.getFunction().readValue(); if (obj == null) { return null; } if (clazz.isInstance(obj)) { return clazz.cast(obj); } else { throw new RuntimeException(argName + " must be a " + clazz.getSimpleName() + " (was " + ValueUtil.typeOf(obj).getSimpleName() + ")"); } } }, arg1.getName()); } }