You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2010/11/11 18:02:49 UTC
svn commit: r1033998 - in /jackrabbit/trunk:
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/
jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/...
Author: mduerig
Date: Thu Nov 11 17:02:49 2010
New Revision: 1033998
URL: http://svn.apache.org/viewvc?rev=1033998&view=rev
Log:
JCR-2800: Implement search facility for users and groups
improved conditions, added like condition
work in progress
Modified:
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java?rev=1033998&r1=1033997&r2=1033998&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java Thu Nov 11 17:02:49 2010
@@ -25,49 +25,18 @@ public interface QueryBuilder<T> {
* The sort order of the result set of a query.
*/
enum Direction {
- ASCENDING("ascending", RelationOp.GT),
- DESCENDING("descending", RelationOp.LT);
+ ASCENDING("ascending"),
+ DESCENDING("descending");
private final String direction;
- private final RelationOp collation;
- Direction(String direction, RelationOp collation) {
+ Direction(String direction) {
this.direction = direction;
- this.collation = collation;
}
public String getDirection() {
return direction;
}
-
- public RelationOp getCollation() {
- return collation;
- }
- }
-
- /**
- * Relational operators for comparing a property to a value. Correspond
- * to the general comparison operators as define in JSR-170.
- * The {@link #EX} tests for existence of a property.
- */
- enum RelationOp {
- NE("!="),
- EQ("="),
- LT("<"),
- LE("<="),
- GT(">"),
- GE("=>"),
- EX("");
-
- private final String op;
-
- RelationOp(String op) {
- this.op = op;
- }
-
- public String getOp() {
- return op;
- }
}
/**
@@ -136,19 +105,109 @@ public interface QueryBuilder<T> {
/**
* Create a condition which holds iff the node of an {@link Authorizable} has a
- * property at <code>relPath</code> which relates to <code>value</code> through
- * <code>op</<code>. The format of the <code>relPath</code> argument is the same
- * as in XPath: <code>@attributeName</code> for an attribute on this node and
+ * property at <code>relPath</code> which is not equal to <code>value</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param value Value to compare the property at <code>relPath</code> to
+ * @return A condition
+ */
+ T neq(String relPath, Value value);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which is equal to <code>value</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param value Value to compare the property at <code>relPath</code> to
+ * @return A condition
+ */
+ T eq(String relPath, Value value);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which is smaller than <code>value</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
* <code>relative/path/@attributeName</code> for an attribute of a descendant node.
- * {@link RelationOp#EX} tests for existence of a property. In this case the
- * <code>value</code> argument is ignored.
*
* @param relPath Relative path from the authorizable's node to the property
- * @param op Comparison operator
* @param value Value to compare the property at <code>relPath</code> to
* @return A condition
*/
- T property(String relPath, RelationOp op, Value value);
+ T lt(String relPath, Value value);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which is smaller than or equal to <code>value</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param value Value to compare the property at <code>relPath</code> to
+ * @return A condition
+ */
+ T le(String relPath, Value value);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which is greater than <code>value</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param value Value to compare the property at <code>relPath</code> to
+ * @return A condition
+ */
+ T gt(String relPath, Value value);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which is greater than or equal to <code>value</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param value Value to compare the property at <code>relPath</code> to
+ * @return A condition
+ */
+ T ge(String relPath, Value value);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code>.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @return A condition
+ */
+ T exists(String relPath);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which matches the pattern in <code>pattern</code>.
+ * The percent character Ô%Õ represents any string of zero or more characters and the
+ * underscore character Ô_Õ represents any single character. Any literal use of these characters
+ * and the backslash character Ô\Õ must be escaped with a backslash character.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param pattern Pattern to match the property at <code>relPath</code> against
+ * @return A condition
+ */
+ T like(String relPath, String pattern);
/**
* Create a full text search condition. The condition holds iff the node of an
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java?rev=1033998&r1=1033997&r2=1033998&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java Thu Nov 11 17:02:49 2010
@@ -28,6 +28,32 @@ import java.util.List;
public class XPathQueryBuilder implements QueryBuilder<XPathQueryBuilder.Condition> {
+ /**
+ * Relational operators for comparing a property to a value. Correspond
+ * to the general comparison operators as define in JSR-170.
+ * The {@link #EX} tests for existence of a property.
+ */
+ enum RelationOp {
+ NE("!="),
+ EQ("="),
+ LT("<"),
+ LE("<="),
+ GT(">"),
+ GE("=>"),
+ EX(""),
+ LIKE("like");
+
+ private final String op;
+
+ RelationOp(String op) {
+ this.op = op;
+ }
+
+ public String getOp() {
+ return op;
+ }
+ }
+
interface Condition {
void accept(ConditionVisitor visitor) throws RepositoryException;
}
@@ -123,6 +149,38 @@ public class XPathQueryBuilder implement
return new PropertyCondition(relPath, op, value);
}
+ public Condition neq(String relPath, Value value) {
+ return new PropertyCondition(relPath, RelationOp.NE, value);
+ }
+
+ public Condition eq(String relPath, Value value) {
+ return new PropertyCondition(relPath, RelationOp.EQ, value);
+ }
+
+ public Condition lt(String relPath, Value value) {
+ return new PropertyCondition(relPath, RelationOp.LT, value);
+ }
+
+ public Condition le(String relPath, Value value){
+ return new PropertyCondition(relPath, RelationOp.LE, value);
+ }
+
+ public Condition gt(String relPath, Value value) {
+ return new PropertyCondition(relPath, RelationOp.GT, value);
+ }
+
+ public Condition ge(String relPath, Value value) {
+ return new PropertyCondition(relPath, RelationOp.GE, value);
+ }
+
+ public Condition exists(String relPath) {
+ return new PropertyCondition(relPath, RelationOp.EX);
+ }
+
+ public Condition like(String relPath, String pattern) {
+ return new PropertyCondition(relPath, RelationOp.LIKE, pattern);
+ }
+
public Condition contains(String relPath, String searchExpr) {
return new ContainsCondition(relPath, searchExpr);
}
@@ -149,11 +207,27 @@ public class XPathQueryBuilder implement
private final String relPath;
private final RelationOp op;
private final Value value;
+ private final String pattern;
public PropertyCondition(String relPath, RelationOp op, Value value) {
this.relPath = relPath;
this.op = op;
this.value = value;
+ pattern = null;
+ }
+
+ public PropertyCondition(String relPath, RelationOp op, String pattern) {
+ this.relPath = relPath;
+ this.op = op;
+ value = null;
+ this.pattern = pattern;
+ }
+
+ public PropertyCondition(String relPath, RelationOp op) {
+ this.relPath = relPath;
+ this.op = op;
+ value = null;
+ pattern = null;
}
public String getRelPath() {
@@ -168,6 +242,10 @@ public class XPathQueryBuilder implement
return value;
}
+ public String getPattern() {
+ return pattern;
+ }
+
public void accept(ConditionVisitor visitor) throws RepositoryException {
visitor.visit(this);
}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java?rev=1033998&r1=1033997&r2=1033998&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java Thu Nov 11 17:02:49 2010
@@ -20,11 +20,11 @@ package org.apache.jackrabbit.core.secur
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
-import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.security.user.XPathQueryBuilder.Condition;
+import org.apache.jackrabbit.core.security.user.XPathQueryBuilder.RelationOp;
import org.apache.jackrabbit.spi.commons.iterator.Iterators;
import org.apache.jackrabbit.spi.commons.iterator.Predicate;
import org.apache.jackrabbit.spi.commons.iterator.Predicates;
@@ -78,7 +78,7 @@ public class XPathQueryEvaluator impleme
log.warn("Ignoring bound {} since no sort order is specified");
}
else {
- Condition boundCondition = builder.property(sortCol, sortDir.getCollation(), bound);
+ Condition boundCondition = builder.property(sortCol, getCollation(sortDir), bound);
condition = condition == null
? boundCondition
: builder.and(condition, boundCondition);
@@ -123,6 +123,13 @@ public class XPathQueryEvaluator impleme
if (relOp == RelationOp.EX) {
xPath.append(condition.getRelPath());
}
+ else if (relOp == RelationOp.LIKE) {
+ xPath.append("jcr:like(")
+ .append(condition.getRelPath())
+ .append(",'")
+ .append(condition.getPattern())
+ .append("')");
+ }
else {
xPath.append(condition.getRelPath())
.append(condition.getOp().getOp())
@@ -207,6 +214,19 @@ public class XPathQueryEvaluator impleme
}
}
+ private static RelationOp getCollation(Direction direction) throws RepositoryException {
+ switch (direction) {
+ case ASCENDING:
+ return RelationOp.GT;
+
+ case DESCENDING:
+ return RelationOp.LT;
+
+ default:
+ throw new RepositoryException("Unknown sort order " + direction);
+ }
+ }
+
@SuppressWarnings("unchecked")
private static Iterator<Node> execute(Query query) throws RepositoryException {
return query.execute().getNodes();
Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java?rev=1033998&r1=1033997&r2=1033998&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java Thu Nov 11 17:02:49 2010
@@ -18,7 +18,6 @@
package org.apache.jackrabbit.api.security.user;
import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
-import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
import org.apache.jackrabbit.spi.commons.iterator.Iterators;
import org.apache.jackrabbit.spi.commons.iterator.Predicate;
@@ -296,7 +295,7 @@ public class UserManagerSearchTest exten
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
- property("@canFly", RelationOp.EQ, vf.createValue(true)));
+ eq("@canFly", vf.createValue(true)));
}
});
@@ -320,7 +319,7 @@ public class UserManagerSearchTest exten
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
- property("profile/@weight", RelationOp.GT, vf.createValue(2000.0)));
+ gt("profile/@weight", vf.createValue(2000.0)));
}
});
@@ -344,7 +343,7 @@ public class UserManagerSearchTest exten
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
- property("@numberOfLegs", RelationOp.EQ, vf.createValue(8)));
+ eq("@numberOfLegs", vf.createValue(8)));
}
});
@@ -368,7 +367,7 @@ public class UserManagerSearchTest exten
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
- property("@poisonous", RelationOp.EX, null));
+ exists("@poisonous"));
}
});
@@ -388,6 +387,36 @@ public class UserManagerSearchTest exten
assertSameElements(result, expected);
}
+ public void testPropertyLike() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ like("profile/@food", "m%"));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] food = user.getProperty("profile/food");
+ if (food == null || food.length != 1) {
+ return false;
+ }
+ else {
+ String value = food[0].getString();
+ return value.length() > 0 && value.charAt(0) == 'm';
+ }
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
public void testContains1() throws RepositoryException {
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
@@ -445,9 +474,9 @@ public class UserManagerSearchTest exten
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
and(builder.
- property("profile/@cute", RelationOp.EQ, vf.createValue(true)), builder.
+ eq("profile/@cute", vf.createValue(true)), builder.
not(builder.
- property("@color", RelationOp.EQ, vf.createValue("black")))));
+ eq("@color", vf.createValue("black")))));
}
});
@@ -474,8 +503,8 @@ public class UserManagerSearchTest exten
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
or(builder.
- property("profile/@food", RelationOp.EQ, vf.createValue("mice")), builder.
- property("profile/@food", RelationOp.EQ, vf.createValue("nectar"))));
+ eq("profile/@food", vf.createValue("mice")), builder.
+ eq("profile/@food", vf.createValue("nectar"))));
}
});
@@ -512,8 +541,8 @@ public class UserManagerSearchTest exten
public void testSortOrder1() throws RepositoryException {
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
- builder.setCondition(
- builder.property("@color", RelationOp.EX, null));
+ builder.setCondition(builder.
+ exists("@color"));
builder.setSortOrder("@color", Direction.DESCENDING);
}
});
@@ -533,8 +562,8 @@ public class UserManagerSearchTest exten
public void testSortOrder2() throws RepositoryException {
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
- builder.setCondition(
- builder.property("profile/@weight", RelationOp.EX, null));
+ builder.setCondition(builder.
+ exists("profile/@weight"));
builder.setSortOrder("profile/@weight", Direction.ASCENDING);
}
});
@@ -604,7 +633,7 @@ public class UserManagerSearchTest exten
Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
public <T> void build(QueryBuilder<T> builder) {
builder.setCondition(builder.
- property("profile/@cute", RelationOp.EQ, vf.createValue(true)));
+ eq("profile/@cute", vf.createValue(true)));
builder.setSortOrder("profile/@weight", Direction.ASCENDING);
builder.setLimit(vf.createValue(1000.0), count);
}