You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2009/10/23 12:50:40 UTC
svn commit: r828996 -
/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java
Author: mreutegg
Date: Fri Oct 23 10:50:40 2009
New Revision: 828996
URL: http://svn.apache.org/viewvc?rev=828996&view=rev
Log:
JCR-2361: Add parser callback to GQL
- added callback
- used generics where possible
- fixed a bug in order by where relative paths were mangled
Modified:
jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java
Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java?rev=828996&r1=828995&r2=828996&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java Fri Oct 23 10:50:40 2009
@@ -16,34 +16,34 @@
*/
package org.apache.jackrabbit.commons.query;
-import org.apache.jackrabbit.util.ISO9075;
-import org.apache.jackrabbit.util.Text;
-import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
-import javax.jcr.query.QueryManager;
-import javax.jcr.query.RowIterator;
-import javax.jcr.query.Row;
-import javax.jcr.query.Query;
+import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
-import javax.jcr.Session;
+import javax.jcr.RangeIterator;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
import javax.jcr.Value;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.RangeIterator;
-import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.nodetype.NodeTypeIterator;
-import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
+import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Arrays;
-import java.util.NoSuchElementException;
-import java.util.Collection;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+
+import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.util.Text;
/**
* <code>GQL</code> is a simple fulltext query language, which supports field
@@ -120,6 +120,12 @@
* {@link Row#getValue(String) Row.getValue("rep:excerpt()");}. Please note
* that this is feature is Jackrabbit specific and will not work with other
* implementations!
+ * <p/>
+ * <b>Parser callbacks</b>
+ * <p/>
+ * You can get callbacks for each field and query term pair using the method
+ * {@link #parse(String, Session, ParserCallback)}. This may be useful when you
+ * want to do some transformation on the GQL before it is actually executed.
*/
public final class GQL {
@@ -197,7 +203,7 @@
/**
* List that contains all {@link PropertyExpression}s.
*/
- private final List conditions = new ArrayList();
+ private final List<Expression> conditions = new ArrayList<Expression>();
/**
* An optional common path prefix for the GQL query.
@@ -212,17 +218,17 @@
/**
* Maps local names of node types to prefixed names.
*/
- private Map ntNames;
+ private Map<String, String[]> ntNames;
/**
* Maps local names of child node definitions to prefixed child node names.
*/
- private Map childNodeNames;
+ private Map<String, String> childNodeNames;
/**
* Maps local names of property definitions to prefixed property names.
*/
- private Map propertyNames;
+ private Map<String, String> propertyNames;
/**
* The path constraint. Defaults to: <code>//*</code>
@@ -309,6 +315,23 @@
}
/**
+ * Parses the given <code>statement</code> and generates callbacks for each
+ * GQL term parsed.
+ *
+ * @param statement the GQL statement.
+ * @param session the current session to resolve namespace prefixes.
+ * @param callback the callback handler.
+ * @throws RepositoryException if an error occurs while parsing.
+ */
+ public static void parse(String statement,
+ Session session,
+ ParserCallback callback)
+ throws RepositoryException {
+ GQL query = new GQL(statement, session, null, null);
+ query.parse(callback);
+ }
+
+ /**
* Defines a filter for query result rows.
*/
public interface Filter {
@@ -326,6 +349,26 @@
public boolean include(Row row) throws RepositoryException;
}
+ /**
+ * Defines a callback interface that may be implemented by client code to
+ * get a callback for each GQL term that is parsed.
+ */
+ public interface ParserCallback {
+
+ /**
+ * A GQL term was parsed.
+ *
+ * @param property the name of the property or an empty string if the
+ * term is not prefixed.
+ * @param value the value of the term.
+ * @param optional whether this term is prefixed with an OR operator.
+ * @throws RepositoryException if an error occurs while processing the
+ * term.
+ */
+ public void term(String property, String value, boolean optional)
+ throws RepositoryException;
+ }
+
//-----------------------------< internal >---------------------------------
/**
@@ -351,11 +394,11 @@
if (numResults == Integer.MAX_VALUE) {
return new RowIterAdapter(nodes, nodes.getSize());
}
- List resultRows = new ArrayList();
+ List<Row> resultRows = new ArrayList<Row>();
while (numResults-- > 0 && nodes.hasNext()) {
resultRows.add(nodes.nextRow());
}
- return new RowIterAdapter(resultRows, nodes.getSize());
+ return new RowIterAdapter(resultRows, resultRows.size());
} catch (RepositoryException e) {
// in case of error return empty result
return RowIteratorAdapter.EMPTY;
@@ -369,7 +412,12 @@
* @throws RepositoryException if an error occurs while translating the query.
*/
private String translateStatement() throws RepositoryException {
- parse();
+ parse(new ParserCallback() {
+ public void term(String property, String value, boolean optional)
+ throws RepositoryException {
+ pushExpression(property, value, optional);
+ }
+ });
StringBuffer stmt = new StringBuffer();
// path constraint
stmt.append(pathConstraint);
@@ -378,8 +426,8 @@
if (typeConstraints != null) {
predicate.addOperand(typeConstraints);
}
- for (Iterator it = conditions.iterator(); it.hasNext(); ) {
- predicate.addOperand((Expression) it.next());
+ for (Expression condition : conditions) {
+ predicate.addOperand(condition);
}
if (predicate.getSize() > 0) {
stmt.append("[");
@@ -408,18 +456,17 @@
String[] resolvedNames = resolveNodeTypeName(ntName);
// now resolve node type hierarchy
- for (int i = 0; i < resolvedNames.length; i++) {
+ for (String resolvedName : resolvedNames) {
try {
- NodeType base = ntMgr.getNodeType(resolvedNames[i]);
+ NodeType base = ntMgr.getNodeType(resolvedName);
if (base.isMixin()) {
// search for nodes where jcr:mixinTypes is set to this mixin
- addTypeConstraint(new MixinComparision(resolvedNames[i]));
+ addTypeConstraint(new MixinComparision(resolvedName));
} else {
// search for nodes where jcr:primaryType is set to this type
- addTypeConstraint(new PrimaryTypeComparision(resolvedNames[i]));
+ addTypeConstraint(new PrimaryTypeComparision(resolvedName));
}
-
// now search for all node types that are derived from base
NodeTypeIterator allTypes = ntMgr.getAllNodeTypes();
while (allTypes.hasNext()) {
@@ -435,7 +482,7 @@
}
} catch (NoSuchNodeTypeException e) {
// add anyway -> will not match anything
- addTypeConstraint(new PrimaryTypeComparision(resolvedNames[i]));
+ addTypeConstraint(new PrimaryTypeComparision(resolvedName));
}
}
}
@@ -469,7 +516,7 @@
} else {
if (ntNames == null) {
NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
- ntNames = new HashMap();
+ ntNames = new HashMap<String, String[]>();
NodeTypeIterator it = ntMgr.getAllNodeTypes();
while (it.hasNext()) {
String name = it.nextNodeType().getName();
@@ -478,7 +525,7 @@
if (idx != -1) {
localName = name.substring(idx + 1);
}
- String[] nts = (String[]) ntNames.get(localName);
+ String[] nts = ntNames.get(localName);
if (nts == null) {
nts = new String[]{name};
} else {
@@ -490,7 +537,7 @@
ntNames.put(localName, nts);
}
}
- names = (String[]) ntNames.get(ntName);
+ names = ntNames.get(ntName);
if (names == null) {
names = new String[]{ntName};
}
@@ -516,14 +563,14 @@
return name;
}
if (propertyNames == null) {
- propertyNames = new HashMap();
+ propertyNames = new HashMap<String, String>();
NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
NodeTypeIterator it = ntMgr.getAllNodeTypes();
while (it.hasNext()) {
NodeType nt = it.nextNodeType();
PropertyDefinition[] defs = nt.getDeclaredPropertyDefinitions();
- for (int i = 0; i < defs.length; i++) {
- String pn = defs[i].getName();
+ for (PropertyDefinition def : defs) {
+ String pn = def.getName();
if (!pn.equals("*")) {
String localName = pn;
int idx = pn.indexOf(':');
@@ -535,7 +582,7 @@
}
}
}
- String pn = (String) propertyNames.get(name);
+ String pn = propertyNames.get(name);
if (pn != null) {
return pn;
} else {
@@ -561,14 +608,14 @@
return name;
}
if (childNodeNames == null) {
- childNodeNames = new HashMap();
+ childNodeNames = new HashMap<String, String>();
NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
NodeTypeIterator it = ntMgr.getAllNodeTypes();
while (it.hasNext()) {
NodeType nt = it.nextNodeType();
NodeDefinition[] defs = nt.getDeclaredChildNodeDefinitions();
- for (int i = 0; i < defs.length; i++) {
- String cnn = defs[i].getName();
+ for (NodeDefinition def : defs) {
+ String cnn = def.getName();
if (!cnn.equals("*")) {
String localName = cnn;
int idx = cnn.indexOf(':');
@@ -580,7 +627,7 @@
}
}
}
- String cnn = (String) childNodeNames.get(name);
+ String cnn = childNodeNames.get(name);
if (cnn != null) {
return cnn;
} else {
@@ -600,10 +647,11 @@
/**
* Parses the GQL query statement.
*
+ * @param callback the parser callback.
* @throws RepositoryException if an error occurs while reading from the
* repository.
*/
- private void parse() throws RepositoryException {
+ private void parse(ParserCallback callback) throws RepositoryException {
char[] stmt = new char[statement.length() + 1];
statement.getChars(0, statement.length(), stmt, 0);
stmt[statement.length()] = ' ';
@@ -611,8 +659,7 @@
StringBuffer value = new StringBuffer();
boolean quoted = false;
boolean optional = false;
- for (int i = 0; i < stmt.length; i++) {
- char c = stmt[i];
+ for (char c : stmt) {
switch (c) {
case ' ':
if (quoted) {
@@ -624,7 +671,7 @@
if (v.equals(OR) && p.length() == 0) {
optional = true;
} else {
- pushExpression(p, v, optional);
+ callback.term(p, v, optional);
optional = false;
}
property.setLength(0);
@@ -649,7 +696,7 @@
case '"':
quoted = !quoted;
break;
- // noise
+ // noise
case '*':
case '?':
case '\'':
@@ -691,8 +738,8 @@
} else if (property.equals(TYPE)) {
String[] nts = Text.explode(value, ',');
if (nts.length > 0) {
- for (int i = 0; i < nts.length; i++) {
- collectNodeTypes(nts[i]);
+ for (String nt : nts) {
+ collectNodeTypes(nt);
}
}
} else if (property.equals(ORDER)) {
@@ -730,7 +777,7 @@
} else {
ContainsExpression expr = new ContainsExpression(property, value);
if (optional) {
- Expression last = (Expression) conditions.get(conditions.size() - 1);
+ Expression last = conditions.get(conditions.size() - 1);
if (last instanceof OptionalExpression) {
((OptionalExpression) last).addOperand(expr);
} else {
@@ -854,16 +901,19 @@
}
String slash = "";
for (int i = 0; i < parts.length; i++) {
- buffer.append(slash);
- String name;
if (i == parts.length - 1) {
- // last part
- buffer.append("@");
- name = resolvePropertyName(parts[i]);
+ if (!parts[i].equals(".")) {
+ // last part
+ buffer.append(slash);
+ buffer.append("@");
+ buffer.append(ISO9075.encode(
+ resolvePropertyName(parts[i])));
+ }
} else {
- name = resolveChildNodeName(parts[i]);
+ buffer.append(slash);
+ buffer.append(ISO9075.encode(
+ resolveChildNodeName(parts[i])));
}
- buffer.append(ISO9075.encode(name));
slash = "/";
}
}
@@ -886,7 +936,7 @@
*/
private abstract class NAryExpression implements Expression {
- private final List operands = new ArrayList();
+ private final List<Expression> operands = new ArrayList<Expression>();
public void toString(StringBuffer buffer)
throws RepositoryException {
@@ -894,9 +944,8 @@
buffer.append("(");
}
String op = "";
- for (Iterator it = operands.iterator(); it.hasNext(); ) {
+ for (Expression expr : operands) {
buffer.append(op);
- Expression expr = (Expression) it.next();
expr.toString(buffer);
op = getOperation();
}
@@ -954,11 +1003,10 @@
public void toString(StringBuffer buffer)
throws RepositoryException {
buffer.append("order by ");
- List names = new ArrayList(Arrays.asList(Text.explode(value, ',')));
+ List<String> names = new ArrayList<String>(Arrays.asList(Text.explode(value, ',')));
int length = buffer.length();
String comma = "";
- for (Iterator it = names.iterator(); it.hasNext(); ) {
- String name = (String) it.next();
+ for (String name : names) {
boolean asc;
if (name.startsWith("-")) {
name = name.substring(1);
@@ -972,8 +1020,8 @@
}
if (name.length() > 0) {
buffer.append(comma);
- name = resolvePropertyName(name);
- buffer.append("@").append(ISO9075.encode(name));
+ name = createPropertyName(resolvePropertyName(name));
+ buffer.append(name);
if (!asc) {
buffer.append(" ").append(DESCENDING);
}
@@ -986,6 +1034,25 @@
}
}
+ private String createPropertyName(String name) {
+ if (name.contains("/")) {
+ String[] labels = name.split("/");
+
+ name = "";
+ for (int i = 0; i < labels.length; i++) {
+ String label = ISO9075.encode(labels[i]);
+ if (i < (labels.length - 1)) {
+ name += label + "/";
+ } else {
+ name += "@" + label;
+ }
+ }
+ return name;
+ } else {
+ return "@" + ISO9075.encode(name);
+ }
+ }
+
private void defaultOrderBy(StringBuffer buffer) {
buffer.append("@").append(JCR_SCORE).append(" ").append(DESCENDING);
}