class BinaryOperationExpression extends ComplexExpression {

    
    private static final long serialVersionUID = 7524261639178859585L;


    private final IStandardExpression left;
    private final IStandardExpression right;

    
    protected BinaryOperationExpression(final IStandardExpression left, final IStandardExpression right) {
        super();
        Validate.notNull(left, "Left-side expression cannot be null");
        Validate.notNull(right, "Right-side expression cannot be null");
        this.left = left;
        this.right = right;
    }
    
    public IStandardExpression getLeft() {
        return this.left;
    }

    public IStandardExpression getRight() {
        return this.right;
    }



    protected String getStringRepresentation(final String operator) {
        final StringBuilder sb = new StringBuilder();
        if (this.left instanceof ComplexExpression) {
            sb.append(Expression.NESTING_START_CHAR);
            sb.append(this.left);
            sb.append(Expression.NESTING_END_CHAR);
        } else {
            sb.append(this.left);
        }
        sb.append(' ');
        sb.append(operator);
        sb.append(' ');
        if (this.right instanceof ComplexExpression) {
            sb.append(Expression.NESTING_START_CHAR);
            sb.append(this.right);
            sb.append(Expression.NESTING_END_CHAR);
        } else {
            sb.append(this.right);
        }
        return sb.toString();
    }
    
    
    
    
    protected static ExpressionParsingState composeBinaryOperationExpression(
            final ExpressionParsingState state, final int nodeIndex, final String[] operators,
            final boolean[] leniencies, final Class<? extends BinaryOperationExpression>[] operationClasses,
            final Method leftAllowedMethod, final Method rightAllowedMethod) {

        // Returning "state" means "try next in chain" or "success"
        // Returning "null" means parsing error
        //
        // "Lenient" means that if the last operator occurrence found does not result
        // in a valid parse result, the previous-to-last (and all previous to that)
        // should also be tried in sequence (this is mainly to avoid "-" symbols from
        // minus operators getting in the way of subtraction operations).

        final String input = state.get(nodeIndex).getInput();

        if (StringUtils.isEmptyOrWhitespace(input)) {
            return null;
        }

        String scannedInput = input.toLowerCase(); 

        do {
            
            int operatorIndex = -1;
            int operatorPosFrom = -1;
            int operatorPosTo = Integer.MAX_VALUE;
            int operatorLen = 0;
            
            for (int i = 0; i < operators.length; i++) {
                // Trying to fail quickly...
                final int currentOperatorPosFrom = scannedInput.lastIndexOf(operators[i]);
                if (currentOperatorPosFrom != -1) {
                    final int currentOperatorLen = operators[i].length();
                    final int currentOperatorPosTo = currentOperatorPosFrom + currentOperatorLen;
                    if (operatorPosFrom == -1 || operatorPosTo < currentOperatorPosFrom || 
                            (currentOperatorLen > operatorLen && currentOperatorPosTo >= operatorPosTo)) {
                        // the last condition is for allowing "<=" not to be confused with "<" or "neq" with "eq"
                        operatorPosFrom = currentOperatorPosFrom;
                        operatorLen = operators[i].length();
                        operatorPosTo = currentOperatorPosFrom + operatorLen;
                        operatorIndex = i;
                    }
                }
            }
            if (operatorPosFrom == -1) {
                return state;
            }
            
            if (doComposeBinaryOperationExpression(
                        state, nodeIndex, operators[operatorIndex], operationClasses[operatorIndex],
                        leftAllowedMethod, rightAllowedMethod, input, operatorPosFrom) == null) {

                if (leniencies[operatorIndex]) {
                    scannedInput = scannedInput.substring(0, operatorPosFrom);
                } else {
                    return null;
                }

            } else {
                return state;
            }

            
        } while (true);
        

    }
        
        
    
    
    
    private static ExpressionParsingState doComposeBinaryOperationExpression(
            final ExpressionParsingState state, final int nodeIndex, final String operator,
            final Class<? extends BinaryOperationExpression> operationClass,
            final Method leftAllowedMethod, final Method rightAllowedMethod,
            final String input, final int operatorPos) {<FILL_FUNCTION_BODY>}
    
    
    
    
}

             
        final String leftStr = input.substring(0, operatorPos).trim();
        final String rightStr = input.substring(operatorPos + operator.length()).trim();
         
        if (leftStr.length() == 0 || rightStr.length() == 0) {
            return null;
        }

        final Expression leftExpr = ExpressionParsingUtil.parseAndCompose(state, leftStr);
        try {
            if (leftExpr == null || !((Boolean)leftAllowedMethod.invoke(null,leftExpr)).booleanValue()) {
                return null;
            }
        } catch (final IllegalAccessException e) {
            // Should never happen, would be a programming error
            throw new TemplateProcessingException("Error invoking operand validation in binary operation", e);
        } catch (final InvocationTargetException e) {
            // Should never happen, would be a programming error
            throw new TemplateProcessingException("Error invoking operand validation in binary operation", e);
        }

        final Expression rightExpr = ExpressionParsingUtil.parseAndCompose(state, rightStr);
        try {
            if (rightExpr == null || !((Boolean)rightAllowedMethod.invoke(null,rightExpr)).booleanValue()) {
                return null;
            }
        } catch (final IllegalAccessException e) {
            // Should never happen, would be a programming error
            throw new TemplateProcessingException("Error invoking operand validation in binary operation", e);
        } catch (final InvocationTargetException e) {
            // Should never happen, would be a programming error
            throw new TemplateProcessingException("Error invoking operand validation in binary operation", e);
        }

        try {

            final BinaryOperationExpression operationExpression =
                operationClass.getDeclaredConstructor(IStandardExpression.class, IStandardExpression.class).
                            newInstance(leftExpr, rightExpr);
            state.setNode(nodeIndex, operationExpression);
            
        } catch (final TemplateProcessingException e) {
            throw e;
        } catch (final Exception e) {
            throw new TemplateProcessingException(
                    "Error during creation of Binary Operation expression for operator: \"" + operator + "\"", e);
        }

        return state;
        
    
<methods><variables>private static final long serialVersionUID