JcrPropertyPredicateEvaluator.java 10.7 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Node
 *  javax.jcr.NodeIterator
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Value
 *  javax.jcr.ValueFormatException
 *  javax.jcr.query.Row
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.jackrabbit.util.Text
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.search.eval;

import com.day.cq.search.Predicate;
import com.day.cq.search.eval.AbstractPredicateEvaluator;
import com.day.cq.search.eval.EvaluationContext;
import com.day.cq.search.eval.XPath;
import com.day.cq.search.facets.FacetExtractor;
import com.day.cq.search.facets.extractors.DistinctValuesFacetExtractor;
import com.day.cq.search.impl.util.GlobPatternUtil;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.query.Row;
import org.apache.felix.scr.annotations.Component;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=0, factory="com.day.cq.search.eval.PredicateEvaluator/property")
public class JcrPropertyPredicateEvaluator
extends AbstractPredicateEvaluator {
    private static final Logger log = LoggerFactory.getLogger(JcrPropertyPredicateEvaluator.class);
    public static final String PROPERTY = "property";
    public static final String VALUE = "value";
    public static final String OPERATION = "operation";
    public static final String OP_EQUALS = "equals";
    public static final String OP_UNEQUALS = "unequals";
    public static final String OP_LIKE = "like";
    public static final String OP_NOT = "not";
    public static final String OP_EXISTS = "exists";
    public static final String AND = "and";
    public static final String DEPTH = "depth";
    public static final String STEP = "*/";
    public static final int MAX_NUMBER_OF_VALUES = Integer.MAX_VALUE;

    @Override
    public String getXPathExpression(Predicate p, EvaluationContext context) {
        String property = p.get("property");
        int depth = Integer.parseInt(p.get("depth", "0"));
        String operation = p.get("operation");
        if ("not".equals(operation) || "exists".equals(operation) || p.hasNonEmptyValue("value")) {
            return this.getXPathExpression(property, p.get("value"), this.getOperation(p, operation), depth);
        }
        boolean and = p.getBool("and");
        StringBuilder builder = new StringBuilder();
        builder.append("(");
        for (Map.Entry<String, String> entry : p.getParameters().entrySet()) {
            String key = entry.getKey();
            if (key == null || !key.endsWith("_value")) continue;
            if (builder.length() > 1) {
                builder.append(and ? " and " : " or ");
            }
            builder.append(this.getXPathExpression(property, entry.getValue(), operation, depth));
        }
        if (builder.length() == 1) {
            return null;
        }
        builder.append(")");
        return builder.toString();
    }

    protected String getXPathExpression(String property, String value, String operation, int depth) {
        String expr = this.getXPathExpression(property, value, operation);
        StringBuilder builder = new StringBuilder();
        if (depth > 0) {
            builder.append("(");
        }
        for (int i = 0; i <= depth; ++i) {
            if (i > 0) {
                builder.append(" or ");
                for (int j = 0; j < i; ++j) {
                    builder.append("*/");
                }
            }
            builder.append(expr);
        }
        if (depth > 0) {
            builder.append(")");
        }
        return builder.toString();
    }

    protected String getXPathExpression(String property, String value, String operation) {
        if (property == null || property.length() == 0 || !"not".equals(operation) && !"exists".equals(operation) && (value == null || value.length() == 0)) {
            return null;
        }
        if ("equals".equals(operation)) {
            return XPath.getEqualsExpression(property, value);
        }
        if ("unequals".equals(operation)) {
            return XPath.getUnequalsExpression(property, value);
        }
        if ("like".equals(operation)) {
            return XPath.getJcrLikeExpression(property, value);
        }
        if ("exists".equals(operation)) {
            return XPath.getPropertyPath(property);
        }
        if ("not".equals(operation)) {
            return XPath.getNotExpression(property);
        }
        return XPath.getEqualsExpression(property, value);
    }

    protected String getEqualsExpression(String property, String value) {
        return XPath.getEqualsExpression(property, value);
    }

    private String getOperation(Predicate p, String operation) {
        if ("exists".equals(operation) && "false".equals(p.get("value", "true"))) {
            operation = "not";
        }
        return operation;
    }

    @Override
    public String[] getOrderByProperties(Predicate p, EvaluationContext context) {
        return new String[]{p.get("property")};
    }

    @Override
    public FacetExtractor getFacetExtractor(Predicate p, EvaluationContext context) {
        if (p.hasNonEmptyValue("property")) {
            Predicate template = p.clone();
            template.set("operation", "equals");
            return new DistinctValuesFacetExtractor(p.get("property"), null, template, "value");
        }
        return null;
    }

    @Override
    public boolean includes(Predicate p, Row row, EvaluationContext context) {
        String operation = p.get("operation", "equals");
        int depth = Integer.parseInt(p.get("depth", "0"));
        if ("not".equals(operation) || "exists".equals(operation) || p.hasNonEmptyValue("value")) {
            return this.includes(context.getNode(row), context.getPath(row), p.get("property"), p.get("value"), this.getOperation(p, operation), depth);
        }
        boolean and = p.getBool("and");
        operation = this.getOperation(p, operation);
        boolean emptyPredicate = true;
        for (Map.Entry<String, String> entry : p.getParameters().entrySet()) {
            String key = entry.getKey();
            if (key == null || !key.endsWith("_value")) continue;
            emptyPredicate = false;
            boolean match = this.includes(context.getNode(row), context.getPath(row), p.get("property"), entry.getValue(), operation, depth);
            if (and) {
                if (match) continue;
                return false;
            }
            if (!match) continue;
            return true;
        }
        if (and) {
            return true;
        }
        return emptyPredicate;
    }

    protected boolean includes(Node node, String path, String property, String value, String operation, int depth) {
        boolean matches = this.includes(node, path, property, value, operation);
        if (!matches && depth > 0) {
            try {
                NodeIterator it = node.getNodes();
                while (!matches && it.hasNext()) {
                    matches = this.includes((Node)it.next(), path, property, value, operation, depth - 1);
                }
            }
            catch (RepositoryException e) {
                log.error("Could not evaluate property = '" + property + "', value = '" + value + "', node = '" + path + "'", (Throwable)e);
                throw new RuntimeException("", (Throwable)e);
            }
        }
        return matches;
    }

    protected boolean includes(Node node, String path, String property, String value, String operation) {
        if (property == null || property.length() == 0 || !"not".equals(operation) && !"exists".equals(operation) && (value == null || value.length() == 0)) {
            return true;
        }
        try {
            String childNode = Text.getRelativeParent((String)property, (int)1);
            String propName = Text.getName((String)property);
            if (childNode.length() > 0) {
                if (node.hasNode(childNode)) {
                    node = node.getNode(childNode);
                } else {
                    return false;
                }
            }
            if (node.hasProperty(propName)) {
                Property prop = node.getProperty(propName);
                if (prop.isMultiple()) {
                    if ("not".equals(operation)) {
                        return prop.getValues() == null || prop.getValues().length == 0;
                    }
                    if ("exists".equals(operation)) {
                        return prop.getValues() != null && prop.getValues().length > 0;
                    }
                    for (Value v : prop.getValues()) {
                        if (!this.matches(value, operation, v.getString())) continue;
                        return true;
                    }
                    return false;
                }
                return this.matches(value, operation, prop.getString());
            }
            if ("not".equals(operation)) {
                return true;
            }
            if ("exists".equals(operation)) {
                return false;
            }
            return false;
        }
        catch (ValueFormatException e) {
            log.warn("Could not evaluate property = '" + property + "', value = '" + value + "', node = '" + path + "'", (Throwable)e);
        }
        catch (RepositoryException e) {
            log.error("Could not evaluate property = '" + property + "', value = '" + value + "', node = '" + path + "'", (Throwable)e);
            throw new RuntimeException("", (Throwable)e);
        }
        return true;
    }

    private boolean matches(String value, String operation, String propValue) {
        if ("not".equals(operation)) {
            return propValue == null;
        }
        if ("exists".equals(operation)) {
            return propValue != null;
        }
        if ("equals".equals(operation)) {
            return propValue.equals(value);
        }
        if ("unequals".equals(operation)) {
            return !propValue.equals(value);
        }
        if ("like".equals(operation)) {
            return Pattern.matches(this.convertWildcardsForGlobPattern(value), propValue);
        }
        return false;
    }

    private String convertWildcardsForGlobPattern(String term) {
        term = term.replace('%', '*').replace('_', '?');
        return GlobPatternUtil.convertWildcardToRegex(term);
    }

    @Override
    public boolean canXpath(Predicate predicate, EvaluationContext context) {
        return true;
    }

    @Override
    public boolean canFilter(Predicate predicate, EvaluationContext context) {
        return true;
    }
}