com.day.cq.search.eval.PathPredicateEvaluator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* Copyright 1997-2008 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
package com.day.cq.search.eval;
import java.util.Comparator;
import javax.jcr.query.Row;
import org.apache.jackrabbit.util.ISO9075;
import org.apache.jackrabbit.util.Text;
import com.day.cq.search.Predicate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceVendor;
/**
* Searches within a given path.
*
*
* Does not support facet extraction.
*
*
Name:
* path
*
* Properties:
*
* - path
* - path pattern; depending on
exact
, either the entire subtree
* will match (like appending //* in xpath, but note that this does not include
* the base path) (exact=false, default) or only an exact path matches, which
* can include wildcards (*); if self
is set, the entire subtree
* including the base node will be searched
* - exact
* - if exact is true/on, the exact path must match, but it can contain simple
* wildcards (*), that match names, but not "/"; if it is false (default) all
* descendents are included (optional)
* - flat
* - searches only the direct children (like appending "/*" in xpath) (only
* used if 'exact' is not true, optional)
* - self
* - searches the subtree but includes the base node given as path
* (no wildcards)
*
*
* @since 5.2
*/
@ServiceVendor("Adobe Systems Incorporated")
@Component(factory = "com.day.cq.search.eval.PredicateEvaluator/path")
public class PathPredicateEvaluator extends AbstractPredicateEvaluator {
public static final String PATH = "path";
public static final String EXACT = "exact";
public static final String FLAT = "flat";
public static final String SELF = "self";
/**
* Encodes absolute paths, but keeps wildcards in case this is
* an "exact" query containing wildcards.
*/
public static String encodePath(Predicate p) {
if (!p.hasNonEmptyValue(PATH)) {
return "";
}
String path = p.get(PATH);
if (p.getBool(EXACT) && containsWildcard(path)) {
// encode but keep wildcards (*)
// algorithm below does not like leading slash
// (but produces only absolute paths)
if (path.startsWith("/")) {
path = path.substring(1);
}
StringBuilder builder = new StringBuilder();
for (String name : Text.explode(path, '/', true)) {
builder.append("/");
if (name.length() == 0) {
continue;
} else if ("*".equals(name)) {
builder.append("*");
} else {
builder.append(ISO9075.encode(name));
}
}
return builder.toString();
}
return ISO9075.encodePath(path);
}
private static boolean containsWildcard(String s) {
return s.indexOf('*') >= 0;
}
private static boolean match(String pattern, String s) {
return recurseMatchPattern(pattern, s, 0, 0);
}
/**
* Adapted variant from GlobPattern. Reduced to "*" wildcard only and it
* will not match "/" (path separators).
*/
private static boolean recurseMatchPattern(String pattern, String s, int sIdx, int pIdx) {
int pLen = pattern.length();
int sLen = s.length();
for (; ; ) {
if (pIdx >= pLen) {
return (sIdx >= sLen);
}
if (sIdx >= sLen && pattern.charAt(pIdx) != '*') {
return false;
}
// Check for a '*' as the next pattern char.
// This is handled by a recursive call for
// each postfix of the name.
if (pattern.charAt(pIdx) == '*') {
// wild card does match everything except the /
while (sIdx < sLen && s.charAt(sIdx) != '/') {
sIdx++;
}
pIdx++;
return recurseMatchPattern(pattern, s, sIdx, pIdx);
}
// Check for backslash escapes
// We just skip over them to match the next char.
if (pattern.charAt(pIdx) == '\\') {
if (++pIdx >= pLen) {
return false;
}
}
if (pIdx < pLen && sIdx < sLen) {
if (pattern.charAt(pIdx) != s.charAt(sIdx)) {
return false;
}
}
++pIdx;
++sIdx;
}
}
@Override
public String getXPathExpression(Predicate p, EvaluationContext context) {
// this predicateEvaluator has special handling in RootEvaluator (if it is the first path
// predicateEvaluator), otherwise we handle it in the includes method below
return null;
}
@Override
public boolean includes(Predicate p, Row row, EvaluationContext context) {
if (!p.hasNonEmptyValue(PATH)) {
return true;
}
final String path = context.getPath(row);
if (path != null) {
String pattern = p.get(PATH);
if (p.getBool(EXACT)) {
// exact: path is addressed directly, or contains wildcards (*) to use
// no trailing slash, because Node.getPath() never has trailing slashes
if (pattern.endsWith("/")) {
pattern = pattern.substring(0, pattern.length() - 1);
}
if (containsWildcard(pattern)) {
return match(pattern, path);
}
return path.equals(pattern);
} else {
// find all descendants or children (flat=true)
// remove erroneously included double slashes (work in xpath, but not in our globbing)
pattern = pattern.replaceAll("//", "/");
if (p.getBool(FLAT)) {
// a) search only children (only in this folder): /content/dam/*
if (pattern.endsWith("/")) {
return path.matches(pattern + "[^/]+");
} else {
return path.matches(pattern + "/[^/]+");
}
} else if (p.getBool(SELF)) {
// b) descendant-or-self axis (deep search, but including the base node): /content/dam/descendant-or-self::node()
// NOTE: this is not supported in JCR XPath and Jackrabbit
return path.startsWith(pattern);
} else {
// c) child or descendants axis (deep search): /content/dam//*
// self matches not allowed in this case
if (path.equals(pattern)) {
return false;
}
return path.startsWith(pattern + "/");
}
}
}
return false;
}
@Override
public Comparator getOrderByComparator(Predicate predicate, final EvaluationContext context) {
return new Comparator() {
public int compare(Row r1, Row r2) {
final String path1 = context.getPath(r1);
if (path1 == null) return 0;
final String path2 = context.getPath(r2);
if (path2 == null) return 0;
return path1.compareTo(path2);
}
};
}
@Override
public boolean canXpath(Predicate predicate, EvaluationContext context) {
return false;
}
@Override
public boolean canFilter(Predicate predicate, EvaluationContext context) {
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy