All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.x5.template.Conditional Maven / Gradle / Ivy

There is a newer version: 3.6.2
Show newest version
package com.x5.template;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.x5.template.filters.FilterArgs;
import com.x5.template.filters.RegexFilter;

public class Conditional
{
    private static final int EXISTENCE = 0;
    private static final int COMPARE_CONSTANT = 1;
    private static final int COMPARE_REGEX = 2;
    private static final int COMPARE_TAGEXPR = 3;

    private String rawTest;
    private int testType = EXISTENCE;
    private boolean isNeg = false;

    // when comparing against a constant, this is the constant
    // when comparing against a regex, this is the regex
    private String compareTo = null;

    // if left side or right side are tag expressions, pre-init them and store here
    private SnippetTag leftSide = null;
    private SnippetTag rightSide = null;

    public Conditional(String test)
    {
        rawTest = test;
        init(test);
    }

    public String toString()
    {
        return rawTest;
    }

    private void init(String test)
    {
        if (test == null || test.trim().length() == 0) {
            this.testType = EXISTENCE;
            this.isNeg = true;
            return;
        }

        test = test.trim();

        char firstChar = test.charAt(0);
        if (firstChar == '$' || firstChar == '!' || firstChar == '~') {
            test = test.substring(1);
            if (test.length() == 0) {
                this.testType = EXISTENCE;
                this.isNeg = true;
                return;
            }
        }
        // eat one more in the !~tag case
        if (firstChar == '!' && (test.charAt(0) == '$' || test.charAt(0) == '~')) {
            test = test.substring(1);
        }

        // find the end of the LHS
        int scanStart = skipModifiers(test, 0);

        if (test.indexOf('=', scanStart) < 0 && test.indexOf("!~", scanStart) < 0) {
            this.testType = EXISTENCE;
            // simplest case: no comparison, just a non-null (or null) test
            if (firstChar == '$' || firstChar == '~') {
                this.leftSide = SnippetTag.parseTag(test);
            } else if (firstChar == '!') {
                this.leftSide = SnippetTag.parseTag(test);
                this.isNeg = true;
            } else {
                this.isNeg = !test.equalsIgnoreCase("true");
            }
            return;
        }

        // now handle straight equality/inequality
        // ($asdf == $xyz) and ($asdf != $xyz)
        int eqPos = test.indexOf("==", scanStart);
        int ineqPos = test.indexOf("!=", scanStart);
        if (eqPos > 0 || ineqPos > 0) {
            this.isNeg = eqPos < 0;
            String tagA = test.substring(0, isNeg ? ineqPos : eqPos).trim();
            String tagB = test.substring((isNeg ? ineqPos : eqPos) + 2).trim();

            this.leftSide = SnippetTag.parseTag(tagA);

            if (tagB.charAt(0) == '$' || tagB.charAt(0) == '~') {
                this.rightSide = SnippetTag.parseTag(tagB.substring(1));
                this.testType = COMPARE_TAGEXPR;
            } else {
                this.testType = COMPARE_CONSTANT;
                String match = tagB;
                // allow tagB to be quoted?  if so, strip quotes here
                if (tagB.charAt(0) == '"' && tagB.charAt(match.length()-1) == '"') {
                    // FIXME should scan for unescaped end-quote in the middle of the string
                    match = tagB.substring(1, tagB.length()-1);
                    // quoted strings may have escaped characters.
                    // unescape.
                    match = unescape(match);
                } else if (tagB.charAt(0) == '\'' && tagB.charAt(match.length()-1) == '\'') {
                    // FIXME should scan for unescaped end-quote in the middle of the string
                    match = tagB.substring(1, tagB.length()-1);
                    // unescape
                    match = unescape(match);
                }
                this.compareTo = match;
            }
            return;
        }

        // handle pattern match
        int regexOpPos = test.indexOf("=~", scanStart);
        int negRegexOpPos = test.indexOf("!~", scanStart);
        if (regexOpPos < 0 && negRegexOpPos < 0) {
            isNeg = true;
            return;
        }
        this.isNeg = regexOpPos < 0;
        regexOpPos = isNeg ? negRegexOpPos : regexOpPos;

        String var = test.substring(0, regexOpPos).trim();
        String regex = test.substring(regexOpPos + 2).trim();
        this.leftSide = SnippetTag.parseTag(var);
        this.testType = COMPARE_REGEX;
        this.compareTo = regex;
    }

    public static int skipModifiers(String test, int start)
    {
        char[] chars = test.toCharArray();
        int i = start;
        for (; i compiledRegex = new HashMap();

    private Pattern compilePattern(String regex)
    {
        if (compiledRegex.containsKey(regex)) {
            return compiledRegex.get(regex);
        }

        int cursor = 0;
        if (regex.charAt(cursor) == 'm') cursor++;
        if (regex.charAt(cursor) == '/') cursor++;
        int regexEnd = RegexFilter.nextRegexDelim(regex,cursor);
        if (regexEnd < 0) return null; // fatal, unmatched regex boundary

        String pattern = regex.substring(cursor,regexEnd);
        // check for modifiers after regex end
        boolean ignoreCase = false;
        boolean multiLine = false;
        boolean dotAll = false;

        for (int i=regex.length()-1; i>regexEnd; i--) {
            char option = regex.charAt(i);
            if (option == 'i') ignoreCase = true;
            if (option == 'm') multiLine = true;
            if (option == 's') dotAll = true; // dot matches newlines too
        }

        if (multiLine) pattern = "(?m)" + pattern;
        if (ignoreCase) pattern = "(?i)" + pattern;
        if (dotAll) pattern = "(?s)" + pattern;

        Pattern p = Pattern.compile(pattern);
        compiledRegex.put(regex, p);

        return p;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy