com.day.cq.search.PredicateConverter Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* Copyright 1997-2009 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;
import com.day.cq.search.eval.FulltextPredicateEvaluator;
import com.day.cq.search.eval.JcrPropertyPredicateEvaluator;
import com.day.cq.search.eval.PathPredicateEvaluator;
import com.day.cq.search.eval.RangePropertyPredicateEvaluator;
import com.day.cq.search.eval.TypePredicateEvaluator;
import com.day.cq.search.impl.builder.PredicateWalker;
import org.apache.jackrabbit.commons.query.GQL;
import org.apache.jackrabbit.util.Text;
import javax.jcr.RepositoryException;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* {@linkplain PredicateConverter} provides mappings from the
* predicate/predicate group data structure to others, such as a simple
* key/value string map (eg. a request parameter map).
*
* @since 5.2
*/
public abstract class PredicateConverter {
/**
* Prefix to separate predicate group parameters from child predicates (to
* be exact: from parameters of child predicates). This also means that
* there cannot be a predicate type named like this (ie.
* {@value}
). Examples to show the difference:
*
* p.limit = true
(group parameter)
* type = nt:file
(child predicate)
* path.exact = true
(child predicate parameter)
* group.type = nt:file
(child predicate group w/
* predicate)
* group.p.or = true
(group parameter of child group)
*
*/
public static final String GROUP_PARAMETER_PREFIX = "p";
/**
* All parameters starting with "_" will be ignored. Typical examples
* are "_charset_" or "_dc".
*/
public static final String IGNORE_PARAMETER_PREFIX = "_";
private static final String RANGE_DELIMITER = "..";
private static final String ORDER = "order";
private static final String DATERANGE_PRED = "daterange";
private static final String RANGEPROP_PRED = "rangeproperty";
private static final String DATE_REGEX = ".*[0-9]+-.*";
private static final String RANGE_REGEX = "(.*)\\.{2,}(.*)";
/**
* Converts a map with predicates and their parameters into a predicate
* tree. Accepts a map with strings as keys and either simple strings as
* values or string arrays as values. In the array case, the first value
* will be chosen.
*
*
* Note that all parameters starting with a "_" (see
* {@link #IGNORE_PARAMETER_PREFIX}) will be ignored. Typical examples are
* "_charset_" or "_dc".
*/
public static PredicateGroup createPredicates(Map predicateParameterMap) {
PredicateGroup root = new PredicateGroup();
PredicateTreeNode rootNode = new PredicateTreeNode(root);
for (final Object nameObj : predicateParameterMap.keySet()) {
String name = (String) nameObj;
if (name != null && name.startsWith(IGNORE_PARAMETER_PREFIX)) {
continue;
}
String value;
Object valueObj = predicateParameterMap.get(name);
if (valueObj instanceof String) {
value = (String) valueObj;
} else if (valueObj instanceof String[]) {
// take only the first value
String[] valueArray = (String[]) valueObj;
value = valueArray.length == 0 ? null : valueArray[0];
} else {
value = null;
}
if (isGroupParameter(name)) {
// excerpt, order by, limit, etc.
root.set(getGroupParameterName(name), value);
} else {
// predicates
rootNode.insert(name, value);
}
}
rootNode.generatePredicateTree();
return root;
}
/**
* Converts a predicate tree into a parameter map, the inverse
* transformation of {@link #createPredicates(Map)}.
*/
public static Map createMap(PredicateGroup root) {
CollectParameters collector = new CollectParameters();
collector.visit(root);
return collector.getParameters();
}
/**
* Returns an URL query part containing the given group. This is the same
* mapping as used in {@link #createMap(PredicateGroup)} and
* {@link #createPredicates(Map)}. For example, the returned value could be:
* type=cq:Page&path=/content
. Note that this won't be a
* complete URL, just a list of parameters for an URL query part. The keys
* and values will be properly escaped for use in an URL.
*/
public static String toURL(PredicateGroup group) {
StringBuffer urlPart = new StringBuffer();
Map params = createMap(group);
for (String key : params.keySet()) {
if (urlPart.length() > 0) {
urlPart.append("&");
}
urlPart.append(Text.escape(key)).append("=");
String value = params.get(key);
if (value != null) {
urlPart.append(Text.escape(value));
}
}
return urlPart.toString();
}
// -----------------------------------------------------< private >
private static class CollectParameters extends PredicateWalker {
private Map result = new TreeMap();
@Override
protected void visitInternal(Predicate predicate) {
if (predicate instanceof PredicateGroup) {
PredicateGroup group = (PredicateGroup) predicate;
Map groupParams = group.getParameters();
String path = group.getPath();
if (path == null) {
path = "";
} else {
path = path + ".";
}
for (String key : groupParams.keySet()) {
result.put(path + PredicateConverter.GROUP_PARAMETER_PREFIX + "." + key, groupParams.get(key));
}
} else {
Map params = predicate.getParameters();
String path = predicate.getPath();
for (String key : params.keySet()) {
if (key.equals(predicate.getType())) {
// embrace short form
result.put(path, params.get(key));
} else {
result.put(path + "." + key, params.get(key));
}
}
}
}
public Map getParameters() {
return result;
}
}
private static boolean isGroupParameter(String subParameter) {
return subParameter.startsWith(GROUP_PARAMETER_PREFIX + ".");
}
private static String getGroupParameterName(String subParameter) {
return subParameter.substring((GROUP_PARAMETER_PREFIX + ".").length());
}
/**
* PredicateTreeNode
is a helper data structure for parsing
* key-value based predicateEvaluator parameter maps and turning them into a
* "tree" of {@link Predicate Predicates} ({@link PredicateGroup} acts as
* the folder "node"). It ensures the order given by the naming schema.
* Parameters and their predicates can be inserted in any order (via
* {@link #insert(String, String)}) and the tree-structured predicates are
* generated at the end (via {@link #generatePredicateTree()}).
*
* Schema Examples:
* predicateType = foobar
* 2.predicateType = foobar
* 2.predicateType.param = something
* group.1.group.2.predicateType = bla
*/
private static class PredicateTreeNode {
public Predicate predicate;
/** Sorted map required to ensure order of keys */
private SortedMap