/*
* 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.core.query;
import static javax.jcr.query.Query.JCR_SQL2;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
import javax.jcr.query.qom.QueryObjectModelFactory;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
import org.apache.jackrabbit.core.query.lucene.SearchIndex;
import org.apache.jackrabbit.test.AbstractJCRTest;
/**
* Abstract base class for query test cases.
*/
public class AbstractQueryTest extends AbstractJCRTest {
protected QueryManager qm;
protected QueryObjectModelFactory qomFactory;
protected void setUp() throws Exception {
super.setUp();
qm = superuser.getWorkspace().getQueryManager();
qomFactory = qm.getQOMFactory();
}
protected void tearDown() throws Exception {
qm = null;
qomFactory = null;
super.tearDown();
}
/**
* Checks if the <code>result</code> contains a number of <code>hits</code>.
*
* @param result the <code>QueryResult</code>.
* @param hits the number of expected hits.
* @throws RepositoryException if an error occurs while iterating over
* the result nodes.
*/
protected void checkResult(QueryResult result, int hits)
throws RepositoryException {
int count = 0;
log.println("Rows:");
for (Row row : JcrUtils.getRows(result)) {
log.println(" " + row);
count++;
}
if (count == 0) {
log.println(" NONE");
}
assertEquals("Wrong hit count.", hits, count);
}
/**
* Checks if the <code>result</code> contains a number of <code>hits</code>
* and <code>columns</code>.
*
* @param result the <code>QueryResult</code>.
* @param hits the number of expected hits.
* @param columns the number of expected columns.
* @throws RepositoryException if an error occurs while iterating over the
* result nodes.
*/
protected void checkResult(QueryResult result, int hits, int columns)
throws RepositoryException {
checkResult(result, hits);
// now check column count
int count = 0;
log.println("Properties:");
String[] propNames = result.getColumnNames();
for (RowIterator it = result.getRows(); it.hasNext(); count++) {
StringBuffer msg = new StringBuffer();
Value[] values = it.nextRow().getValues();
for (int i = 0; i < propNames.length; i++) {
msg.append(" ").append(propNames[i]).append(": ");
if (values[i] == null) {
msg.append("null");
} else {
msg.append(values[i].getString());
}
}
log.println(msg);
}
if (count == 0) {
log.println(" NONE");
}
assertEquals("Wrong column count.", columns, count);
}
/**
* Returns the nodes in <code>it</code> as an array of Nodes.
* @param it the NodeIterator.
* @return the elements of the iterator as an array of Nodes.
*/
protected Node[] toArray(NodeIterator it) {
List<Node> nodes = new ArrayList<Node>();
while (it.hasNext()) {
nodes.add(it.nextNode());
}
return nodes.toArray(new Node[nodes.size()]);
}
/**
* Executes the <code>xpath</code> query and checks the results against
* the specified <code>nodes</code>.
* @param xpath the xpath query.
* @param nodes the expected result nodes.
* @throws RepositoryException if an error occurs while executing the query
* or checking the result.
*/
protected void executeXPathQuery(String xpath, Node[] nodes)
throws RepositoryException {
flushSearchIndex();
QueryResult res = qm.createQuery(xpath, Query.XPATH).execute();
checkResult(res, nodes);
}
/**
* Executes the <code>sql</code> query and checks the results against
* the specified <code>nodes</code>.
* @param sql the sql query.
* @param nodes the expected result nodes.
* @throws RepositoryException if an error occurs while executing the query
* or checking the result.
*/
protected void executeSQLQuery(String sql, Node[] nodes)
throws RepositoryException {
flushSearchIndex();
QueryResult res = qm.createQuery(sql, Query.SQL).execute();
checkResult(res, nodes);
}
/**
* Checks if the result set contains exactly the <code>nodes</code>.
* @param result the query result.
* @param nodes the expected nodes in the result set.
* @throws RepositoryException if an error occurs while reading from the result.
*/
protected void checkResult(QueryResult result, Node[] nodes)
throws RepositoryException {
checkResult(result.getNodes(), nodes);
}
/**
* Checks if the result contains exactly the <code>nodes</code>.
* @param result the query result.
* @param nodes the expected nodes in the result set.
* @throws RepositoryException if an error occurs while reading from the result.
*/
protected void checkResult(RowIterator result, Node[] nodes)
throws RepositoryException {
checkResult(new NodeIteratorAdapter(result) {
public Object next() throws NoSuchElementException {
Row next = (Row) super.next();
try {
return superuser.getItem(next.getValue("jcr:path").getString());
} catch (RepositoryException e) {
throw new NoSuchElementException();
}
}
}, nodes);
}
/**
* Checks if the result contains exactly the <code>nodes</code>.
* @param result the query result.
* @param nodes the expected nodes in the result set.
* @throws RepositoryException if an error occurs while reading from the result.
*/
protected void checkResult(NodeIterator result, Node[] nodes)
throws RepositoryException {
// collect paths
Set<String> expectedPaths = new HashSet<String>();
for (Node n : nodes) {
expectedPaths.add(n.getPath());
}
Set<String> resultPaths = new HashSet<String>();
while (result.hasNext()) {
resultPaths.add(result.nextNode().getPath());
}
// check if all expected are in result
for (Iterator<String> it = expectedPaths.iterator(); it.hasNext();) {
String path = it.next();
assertTrue(path + " is not part of the result set "+ resultPaths, resultPaths.contains(path));
}
// check result does not contain more than expected
for (Iterator<String> it = resultPaths.iterator(); it.hasNext();) {
String path = it.next();
assertTrue(path + " is not expected to be part of the result set " + expectedPaths, expectedPaths.contains(path));
}
}
/**
* Checks if the result set contains exactly the <code>nodes</code> in the
* given sequence.
*
* @param result the query result.
* @param nodes the expected nodes in the result set.
* @throws RepositoryException if an error occurs while reading from the result.
*/
protected void checkResultSequence(RowIterator result, Node[] nodes)
throws RepositoryException {
for (int i = 0; i < nodes.length; i++) {
assertTrue("No more results, expected " + nodes[i].getPath(), result.hasNext());
String path = result.nextRow().getValue("jcr:path").getString();
assertEquals("Wrong sequence", nodes[i].getPath(), path);
}
assertFalse("No more result expected", result.hasNext());
}
/**
* Executes the query specified by <code>statement</code> and returns the
* query result.
*
* @param statement either a SQL or XPath statement.
* @return the query result.
* @throws RepositoryException if an error occurs.
*/
protected QueryResult executeQuery(String statement)
throws RepositoryException {
flushSearchIndex();
if (statement.trim().toLowerCase().startsWith("select")) {
return qm.createQuery(statement, Query.SQL).execute();
} else {
return qm.createQuery(statement, Query.XPATH).execute();
}
}
protected QueryResult executeSQL2Query(String statement)
throws RepositoryException {
return qm.createQuery(statement, JCR_SQL2).execute();
}
protected void executeSQL2Query(String statement, Node[] nodes)
throws RepositoryException {
flushSearchIndex();
QueryResult res = qm.createQuery(statement, JCR_SQL2).execute();
checkResult(res, nodes);
}
protected void flushSearchIndex() throws RepositoryException {
SearchIndex si = getSearchIndex();
if (si != null) {
si.flush();
}
}
/**
* Returns a reference to the underlying search index.
*
* @return the query handler inside the {@link #qm query manager}.
*/
protected SearchIndex getSearchIndex() {
if (qm instanceof QueryManagerImpl) {
return (SearchIndex) ((QueryManagerImpl) qm).getQueryHandler();
}
return null;
}
}