Operations.java

/* 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.esigate.vars;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Support for ESI expressions.
 * 
 * @author Alexis Thaveau
 * @author Nicolas Richeton
 * 
 */
public final class Operations {

    private Operations() {

    }

    private final static Pattern AKAMAI_EXISTS_PATTERN = Pattern.compile("\\$exists\\((.*?)\\)");

    private static boolean executeOperation(String op) {

        int i;
        Double dop1, dop2;
        String op1, op2;

        try {
            if (op.contains("==")) {
                i = op.indexOf("==");
                op1 = VarUtils.removeSimpleQuotes(op.substring(0, i));
                op2 = VarUtils.removeSimpleQuotes(op.substring(i + 2));

                dop1 = getOperandAsNumeric(op1);
                dop2 = getOperandAsNumeric(op2);
                if (dop1 != null && dop2 != null) {
                    return dop1.equals(dop2);
                }

                return op1.equals(op2);

            } else if (op.contains("!=")) {
                i = op.indexOf("!=");
                op1 = VarUtils.removeSimpleQuotes(op.substring(0, i));
                op2 = VarUtils.removeSimpleQuotes(op.substring(i + 2));

                dop1 = getOperandAsNumeric(op1);
                dop2 = getOperandAsNumeric(op2);
                if (dop1 != null && dop2 != null) {
                    return !dop1.equals(dop2);
                }

                return !op1.equals(op2);

            } else if (op.contains(">=")) {
                i = op.indexOf(">=");
                op1 = VarUtils.removeSimpleQuotes(op.substring(0, i));
                op2 = VarUtils.removeSimpleQuotes(op.substring(i + 2));

                dop1 = getOperandAsNumeric(op1);
                dop2 = getOperandAsNumeric(op2);
                if (dop1 != null && dop2 != null) {
                    return dop1 >= dop2;
                }
                int cmp = op1.compareTo(op2);
                return cmp >= 0;

            } else if (op.contains("<=")) {
                i = op.indexOf("<=");
                op1 = VarUtils.removeSimpleQuotes(op.substring(0, i));
                op2 = VarUtils.removeSimpleQuotes(op.substring(i + 2));

                dop1 = getOperandAsNumeric(op1);
                dop2 = getOperandAsNumeric(op2);
                if (dop1 != null && dop2 != null) {
                    return dop1 <= dop2;
                }

                int cmp = op1.compareTo(op2);
                return cmp <= 0;

            } else if (op.contains(">")) {
                i = op.indexOf(">");
                op1 = VarUtils.removeSimpleQuotes(op.substring(0, i));
                op2 = VarUtils.removeSimpleQuotes(op.substring(i + 1));

                dop1 = getOperandAsNumeric(op1);
                dop2 = getOperandAsNumeric(op2);
                if (dop1 != null && dop2 != null) {
                    return dop1 > dop2;
                }

                int cmp = op1.compareTo(op2);
                return cmp > 0;
            } else if (op.contains("<")) {
                i = op.indexOf("<");
                op1 = VarUtils.removeSimpleQuotes(op.substring(0, i));
                op2 = VarUtils.removeSimpleQuotes(op.substring(i + 1));

                dop1 = getOperandAsNumeric(op1);
                dop2 = getOperandAsNumeric(op2);
                if (dop1 != null && dop2 != null) {
                    return dop1 < dop2;
                }

                int cmp = op1.compareTo(op2);
                return cmp < 0;
            }
        } catch (Exception e) {
            return false;
        }

        return false;
    }

    private static boolean executeOperations(ArrayList<String> operands, ArrayList<String> operations) {
        boolean res;
        ArrayList<Boolean> results = new ArrayList<>();

        try {
            for (String op : operands) {
                results.add(executeOperation(op));
            }

            if (results.size() == 1) {
                if (operations.size() == 1 && operations.get(0).equals("!")) {
                    return !results.get(0);
                } else {
                    return results.get(0);
                }
            }

            int i = 1;
            res = results.get(0);
            for (String op : operations) {
                if (op.equals("&")) {
                    res = res && results.get(i);
                } else if (op.equals("|")) {
                    res = res || results.get(i);
                } else {
                    res = false;
                }
                i++;
            }
        } catch (Exception e) {
            return false;
        }

        return res;
    }

    /**
     * Get an operand as a numeric type.
     * 
     * @param op
     *            operand as String
     * @return Double value or null if op is not numeric
     */
    private static Double getOperandAsNumeric(String op) {
        Double d = null;
        try {
            d = Double.valueOf(op);
        } catch (Exception e) {
            // Null is returned if not numeric.
        }
        return d;
    }

    public static boolean processOperators(String test) {

        if (test == null || test.equals("") || test.contains("$exists()") || test.contains("$exists('')")) {
            return false;
        }

        ArrayList<String> operands = new ArrayList<>();
        ArrayList<String> operations = new ArrayList<>();

        Matcher existsMatcher = AKAMAI_EXISTS_PATTERN.matcher(test);

        while (existsMatcher.find()) {
            String group = existsMatcher.group();
            String existsValue = group.substring(8, group.length() - 1);

            test = test.replace(group, existsValue + " != \"\"");
        }

        String s = test.replaceAll(" ", "");
        if (s.startsWith("!")) {
            operations.add("!");
        }

        if (s.indexOf('(') == -1) {
            s = "(" + s + ")";
        }

        // allocate (...)
        try {
            while (s.length() > 0) {
                int sbIndex = s.indexOf(')');
                operands.add(s.substring(s.indexOf('(') + 1, sbIndex));
                if (s.length() > sbIndex + 1) {
                    String oper = s.substring(sbIndex + 1, s.substring(sbIndex).indexOf('(') + sbIndex);
                    operations.add(oper);
                    s = s.substring(sbIndex + 2);
                } else {
                    s = "";
                }

            }
        } catch (Exception e) {
            return false;
        }

        return executeOperations(operands, operations);
    }

}