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

com.day.cq.search.eval.PathPredicateEvaluator Maven / Gradle / Ivy

/*
 * 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.felix.scr.annotations.Component;
import org.apache.jackrabbit.util.ISO9075;
import org.apache.jackrabbit.util.Text;

import com.day.cq.search.Predicate;

/**
 * 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 */ @Component(metatype = false, 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