class ExpressionMatcher {

    /** find "(nested_expression)" in "left and (nested_expression) or right" expression */
    private static final Pattern NESTED_EXPRESSION_PATTERN = Pattern.compile("\\([^()]+\\)");

    private ExpressionMatcher() {

    }

    /**
     * Test provided {@code expression} against list of {@code items}.
     * Case insensitive.
     *
     * @param expression - expression that will be parsed and evaluated against provided list of items
     * @param items - list of items
     * @return {@code true} if provided list of items satisfy expression criteria. {@code false} otherwise.
     */
    public static boolean matches(String expression, Collection<String> items) {<FILL_FUNCTION_BODY>}
}

        final String requiredNotRegex = "^@\\s*(!|not )";
        expression = StringUtil.trimToEmpty(expression);

        if (expression.equals(":TRUE")) {
            return true;
        }
        if (expression.equals(":FALSE")) {
            return false;
        }

        while (expression.contains("(")) {
            Matcher matcher = NESTED_EXPRESSION_PATTERN.matcher(expression);
            if (!matcher.find()) {
                throw new UnexpectedLiquibaseException("Cannot parse expression " + expression);
            }

            String left = expression.substring(0, matcher.start());
            String right = expression.substring(matcher.end());
            String nestedExpression = expression.substring(matcher.start() + 1, matcher.end() - 1); // +1/-1 -- exclude captured parenthesis

            expression = left + " :" + String.valueOf(matches(nestedExpression, items)).toUpperCase() + " " + right;
        }

        expression = expression.replaceAll("\\s*,\\s*", " or ");

        String[] orSplit = expression.split("\\s+or\\s+");
        if (orSplit.length > 1) {
            for (String split : orSplit) {
                if (matches(split, items)) {
                    return true;
                }
            }
            return false;
        }

        String[] andSplit = expression.split("\\s+and\\s+");
        if (andSplit.length > 1) {
            for (String split : andSplit) {
                if (!matches(split, items)) {
                    return false;
                }
            }
            return true;
        }

        boolean notExpression = false;
        if (expression.startsWith("!")) {
            notExpression = true;
            expression = expression.substring(1).trim();
        } else if (expression.toLowerCase().startsWith("not ")) {
            notExpression = true;
            expression = expression.substring(4).trim();
        } else if (expression.matches(requiredNotRegex + ".+")) {
            notExpression = true;
            expression = expression.replaceFirst(requiredNotRegex, "").trim();
        }

        boolean requiredExpression = false;
        if (expression.startsWith("@")) {
            requiredExpression = true;
            expression = expression.substring(1).trim();
        }

        if (!requiredExpression && items.isEmpty()) {
            return true;
        }

        if (expression.trim().equals(":TRUE")) {
            return !notExpression;
        }
        if (expression.trim().equals(":FALSE")) {
            return notExpression;
        }

        for (String item : items) {
            if (item.equalsIgnoreCase(expression)) {
                return !notExpression;
            }
        }
        return notExpression;
    
<no_super_class>