com.day.cq.search.Predicate Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* 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;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import com.day.cq.search.eval.PredicateEvaluator;
import com.day.cq.search.result.SearchResult;
/**
* A Predicate
is a single constraint for a {@link Query}. It is a
* pure data model object, consisting of a parameter map based on key-value
* string pairs.
*
*
* Each {@link Predicate} has a type ({@link #getType()}) that is used to find
* the right {@link PredicateEvaluator} that will handle this predicate and eg.
* translate it into an XPath statement.
*
*
* Using the composite pattern, the subclass {@link PredicateGroup} allows to
* group multiple predicates into one. This allows the query to actually consist
* of a predicate tree and reflects more complex queries that include sub-terms.
* The methods {@link #getName()}, {@link #getPath()} and {@link #getParent()}
* return that hierarchy information. The name/path is important, because facets
* will be returned in a map with the appropriate predicate path as key (see
* {@link SearchResult#getFacets()}).
*
*
* The parameters are set via the {@link #set(String, String)} method and
* retrieved via {@link #get(String)}, {@link #get(String, String)} or
* {@link #getParameters()}. Predicates are also {@link Cloneable}, so that it
* is easy to copy them.
*
* @since 5.2
*/
public class Predicate implements Cloneable {
public static final String PARAM_OFFSET = "offset";
public static final String PARAM_LIMIT = "limit";
public static final String PARAM_EXCERPT = "excerpt";
public static final String PARAM_FACET_STRATEGY = "facetStrategy";
/**
* How to handle traversals - translated into Query Option 'traversal'.
* Valid values = ok|fail|warn
*
* p.traversal=ok
*
*/
public static final String PARAM_OPTIONS_TRAVERSAL = "traversal";
public static final String TRAVERSAL_OK = "ok";
public static final String TRAVERSAL_FAIL = "fail";
public static final String TRAVERSAL_WARN = "warn";
/**
* Translated into Query Option 'index tag'
*
* p.indexTag=tagName
*
*/
public static final String PARAM_OPTIONS_INDEXTAG = "indexTag";
/**
* If true, this allows the implementation to optimize the query and return
* just a guessed {@link SearchResult#getTotalMatches() count}. Defaults to
* false, in which case the total is always accurate.
*
* @since 5.4
*/
public static final String PARAM_GUESS_TOTAL = "guessTotal";
public final static String ORDER_BY = "orderby";
/**
* Parameter on {@code orderby} to control whether the sort order is ascending
* (default) or descending. Possible values are {@code asc} and {@code desc}.
*
* orderby.sort=desc
*
*/
public final static String PARAM_SORT = "sort";
public final static String SORT_ASCENDING = "asc";
public final static String SORT_DESCENDING = "desc";
/**
* Parameter on {@code orderby} to control whether sorting is case insensitive or not.
* Only the value {@code ignore} is supported, in which case "a" comes before "B".
* If empty or left out, sorting is case sensitive, e.g. "B" comes before "a".
*
* orderby.case=ignore
*
*/
public final static String PARAM_CASE = "case";
public final static String IGNORE_CASE = "ignore";
final private String type;
private String name;
private PredicateGroup parent;
private Map params = new HashMap();
private boolean ignore = false;
/**
* Creates a {@link Predicate} of the given type. Note that the type
* cannot be changed later. The name will be deducted automatically,
* see {@link #getName()}. Initially no parameters will be set and
* no parent will be present.
*
* @param type
* predicate type name for finding the right
* {@link PredicateEvaluator}
*/
public Predicate(String type) {
this(null, type);
}
/**
* Creates a {@link Predicate} with the given name and type. Note that name
* and type cannot be changed later. Initially no parameters will be set and
* no parent will be present.
*
*
* The name should not be null
- this case is reserved for a
* root {@link PredicateGroup} (using
* {@link PredicateGroup#PredicateGroup()}).
*
* @param name
* identifying name for this predicate
* @param type
* predicate type name for finding the right
* {@link PredicateEvaluator}
*/
public Predicate(String name, String type) {
this.name = name;
this.type = type;
}
/**
* Returns the predicate type name for finding the right
* {@link PredicateEvaluator}.
*/
public String getType() {
return type;
}
/**
* Returns the name of this predicate. The name is used to allow round-trip
* serialization of predicate trees (where the order is encoded in the
* names) and for identifying predicates in the
* {@link SearchResult#getFacets() facet map}, in logging or other debug
* output.
*
*
* When a predicate is included in {@link PredicateGroup}, the names of the
* parent predicates will make up the {@link #getPath() path}, eg.
* parent.child.grandchild
. The name must not be changed after
* the predicate is passed to a {@link Query}.
*
*
* If no name was set previously, it will be automatically created. If this
* predicate is part of a group, it will get a name of the form "N_type"
* where N is the 1-based index of its position in the parent group and
* "type" is it's {@link #getType() type}. If it does not have a parent group,
* the name will only consist of the type.
*
* @return identifying name for this predicate (can be null
if
* no name was set)
*/
public String getName() {
if (name == null) {
if (parent == null) {
// ROOT node has null name by convention
return null;
}
// check for other siblings
if (parent.size() > 0) {
// need to prefix with index for unique name
int index = parent.indexOf(this) + 1;
return index + "_" + getType();
} else {
// type as name is enough
return getType();
}
}
return name;
}
/**
* Returns the path in a predicate tree of {@link PredicateGroup
* PredicateGroups} to this predicate, eg.
* parent.child.grandchild
. This can be null
if no
* name was set. The path is used for identifying predicates in the
* {@link SearchResult#getFacets() facet map}, in logging or other debug
* output.
*/
public String getPath() {
if (parent != null) {
String path = parent.getPath();
if (path != null) {
return path + "." + getName();
} else {
return getName();
}
} else if (getName() == null) {
return null;
} else {
return getName();
}
}
/**
* Returns the value for the given parameter name or null
if
* that parameter is not set. Parameters always have {@link String} values.
*/
public String get(String parameterName) {
return params.get(parameterName);
}
/**
* Returns the value for the given parameter name or the given default value
* if that parameter is not set. Parameters always have {@link String}
* values.
*/
public String get(String parameterName, String defaultValue) {
if (hasNonEmptyValue(parameterName)) {
return get(parameterName);
} else {
return defaultValue;
}
}
/**
* Returns the boolean value for the given parameter name or
* false
if that parameter is not set, ie. it assumes
* false
as the default of the boolean parameter.
*
*
* Since actual parameter values are strings, it is considered
* true
if the string value is either "true" or "on" (used by
* HTTP forms for checkboxes).
*/
public boolean getBool(String parameterName) {
if (hasNonEmptyValue(parameterName)) {
String value = get(parameterName);
return "on".equals(value) || "true".equals(value);
}
return false;
}
/**
* Sets the parameter 'parameterName' to the value given by 'value'.
* Parameters have always {@link String} values.
*
* @return returns itself for simple one-liners:
* group.add(new Predicate("mytype", "type").set("type", "nt:file"));
*/
public Predicate set(String parameterName, String value) {
params.put(parameterName, value);
return this;
}
/**
* Returns true
when there is a parameter present with the
* given name and if the string value is not empty. Parameters always have
* {@link String} values.
*/
public boolean hasNonEmptyValue(String parameterName) {
String value = get(parameterName);
return (value != null && value.length() > 0);
}
/**
* Returns an unmodifiable map of all parameters and their values.
*/
public Map getParameters() {
return Collections.unmodifiableMap(params);
}
/**
* This is used only during evaluation and marks this predicate as
* "invisible" for any {@link PredicateEvaluator} that will encounter it.
* Don't use that method when creating a query.
*
* @param ignore
* true
if this predicate should be ignored by
* evaluators (default is false
)
*/
public void setIgnored(boolean ignore) {
this.ignore = ignore;
}
/**
* This is used only during evaluation and indicates that this predicate is
* marked as "invisible" for any {@link PredicateEvaluator} that will
* encounter it. This method has no use for clients of the querybuilder API.
*
* @return true
if this predicate should be ignored by
* evaluators (default is false
)
*
*/
public boolean ignored() {
return this.ignore;
}
/**
* Clones this predicate so that the returned clone can be used completely
* independently from this original. Same as {@link #clone(boolean)
* clone(false)}.
*/
@Override
public Predicate clone() {
return clone(false);
}
/**
* Clones this predicate so that the returned clone can be used completely
* independently from this original. Allows for automatic reset of the name.
*
* @param resetName
* whether to reset the name to null
so that they
* will be automatically deducted (see {@link #getName()})
*/
public Predicate clone(boolean resetName) {
// we need to implement clone to make it public (Object's clone is protected)
try {
Predicate clone = (Predicate) super.clone();
// loose parent relationship - if a tree is cloned, the parent's clone method
// will take care of re-setting the parent
clone.parent = null;
clone.params = new HashMap();
for (String key : params.keySet()) {
clone.params.put(key, params.get(key));
}
if (resetName) {
clone.name = null;
}
return clone;
} catch (CloneNotSupportedException e) {
// cannot happen since Predicate implements Cloneable
throw new InternalError(e.toString());
}
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof Predicate))
return false;
Predicate other = (Predicate) obj;
return new EqualsBuilder()
.append(type, other.type)
.append(name, other.name)
.append(params, other.params)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31)
.append(type)
.append(name)
.append(params)
.toHashCode();
}
// -------------------------------------------< protected >
/**
* Used by {@link PredicateGroup} to store the hierarchy information when
* adding a predicate (through eg. {@link PredicateGroup#add(Predicate)}).
* Normally there is no reason to call this method, hence it is
* protected
.
*
* @param parent
* parent predicate group to set
*/
protected void setParent(PredicateGroup parent) {
this.parent = parent;
}
// -------------------------------------------< misc >
/**
* Overwrites the standard {@link Object#toString()} implementation and
* returns a debug-friendly string representation of the predicate.
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
String name = getName();
if (name == null) {
name = "ROOT";
}
buffer.append(name).append("=").append(getType()).append(": ");
Map allParams = new HashMap(params);
Iterator keyIter = allParams.keySet().iterator();
while (keyIter.hasNext()) {
final String key = keyIter.next();
buffer.append(key).append("=").append(allParams.get(key));
if (keyIter.hasNext()) {
buffer.append(", ");
}
}
return buffer.toString();
}
}