You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2010/10/18 12:06:07 UTC
svn commit: r1023721 - in
/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query:
QueryEngine.java SimpleQueryResult.java SimpleRow.java ValueComparator.java
qom/Constraints.java
Author: jukka
Date: Mon Oct 18 10:06:06 2010
New Revision: 1023721
URL: http://svn.apache.org/viewvc?rev=1023721&view=rev
Log:
JCR-2715: Improved join query performance
Add some generic query processing code. Work in progress...
Added:
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/QueryEngine.java (with props)
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleQueryResult.java (with props)
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleRow.java (with props)
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/ValueComparator.java (with props)
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/qom/Constraints.java (with props)
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/QueryEngine.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/QueryEngine.java?rev=1023721&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/QueryEngine.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/QueryEngine.java Mon Oct 18 10:06:06 2010
@@ -0,0 +1,883 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.commons.query;
+
+import static java.util.Locale.ENGLISH;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_ORDER_DESCENDING;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RangeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+import javax.jcr.query.qom.And;
+import javax.jcr.query.qom.BindVariableValue;
+import javax.jcr.query.qom.ChildNode;
+import javax.jcr.query.qom.ChildNodeJoinCondition;
+import javax.jcr.query.qom.Column;
+import javax.jcr.query.qom.Comparison;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.DescendantNode;
+import javax.jcr.query.qom.DescendantNodeJoinCondition;
+import javax.jcr.query.qom.DynamicOperand;
+import javax.jcr.query.qom.EquiJoinCondition;
+import javax.jcr.query.qom.FullTextSearchScore;
+import javax.jcr.query.qom.Join;
+import javax.jcr.query.qom.JoinCondition;
+import javax.jcr.query.qom.Length;
+import javax.jcr.query.qom.Literal;
+import javax.jcr.query.qom.LowerCase;
+import javax.jcr.query.qom.NodeLocalName;
+import javax.jcr.query.qom.NodeName;
+import javax.jcr.query.qom.Not;
+import javax.jcr.query.qom.Operand;
+import javax.jcr.query.qom.Or;
+import javax.jcr.query.qom.Ordering;
+import javax.jcr.query.qom.PropertyExistence;
+import javax.jcr.query.qom.PropertyValue;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.SameNode;
+import javax.jcr.query.qom.SameNodeJoinCondition;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.Source;
+import javax.jcr.query.qom.UpperCase;
+
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.commons.flat.TreeTraverser;
+import org.apache.jackrabbit.commons.iterator.FilteredRangeIterator;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
+import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
+import org.apache.jackrabbit.commons.predicate.Predicate;
+import org.apache.jackrabbit.commons.predicate.Predicates;
+import org.apache.jackrabbit.commons.predicate.RowPredicate;
+import org.apache.jackrabbit.commons.query.qom.Constraints;
+
+public class QueryEngine {
+
+ private final Session session;
+
+ private final NodeTypeManager ntManager;
+
+ private final QueryObjectModelFactory qomFactory;
+
+ private final ValueFactory valueFactory;
+
+ private final Map<String, Value> variables;
+
+ public QueryEngine(Session session, Map<String, Value> variables)
+ throws RepositoryException {
+ this.session = session;
+
+ Workspace workspace = session.getWorkspace();
+ this.ntManager = workspace.getNodeTypeManager();
+ this.qomFactory = workspace.getQueryManager().getQOMFactory();
+ this.valueFactory = session.getValueFactory();
+
+ this.variables = variables;
+ }
+
+ public QueryResult execute(
+ Column[] columns, Source source,
+ Constraint constraint, Ordering[] orderings)
+ throws RepositoryException {
+ if (source instanceof Selector) {
+ Selector selector = (Selector) source;
+ return execute(columns, selector, constraint, orderings);
+ } else if (source instanceof Join) {
+ Join join = (Join) source;
+ return execute(columns, join, constraint, orderings);
+ } else {
+ throw new UnsupportedRepositoryOperationException(
+ "Unknown source type: " + source);
+ }
+ }
+
+ protected QueryResult execute(
+ Column[] columns, Join join,
+ Constraint constraint, Ordering[] orderings)
+ throws RepositoryException {
+ Source left = join.getLeft();
+ Set<String> leftSelectors = getSelectorNames(left).keySet();
+
+ Source right = join.getRight();
+ Set<String> rightSelectors = getSelectorNames(right).keySet();
+
+ List<Row> leftRows = new ArrayList<Row>();
+ QueryResult leftResult =
+ execute(null, left, mapConstraintToSelectors(constraint, leftSelectors), null);
+ for (Row row : JcrUtils.getRows(leftResult)) {
+ leftRows.add(row);
+ }
+
+ QueryResult rightResult =
+ execute(null, right, mapConstraintToSelectors(constraint, rightSelectors), null);
+
+ return null;
+ }
+
+ /**
+ * Returns a mapped constraint that only refers to the given set of
+ * selectors. The returned constraint is guaranteed to match an as small
+ * as possible superset of the node tuples matched by the given original
+ * constraints.
+ *
+ * @param constraint original constraint
+ * @param selectors target selectors
+ * @return mapped constraint
+ * @throws RepositoryException if the constraint mapping fails
+ */
+ private Constraint mapConstraintToSelectors(
+ Constraint constraint, Set<String> selectors)
+ throws RepositoryException {
+ if (constraint == Constraints.TRUE || constraint == Constraints.FALSE) {
+ return constraint;
+ } else if (constraint instanceof And) {
+ And and = (And) constraint;
+ return Constraints.and(
+ qomFactory,
+ mapConstraintToSelectors(and.getConstraint1(), selectors),
+ mapConstraintToSelectors(and.getConstraint2(), selectors));
+ } else if (constraint instanceof Or) {
+ Or or = (Or) constraint;
+ return Constraints.or(
+ qomFactory,
+ mapConstraintToSelectors(or.getConstraint1(), selectors),
+ mapConstraintToSelectors(or.getConstraint2(), selectors));
+ } else if (constraint instanceof Not) {
+ Not not = (Not) constraint;
+ Constraint mapped =
+ mapConstraintToSelectors(not.getConstraint(), selectors);
+ // Tricky handling of NOT constraints, even NOT TRUE maps to TRUE
+ // to guarantee that the mapped result set remains a superset
+ if (mapped == Constraints.TRUE || mapped == Constraints.FALSE) {
+ return Constraints.TRUE;
+ } else {
+ return Constraints.not(qomFactory, mapped);
+ }
+ } else if (selectors.contains(getSelectorName(constraint))) {
+ // This constraint refers to one of the target selectors, keep it
+ return constraint;
+ } else {
+ // This constraint refers to some other selector, drop it
+ return Constraints.TRUE;
+ }
+ }
+
+ /**
+ * Returns the name of the selector referenced by the given constraint.
+ *
+ * @param constraint concrete constraint (i.e. not a logical construct)
+ * @return selector name
+ * @throws UnsupportedRepositoryOperationException
+ * if the constraint type is unknown
+ */
+ private String getSelectorName(Constraint constraint)
+ throws UnsupportedRepositoryOperationException {
+ if (constraint instanceof PropertyExistence) {
+ PropertyExistence pe = (PropertyExistence) constraint;
+ return pe.getSelectorName();
+ } else if (constraint instanceof Comparison) {
+ Comparison c = (Comparison) constraint;
+ return getSelectorName(c.getOperand1());
+ } else if (constraint instanceof SameNode) {
+ SameNode sn = (SameNode) constraint;
+ return sn.getSelectorName();
+ } else if (constraint instanceof ChildNode) {
+ ChildNode cn = (ChildNode) constraint;
+ return cn.getSelectorName();
+ } else if (constraint instanceof DescendantNode) {
+ DescendantNode dn = (DescendantNode) constraint;
+ return dn.getSelectorName();
+ } else {
+ throw new UnsupportedRepositoryOperationException(
+ "Unknown constraint type: " + constraint);
+ }
+ }
+
+ /**
+ * Returns the selector name referenced by the given dynamic operand.
+ *
+ * @param operand dynamic operand
+ * @return selector name
+ * @throws UnsupportedRepositoryOperationException
+ * if the operand type is unknown
+ */
+ private String getSelectorName(DynamicOperand operand)
+ throws UnsupportedRepositoryOperationException {
+ if (operand instanceof FullTextSearchScore) {
+ FullTextSearchScore ftss = (FullTextSearchScore) operand;
+ return ftss.getSelectorName();
+ } else if (operand instanceof Length) {
+ Length length = (Length) operand;
+ return getSelectorName(length.getPropertyValue());
+ } else if (operand instanceof LowerCase) {
+ LowerCase lower = (LowerCase) operand;
+ return getSelectorName(lower.getOperand());
+ } else if (operand instanceof NodeLocalName) {
+ NodeLocalName local = (NodeLocalName) operand;
+ return local.getSelectorName();
+ } else if (operand instanceof NodeName) {
+ NodeName name = (NodeName) operand;
+ return name.getSelectorName();
+ } else if (operand instanceof PropertyValue) {
+ PropertyValue value = (PropertyValue) operand;
+ return value.getSelectorName();
+ } else if (operand instanceof UpperCase) {
+ UpperCase upper = (UpperCase) operand;
+ return getSelectorName(upper.getOperand());
+ } else {
+ throw new UnsupportedRepositoryOperationException(
+ "Unknown dynamic operand type: " + operand);
+ }
+ }
+
+ protected Constraint narrow(
+ Constraint constraint, Set<String> selectors,
+ JoinCondition condition, List<Row> rows)
+ throws RepositoryException {
+ Constraint joinConstraint = Constraints.TRUE;
+ if (condition instanceof EquiJoinCondition) {
+ joinConstraint = getJoinConstraint(
+ (EquiJoinCondition) condition, rows, selectors);
+ } else if (condition instanceof SameNodeJoinCondition) {
+ joinConstraint = getJoinConstraint(
+ (SameNodeJoinCondition) condition, rows, selectors);
+ } else if (condition instanceof ChildNodeJoinCondition) {
+ joinConstraint = getJoinConstraint(
+ (ChildNodeJoinCondition) condition, rows, selectors);
+ } else if (condition instanceof DescendantNodeJoinCondition) {
+ joinConstraint = getJoinConstraint(
+ (DescendantNodeJoinCondition) condition, rows, selectors);
+ }
+ return Constraints.and(
+ qomFactory, joinConstraint, mapConstraintToSelectors(constraint, selectors));
+ }
+
+ /**
+ * Constructs a query constraint for the right side of an equi-join
+ * whose left side results are already known.
+ *
+ * @param condition the equi-join condition
+ * @param leftRows results of the left side of the join
+ * @return constraint for right side results that can possibly match
+ * one or more of the left side results
+ * @throws RepositoryException if the constraint can't be constructed
+ */
+ private Constraint getJoinConstraint(
+ EquiJoinCondition condition, List<Row> leftRows,
+ Set<String> rightSelectors) throws RepositoryException {
+ String selector1 = condition.getSelector1Name();
+ String property1 = condition.getProperty1Name();
+ String selector2 = condition.getSelector2Name();
+ String property2 = condition.getProperty2Name();
+ if (!rightSelectors.contains(selector2)) {
+ if (rightSelectors.contains(selector1)) {
+ selector1 = condition.getSelector2Name();
+ property1 = condition.getProperty2Name();
+ selector2 = condition.getSelector1Name();
+ property2 = condition.getProperty1Name();
+ } else {
+ return Constraints.TRUE;
+ }
+ }
+
+ // Collect all matching values from the left source
+ Set<Value> values = new HashSet<Value>();
+ PropertyValue left = qomFactory.propertyValue(selector2, property2);
+ for (Row row : leftRows) {
+ values.add(getValue(left, row));
+ }
+
+ // Convert each distinct value into a comparison constraint
+ List<Constraint> constraints = new ArrayList<Constraint>(values.size());
+ PropertyValue right = qomFactory.propertyValue(selector1, property1);
+ for (Value value : values) {
+ Literal literal = qomFactory.literal(value);
+ constraints.add(qomFactory.comparison(
+ right, JCR_OPERATOR_EQUAL_TO, literal));
+ }
+
+ // Build a big OR constraint over all the collected comparisons
+ return Constraints.or(qomFactory, constraints.toArray(
+ new Constraint[constraints.size()]));
+ }
+
+ private Constraint getJoinConstraint(
+ SameNodeJoinCondition condition, List<Row> leftRows,
+ Set<String> rightSelectors) throws RepositoryException {
+ String selector1 = condition.getSelector1Name();
+ String selector2 = condition.getSelector2Name();
+ String relativePath = condition.getSelector2Path();
+ if (!rightSelectors.contains(selector1)) {
+ if (relativePath == null && rightSelectors.contains(selector2)) {
+ selector1 = condition.getSelector2Name();
+ selector2 = condition.getSelector1Name();
+ } else {
+ return Constraints.TRUE;
+ }
+ }
+
+ // Collect all matching paths and convert them into constraints
+ List<Constraint> constraints = new ArrayList<Constraint>();
+ for (String path : getPaths(selector2, relativePath, leftRows)) {
+ constraints.add(qomFactory.descendantNode(selector1, path));
+ }
+
+ // Build a big OR constraint over all the collected comparisons
+ return Constraints.or(qomFactory, constraints.toArray(
+ new Constraint[constraints.size()]));
+ }
+
+ private Constraint getJoinConstraint(
+ ChildNodeJoinCondition condition, List<Row> leftRows,
+ Set<String> rightSelectors) throws RepositoryException {
+ String parent = condition.getParentSelectorName();
+ String child = condition.getChildSelectorName();
+ if (!rightSelectors.contains(child)) {
+ return Constraints.TRUE;
+ }
+
+ // Collect all matching paths and convert them into constraints
+ List<Constraint> constraints = new ArrayList<Constraint>();
+ for (String path : getPaths(parent, null, leftRows)) {
+ constraints.add(qomFactory.descendantNode(child, path));
+ }
+
+ // Build a big OR constraint over all the collected comparisons
+ return Constraints.or(qomFactory, constraints.toArray(
+ new Constraint[constraints.size()]));
+ }
+
+ private Constraint getJoinConstraint(
+ DescendantNodeJoinCondition condition, List<Row> leftRows,
+ Set<String> rightSelectors) throws RepositoryException {
+ String ancestor = condition.getAncestorSelectorName();
+ String descendant = condition.getDescendantSelectorName();
+ if (!rightSelectors.contains(descendant)) {
+ return Constraints.TRUE;
+ }
+
+ // Collect all matching paths and convert them into constraints
+ List<Constraint> constraints = new ArrayList<Constraint>();
+ for (String path : getPaths(ancestor, null, leftRows)) {
+ constraints.add(qomFactory.descendantNode(descendant, path));
+ }
+
+ // Build a big OR constraint over all the collected comparisons
+ return Constraints.or(qomFactory, constraints.toArray(
+ new Constraint[constraints.size()]));
+ }
+
+ private Set<String> getPaths(
+ String selectorName, String relativePath, List<Row> rows)
+ throws RepositoryException {
+ Set<String> paths = new HashSet<String>();
+ for (Row row : rows) {
+ try {
+ Node node = row.getNode(selectorName);
+ if (relativePath != null) {
+ node = node.getNode(relativePath);
+ }
+ paths.add(node.getPath());
+ } catch (PathNotFoundException e) {
+ // Node at relative path not found, skip
+ }
+ }
+ return paths;
+ }
+
+ protected QueryResult execute(
+ Column[] columns, Selector selector,
+ Constraint constraint, Ordering[] orderings)
+ throws RepositoryException {
+ Map<String, NodeType> selectorMap = getSelectorNames(selector);
+ final String[] selectorNames =
+ selectorMap.keySet().toArray(new String[selectorMap.size()]);
+
+ final Map<String, PropertyValue> columnMap =
+ getColumnMap(columns, selectorMap);
+ final String[] columnNames =
+ columnMap.keySet().toArray(new String[columnMap.size()]);
+
+ final double[] scores = new double[] { 1.0 };
+
+ Iterator<Node> nodes =
+ TreeTraverser.nodeIterator(session.getRootNode());
+ RangeIterator rows = new RangeIteratorAdapter(nodes) {
+ @Override
+ public Object next() {
+ try {
+ Value[] values = new Value[columnNames.length];
+ Row row = new SimpleRow(
+ columnNames, values, selectorNames,
+ new Node[] { (Node) super.next() }, scores);
+ for (int i = 0; i < values.length; i++) {
+ values[i] =
+ getValue(columnMap.get(columnNames[i]), row);
+ }
+ return row;
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ RangeIterator filtered = new FilteredRangeIterator(
+ rows, getPredicate(selector, constraint));
+ QueryResult result = new SimpleQueryResult(
+ columnNames, selectorNames, new RowIteratorAdapter(filtered));
+ return sort(result, orderings);
+ }
+
+ private Predicate getPredicate(Selector selector, Constraint constraint)
+ throws RepositoryException {
+ final String name = selector.getNodeTypeName();
+ Predicate predicate = getPredicate(constraint);
+ if (name.equals(ntManager.getNodeType(NodeType.NT_BASE).getName())) {
+ return predicate;
+ } else {
+ Predicate typeCheck = new RowPredicate(selector.getSelectorName()) {
+ @Override
+ protected boolean evaluate(Node node)
+ throws RepositoryException {
+ return node.isNodeType(name);
+ }
+ };
+ return Predicates.and(typeCheck, predicate);
+ }
+ }
+
+ private Map<String, PropertyValue> getColumnMap(
+ Column[] columns, Map<String, NodeType> selectors)
+ throws RepositoryException {
+ Map<String, PropertyValue> map =
+ new LinkedHashMap<String, PropertyValue>();
+ if (columns != null && columns.length > 0) {
+ for (int i = 0; i < columns.length; i++) {
+ String name = columns[i].getColumnName();
+ if (name != null) {
+ map.put(name, qomFactory.propertyValue(
+ columns[i].getSelectorName(),
+ columns[i].getPropertyName()));
+ } else {
+ String selector = columns[i].getSelectorName();
+ map.putAll(getColumnMap(selector, selectors.get(selector)));
+ }
+ }
+ } else {
+ for (Map.Entry<String, NodeType> selector : selectors.entrySet()) {
+ map.putAll(getColumnMap(
+ selector.getKey(), selector.getValue()));
+ }
+ }
+ return map;
+ }
+
+ private Map<String, PropertyValue> getColumnMap(
+ String selector, NodeType type) throws RepositoryException {
+ Map<String, PropertyValue> map =
+ new LinkedHashMap<String, PropertyValue>();
+ for (PropertyDefinition definition : type.getPropertyDefinitions()) {
+ String name = definition.getName();
+ if (!definition.isMultiple() && !"*".equals(name)) {
+ // TODO: Add proper quoting
+ map.put(selector + "." + name,
+ qomFactory.propertyValue(selector, name));
+ }
+ }
+ return map;
+ }
+
+ private Map<String, NodeType> getSelectorNames(Source source)
+ throws RepositoryException {
+ if (source instanceof Selector) {
+ Selector selector = (Selector) source;
+ String name = selector.getSelectorName();
+ return Collections.singletonMap(name, ntManager.getNodeType(name));
+ } else if (source instanceof Join) {
+ Join join = (Join) source;
+ Map<String, NodeType> map = new LinkedHashMap<String, NodeType>();
+ map.putAll(getSelectorNames(join.getLeft()));
+ map.putAll(getSelectorNames(join.getRight()));
+ return map;
+ } else {
+ throw new UnsupportedRepositoryOperationException(
+ "Unknown source type: " + source);
+ }
+ }
+
+ /**
+ * Sorts the given query results according to the given QOM orderings.
+ * If one or more orderings have been specified, this method will iterate
+ * through the entire original result set, order the collected rows, and
+ * return a new result set based on the sorted collection of rows.
+ *
+ * @param result original query results
+ * @param orderings QOM orderings
+ * @return sorted query results
+ * @throws RepositoryException if the results can not be sorted
+ */
+ public QueryResult sort(QueryResult result, final Ordering[] orderings)
+ throws RepositoryException {
+ if (orderings != null && orderings.length > 0) {
+ List<Row> rows = new ArrayList<Row>();
+
+ RowIterator iterator = result.getRows();
+ while (iterator.hasNext()) {
+ rows.add(iterator.nextRow());
+ }
+
+ Collections.sort(rows, new Comparator<Row>() {
+ public int compare(Row a, Row b) {
+ try {
+ for (Ordering ordering : orderings) {
+ Operand operand = ordering.getOperand();
+ Value va = getValue(operand, a);
+ Value vb = getValue(operand, b);
+ int order = new ValueComparator().compare(va, vb);
+ if (JCR_ORDER_DESCENDING.equals(ordering.getOrder())) {
+ order = -order;
+ }
+ if (order != 0) {
+ return order;
+ }
+ }
+ return 0;
+ } catch (RepositoryException e) {
+ throw new RuntimeException("Unable to compare rows", e);
+ }
+ }
+ });
+
+ return new SimpleQueryResult(
+ result.getColumnNames(), result.getSelectorNames(),
+ new RowIteratorAdapter(rows));
+ } else {
+ return result;
+ }
+ }
+
+ public Predicate getPredicate(Constraint constraint) {
+ if (constraint == Constraints.TRUE) {
+ return Predicate.TRUE;
+ } else if (constraint == Constraints.FALSE) {
+ return Predicate.FALSE;
+ } else if (constraint instanceof And) {
+ And and = (And) constraint;
+ return Predicates.and(
+ getPredicate(and.getConstraint1()),
+ getPredicate(and.getConstraint2()));
+ } else if (constraint instanceof Or) {
+ Or or = (Or) constraint;
+ return Predicates.or(
+ getPredicate(or.getConstraint1()),
+ getPredicate(or.getConstraint2()));
+ } else if (constraint instanceof Not) {
+ Not not = (Not) constraint;
+ return Predicates.not(getPredicate(not.getConstraint()));
+ } else if (constraint instanceof Not) {
+ Not not = (Not) constraint;
+ return Predicates.not(getPredicate(not.getConstraint()));
+ } else if (constraint instanceof PropertyExistence) {
+ final PropertyExistence pe = (PropertyExistence) constraint;
+ return new RowPredicate(pe.getSelectorName()) {
+ @Override
+ protected boolean evaluate(Node node)
+ throws RepositoryException {
+ return node.hasProperty(pe.getPropertyName());
+ }
+ };
+ } else if (constraint instanceof Comparison) {
+ final Comparison c = (Comparison) constraint;
+ return new RowPredicate() {
+ @Override
+ protected boolean evaluate(Row row)
+ throws RepositoryException {
+ return new ValueComparator().evaluate(
+ c.getOperator(),
+ getValue(c.getOperand1(), row),
+ getValue(c.getOperand2(), row));
+ }
+ };
+ } else if (constraint instanceof SameNode) {
+ final SameNode sn = (SameNode) constraint;
+ return new RowPredicate(sn.getSelectorName()) {
+ @Override
+ protected boolean evaluate(Node node)
+ throws RepositoryException {
+ return node.getPath().equals(sn.getPath());
+ }
+ };
+ } else if (constraint instanceof ChildNode) {
+ final ChildNode cn = (ChildNode) constraint;
+ return new RowPredicate(cn.getSelectorName()) {
+ @Override
+ protected boolean evaluate(Node node)
+ throws RepositoryException {
+ if (node.getDepth() > 0) {
+ String path = node.getParent().getPath();
+ return path.equals(cn.getParentPath());
+ } else {
+ return false;
+ }
+ }
+ };
+ } else if (constraint instanceof DescendantNode) {
+ final DescendantNode dn = (DescendantNode) constraint;
+ return new RowPredicate(dn.getSelectorName()) {
+ @Override
+ protected boolean evaluate(Node node)
+ throws RepositoryException {
+ if (node.getDepth() > 0) {
+ Node parent = node.getParent();
+ if (parent.getPath().equals(dn.getAncestorPath())) {
+ return true;
+ } else {
+ return evaluate(parent);
+ }
+ } else {
+ return false;
+ }
+ }
+ };
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown constraint type: " + constraint);
+ }
+ }
+
+ /**
+ * Evaluates the given operand against the given row. Subclasses can
+ * customise the evaluation process by overriding one or more of the
+ * protected getValue() methods to which this method dispatches the
+ * evaluation process.
+ *
+ * @param operand operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ public Value getValue(Operand operand, Row row)
+ throws RepositoryException {
+ if (operand instanceof BindVariableValue) {
+ return getValue((BindVariableValue) operand, row);
+ } else if (operand instanceof FullTextSearchScore) {
+ return getValue((FullTextSearchScore) operand, row);
+ } else if (operand instanceof Length) {
+ return getValue((Length) operand, row);
+ } else if (operand instanceof Literal) {
+ return getValue((Literal) operand, row);
+ } else if (operand instanceof LowerCase) {
+ return getValue((LowerCase) operand, row);
+ } else if (operand instanceof NodeLocalName) {
+ return getValue((NodeLocalName) operand, row);
+ } else if (operand instanceof NodeName) {
+ return getValue((NodeName) operand, row);
+ } else if (operand instanceof PropertyValue) {
+ return getValue((PropertyValue) operand, row);
+ } else if (operand instanceof UpperCase) {
+ return getValue((UpperCase) operand, row);
+ } else {
+ throw new UnsupportedRepositoryOperationException(
+ "Unknown operand type: " + operand);
+ }
+ }
+
+ /**
+ * Returns the value of the given variable value operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand variable value operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(BindVariableValue operand, Row row)
+ throws RepositoryException {
+ String name = operand.getBindVariableName();
+ Value value = variables.get(name);
+ if (value != null) {
+ return value;
+ } else {
+ throw new RepositoryException("Unknown bind variable: $" + name);
+ }
+ }
+
+ /**
+ * Returns the value of the given search score operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand search score operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(FullTextSearchScore operand, Row row)
+ throws RepositoryException {
+ return valueFactory.createValue(row.getScore(operand.getSelectorName()));
+ }
+
+ /**
+ * Returns the value of the given value length operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @see #getProperty(PropertyValue, Row)
+ * @param operand value length operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(Length operand, Row row)
+ throws RepositoryException {
+ Property property = getProperty(operand.getPropertyValue(), row);
+ return valueFactory.createValue(property.getLength());
+ }
+
+ /**
+ * Returns the value of the given literal value operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand literal value operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(Literal operand, Row row) {
+ return operand.getLiteralValue();
+ }
+
+ /**
+ * Returns the value of the given lower case operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand lower case operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(LowerCase operand, Row row)
+ throws RepositoryException {
+ Value value = getValue(operand.getOperand(), row);
+ return valueFactory.createValue(value.getString().toLowerCase(ENGLISH));
+ }
+
+ /**
+ * Returns the value of the given local name operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand local name operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(NodeLocalName operand, Row row)
+ throws RepositoryException {
+ String name = row.getNode(operand.getSelectorName()).getName();
+ int colon = name.indexOf(':');
+ if (colon != -1) {
+ name = name.substring(colon + 1);
+ }
+ return valueFactory.createValue(name, PropertyType.NAME);
+ }
+
+ /**
+ * Returns the value of the given node name operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand node name operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(NodeName operand, Row row)
+ throws RepositoryException {
+ Node node = row.getNode(operand.getSelectorName());
+ return valueFactory.createValue(node.getName(), PropertyType.NAME);
+ }
+
+ /**
+ * Returns the value of the given property value operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @see #getProperty(PropertyValue, Row)
+ * @param operand property value operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(PropertyValue operand, Row row)
+ throws RepositoryException {
+ return getProperty(operand, row).getValue();
+ }
+
+ /**
+ * Returns the value of the given upper case operand at the given row.
+ * Subclasses can override this method to customise the evaluation.
+ *
+ * @param operand upper case operand
+ * @param row row
+ * @return value of the operand at the given row
+ * @throws RepositoryException if the operand can't be evaluated
+ */
+ protected Value getValue(UpperCase operand, Row row)
+ throws RepositoryException {
+ Value value = getValue(operand.getOperand(), row);
+ return valueFactory.createValue(value.getString().toUpperCase(ENGLISH));
+ }
+
+ /**
+ * Returns the identified property from the given row. This method
+ * is used by both the {@link #getValue(Length, Row)} and the
+ * {@link #getValue(PropertyValue, Row)} methods to access properties.
+ * Subclasses can override this method to customise property access.
+ *
+ * @param operand property value operand
+ * @param row row
+ * @return the identified property
+ * @throws RepositoryException if the property can't be accessed
+ */
+ protected Property getProperty(PropertyValue operand, Row row)
+ throws RepositoryException {
+ String selector = operand.getSelectorName();
+ String property = operand.getPropertyName();
+ return row.getNode(selector).getProperty(property);
+ }
+
+}
Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/QueryEngine.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleQueryResult.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleQueryResult.java?rev=1023721&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleQueryResult.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleQueryResult.java Mon Oct 18 10:06:06 2010
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.commons.query;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+
+import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
+
+/**
+ * Simple query result implementation.
+ */
+public class SimpleQueryResult implements QueryResult {
+
+ /**
+ * The column names of this query.
+ */
+ private final String[] columnNames;
+
+ /**
+ * The selector names of this query.
+ */
+ private final String[] selectorNames;
+
+ /**
+ * Query result rows. Set to <code>null</code> when the iterator has
+ * already been accessed.
+ */
+ private RowIterator rowIterator;
+
+ /**
+ * Creates a query result with the given column and selector names and
+ * row iterator.
+ *
+ * @param columnNames column names
+ * @param selectorNames selector names
+ * @param rowIterator iterator over matching rows
+ */
+ protected SimpleQueryResult(
+ String[] columnNames, String[] selectorNames,
+ RowIterator rowIterator) {
+ assert columnNames != null;
+ assert selectorNames != null && selectorNames.length >= 1;
+ assert rowIterator != null;
+ this.columnNames = columnNames;
+ this.selectorNames = selectorNames;
+ this.rowIterator = rowIterator;
+ }
+
+ /**
+ * Returns the column names of this query. Note that the returned array
+ * is not protected against modification by the client application.
+ *
+ * @return column names
+ */
+ public String[] getColumnNames() {
+ return columnNames;
+ }
+
+ /**
+ * Returns the selector names of this query. Note that the returned array
+ * is not protected against modification by the client application.
+ *
+ * @return selector names
+ */
+ public String[] getSelectorNames() {
+ return selectorNames;
+ }
+
+ /**
+ * Returns the query result rows.
+ *
+ * @return query result rows
+ * @throws RepositoryException if the query results have already
+ * been iterated through
+ */
+ public synchronized RowIterator getRows() throws RepositoryException {
+ if (rowIterator != null) {
+ RowIterator iterator = rowIterator;
+ rowIterator = null;
+ return iterator;
+ } else {
+ throw new RepositoryException(
+ "This query result has already been iterated through");
+ }
+ }
+
+ /**
+ * Returns the nodes that match this query.
+ *
+ * @return matching nodes
+ * @throws RepositoryException if this query has more than one selector,
+ * or if the query results have already been
+ * iterated through
+ */
+ public NodeIterator getNodes() throws RepositoryException {
+ if (selectorNames.length == 1) {
+ return new NodeIteratorAdapter(getRows()) {
+ @Override
+ public Object next() {
+ Row row = (Row) super.next();
+ try {
+ return row.getNode();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(
+ "Unable to access the node in " + row, e);
+ }
+ }
+ };
+ } else {
+ throw new RepositoryException(
+ "This query result contains more than one selector");
+ }
+ }
+
+}
Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleQueryResult.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleRow.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleRow.java?rev=1023721&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleRow.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleRow.java Mon Oct 18 10:06:06 2010
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.commons.query;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.query.Row;
+
+public class SimpleRow implements Row {
+
+ private final String[] columnNames;
+
+ private final Value[] values;
+
+ private final String[] selectorNames;
+
+ private final Node[] nodes;
+
+ private final double[] scores;
+
+ public SimpleRow(
+ String[] columnNames, Value[] values,
+ String[] selectorNames, Node[] nodes, double[] scores) {
+ this.columnNames = columnNames;
+ this.values = values;
+ this.selectorNames = selectorNames;
+ this.nodes = nodes;
+ this.scores = scores;
+ }
+
+ public Value[] getValues() {
+ return values;
+ }
+
+ public Value getValue(String columnName) throws ItemNotFoundException {
+ for (int i = 0; i < columnNames.length; i++) {
+ if (columnNames[i].equals(columnName)) {
+ return values[i];
+ }
+ }
+ throw new ItemNotFoundException("Unknown column name: " + columnName);
+ }
+
+ public Node getNode() throws RepositoryException {
+ checkSingleSelector();
+ return nodes[0];
+ }
+
+ public Node getNode(String selectorName) throws RepositoryException {
+ return nodes[getSelectorIndex(selectorName)];
+ }
+
+ public String getPath() throws RepositoryException {
+ return getNode().getPath();
+ }
+
+ public String getPath(String selectorName) throws RepositoryException {
+ return getNode(selectorName).getPath();
+ }
+
+ public double getScore() throws RepositoryException {
+ checkSingleSelector();
+ return scores[0];
+ }
+
+ public double getScore(String selectorName) throws RepositoryException {
+ return scores[getSelectorIndex(selectorName)];
+ }
+
+ private void checkSingleSelector() throws RepositoryException {
+ if (nodes.length != 1) {
+ throw new RepositoryException("This row has more than one selector");
+ }
+ }
+
+ private int getSelectorIndex(String selector) throws RepositoryException {
+ for (int i = 0; i < selectorNames.length; i++) {
+ if (selectorNames[i].equals(selector)) {
+ return i;
+ }
+ }
+ throw new RepositoryException("Unknown selector name: " + selector);
+ }
+}
Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/SimpleRow.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/ValueComparator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/ValueComparator.java?rev=1023721&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/ValueComparator.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/ValueComparator.java Mon Oct 18 10:06:06 2010
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.commons.query;
+
+import static javax.jcr.PropertyType.DATE;
+import static javax.jcr.PropertyType.DECIMAL;
+import static javax.jcr.PropertyType.DOUBLE;
+import static javax.jcr.PropertyType.LONG;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LIKE;
+
+import java.util.Comparator;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+/**
+ * Comparator for {@link Value} instances.
+ */
+public class ValueComparator implements Comparator<Value> {
+
+ /**
+ * Compares two values.
+ */
+ public int compare(Value a, Value b) {
+ try {
+ int ta = a.getType();
+ int tb = b.getType();
+
+ if ((ta == DECIMAL || ta == DOUBLE || ta == LONG)
+ && (tb == DECIMAL || tb == DOUBLE || tb == LONG)) {
+ return a.getDecimal().compareTo(b.getDecimal());
+ } else if (ta == DATE && tb == DATE) {
+ return a.getDate().compareTo(b.getDate());
+ } else {
+ return a.getString().compareTo(b.getString());
+ }
+ } catch (RepositoryException e) {
+ throw new RuntimeException(
+ "Unable to compare values " + a + " and " + b, e);
+ }
+ }
+
+ /**
+ * Evaluates the given QOM comparison operation with the given values.
+ *
+ * @param operator QOM comparison operator
+ * @param a left value
+ * @param b right value
+ * @return result of the comparison
+ */
+ public boolean evaluate(String operator, Value a, Value b) {
+ if (JCR_OPERATOR_EQUAL_TO.equals(operator)) {
+ return compare(a, b) == 0;
+ } else if (JCR_OPERATOR_GREATER_THAN.equals(operator)) {
+ return compare(a, b) > 0;
+ } else if (JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO.equals(operator)) {
+ return compare(a, b) >= 0;
+ } else if (JCR_OPERATOR_LESS_THAN.equals(operator)) {
+ return compare(a, b) < 0;
+ } else if (JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO.equals(operator)) {
+ return compare(a, b) <= 0;
+ } else if (JCR_OPERATOR_LIKE.equals(operator)) {
+ // TODO: Implement LIKE support
+ throw new RuntimeException(
+ "LIKE comparisions are not currently supported");
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown comparison operator: " + operator);
+ }
+ }
+
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/ValueComparator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/qom/Constraints.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/qom/Constraints.java?rev=1023721&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/qom/Constraints.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/qom/Constraints.java Mon Oct 18 10:06:06 2010
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.commons.query.qom;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+
+public class Constraints {
+
+ public static final Constraint TRUE = new Constraint() {};
+
+ public static final Constraint FALSE = new Constraint() {};
+
+ public static Constraint and(
+ QueryObjectModelFactory factory, Constraint... constraints)
+ throws RepositoryException {
+ Constraint constraint = TRUE;
+ for (int i = 0; constraints != null && i < constraints.length; i++) {
+ if (constraints[i] == FALSE) {
+ return FALSE;
+ } else if (constraints[i] != TRUE) {
+ if (constraint == TRUE) {
+ constraint = constraints[i];
+ } else {
+ constraint = factory.and(constraint, constraints[i]);
+ }
+ }
+ }
+ return constraint;
+ }
+
+
+ public static Constraint or(
+ QueryObjectModelFactory factory, Constraint... constraints)
+ throws RepositoryException {
+ Constraint constraint = FALSE;
+ for (int i = 0; constraints != null && i < constraints.length; i++) {
+ if (constraints[i] == TRUE) {
+ return TRUE;
+ } else if (constraints[i] != FALSE) {
+ if (constraint == FALSE) {
+ constraint = constraints[i];
+ } else {
+ constraint = factory.or(constraint, constraints[i]);
+ }
+ }
+ }
+ return constraint;
+ }
+
+ public static Constraint not(
+ QueryObjectModelFactory factory, Constraint constraint)
+ throws RepositoryException {
+ if (constraint == TRUE) {
+ return FALSE;
+ } else if (constraint == FALSE) {
+ return TRUE;
+ } else {
+ return factory.not(constraint);
+ }
+ }
+
+}
Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/qom/Constraints.java
------------------------------------------------------------------------------
svn:eol-style = native