/* * 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.exoplatform.services.jcr.api.core.query; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.query.InvalidQueryException; /** * <code>UpperLowerCaseQueryTest</code> tests the functions fn:lower-case() and * fn:upper-case() in XPath, LOWER() and UPPER() in SQL and UpperCase and * LowerCase in JQOM. */ public class UpperLowerCaseQueryTest extends AbstractQueryTest { // /** // * Maps operator strings to QueryObjectModelConstants. // */ // private static final Map OPERATORS = new HashMap(); // // static { // OPERATORS.put("=", new Integer(OPERATOR_EQUAL_TO)); // OPERATORS.put(">", new Integer(OPERATOR_GREATER_THAN)); // OPERATORS.put(">=", new Integer(OPERATOR_GREATER_THAN_OR_EQUAL_TO)); // OPERATORS.put("<", new Integer(OPERATOR_LESS_THAN)); // OPERATORS.put("<=", new Integer(OPERATOR_LESS_THAN_OR_EQUAL_TO)); // OPERATORS.put("like", new Integer(OPERATOR_LIKE)); // OPERATORS.put("!=", new Integer(OPERATOR_NOT_EQUAL_TO)); // } public void testEqualsGeneralComparison() throws RepositoryException { check(new String[]{"foo", "Foo", "fOO", "FOO", "fooBar", "fo", "fooo"}, "=", "foo", new boolean[]{true, true, true, true, false, false, false}); check(new String[]{"foo"}, "=", "", new boolean[]{false}); check(new String[]{""}, "=", "", new boolean[]{true}); } public void testGreaterThanGeneralComparison() throws RepositoryException { // check edges check(new String[]{"foo", "FOO", "FoO", "fOo", "FON", "fon", "fo", "FO"}, ">", "foo", new boolean[]{false, false, false, false, false, false, false, false}); check(new String[]{"foo ", "FOOa", "FoOO", "fOo1", "FOp", "foP", "fp", "g", "G"}, ">", "foo", new boolean[]{true, true, true, true, true, true, true, true, true}); // check combinations check(new String[]{"foo", "fooo", "FooO", "fo", "FON", "fon"}, ">", "foo", new boolean[]{false, true, true, false, false, false}); } public void testLessThanGeneralComparison() throws RepositoryException { // check edges check(new String[]{"foo", "FOO", "FoO", "fOo", "foOo", "foo ", "fooa", "fop"}, "<", "foo", new boolean[]{false, false, false, false, false, false, false, false}); check(new String[]{"fo", "FOn", "FoN", "fO", "FO1", "fn", "fN", "E", "e"}, "<", "foo", new boolean[]{true, true, true, true, true, true, true, true, true}); // check combinations check(new String[]{"foo", "fooo", "FooO", "fo", "FON", "fon"}, "<", "foo", new boolean[]{false, false, false, true, true, true}); } public void testGreaterEqualsGeneralComparison() throws RepositoryException { // check edges check(new String[]{"fo", "FO", "Fon", "fONo", "FON", "fO", "fo", "FO"}, ">=", "foo", new boolean[]{false, false, false, false, false, false, false, false}); check(new String[]{"foo", "FoO", "FoOO", "fOo1", "FOp", "foP", "fp", "g", "G"}, ">=", "foo", new boolean[]{true, true, true, true, true, true, true, true, true}); // check combinations check(new String[]{"foo", "fooo", "FOo", "fo", "FON", "fon"}, ">=", "foo", new boolean[]{true, true, true, false, false, false}); } public void testLessEqualsGeneralComparison() throws RepositoryException { // check edges check(new String[]{"fooo", "FOoo", "Fop", "fOpo", "FOP", "fOo ", "fp", "G"}, "<=", "foo", new boolean[]{false, false, false, false, false, false, false, false}); check(new String[]{"foo", "FoO", "Foo", "fOn", "FO", "fo", "f", "E", "e"}, "<=", "foo", new boolean[]{true, true, true, true, true, true, true, true, true}); // check combinations check(new String[]{"foo", "fo", "FOo", "fop", "FOP", "fooo"}, "<=", "foo", new boolean[]{true, true, true, false, false, false}); } public void testNotEqualsGeneralComparison() throws RepositoryException { // check edges check(new String[]{"fooo", "FOoo", "Fop", "fOpo", "FOP", "fOo ", "fp", "G", ""}, "!=", "foo", new boolean[]{true, true, true, true, true, true, true, true, true}); check(new String[]{"foo", "FoO", "Foo", "foO", "FOO"}, "!=", "foo", new boolean[]{false, false, false, false, false}); // check combinations check(new String[]{"foo", "fo", "FOo", "fop", "FOP", "fooo"}, "!=", "foo", new boolean[]{false, true, false, true, true, true}); } public void testLikeComparison() throws RepositoryException { check(new String[]{"foo", "Foo", "fOO", "FO "}, "like", "fo_", new boolean[]{true, true, true, true}); check(new String[]{"foo", "Foo", "fOO", "FOO"}, "like", "f_o", new boolean[]{true, true, true, true}); check(new String[]{"foo", "Foo", "fOO", " OO"}, "like", "_oo", new boolean[]{true, true, true, true}); check(new String[]{"foo", "Foa", "fOO", "FO", "foRm", "fPo", "fno", "FPo", "Fno"}, "like", "fo%", new boolean[]{ true, true, true, true, true, false, false, false, false}); } public void testLikeComparisonRandom() throws RepositoryException { String abcd = "abcd"; Random random = new Random(); for (int i = 0; i < 50; i++) { String pattern = ""; pattern += getRandomChar(abcd, random); pattern += getRandomChar(abcd, random); // create 10 random values with 4 characters String[] values = new String[10]; boolean[] matches = new boolean[10]; for (int n = 0; n < 10; n++) { // at least the first character always matches String value = String.valueOf(pattern.charAt(0)); for (int r = 1; r < 4; r++) { char c = getRandomChar(abcd, random); if (random.nextBoolean()) { c = Character.toUpperCase(c); } value += c; } matches[n] = value.toLowerCase().startsWith(pattern); values[n] = value; } pattern += "%"; check(values, "like", pattern, matches); } } public void testRangeWithEmptyString() throws RepositoryException { check(new String[]{" ", "a", "A", "1", "3", "!", "@"}, ">", "", new boolean[]{true, true, true, true, true, true, true}); check(new String[]{"", "a", "A", "1", "3", "!", "@"}, ">=", "", new boolean[]{true, true, true, true, true, true, true}); check(new String[]{"", "a", "A", "1", "3", "!", "@"}, "<", "", new boolean[]{false, false, false, false, false, false, false}); check(new String[]{"", "a", "A", "1", "3", "!", "@"}, "<=", "", new boolean[]{true, false, false, false, false, false, false}); } public void testInvalidQuery() throws RepositoryException { try { executeXPathQuery("//*[fn:lower-case(@foo) = 123]", new Node[]{}); fail("must throw InvalidQueryException"); } catch (InvalidQueryException e) { // correct } try { executeSQLQuery("select * from nt:base where LOWER(foo) = 123", new Node[]{}); fail("must throw InvalidQueryException"); } catch (InvalidQueryException e) { // correct } } public void testWrongCaseNeverMatches() throws RepositoryException { Node n = testRootNode.addNode("node"); n.setProperty("foo", "Bar"); testRootNode.save(); executeXPathQuery(testPath + "/*[jcr:like(fn:lower-case(@foo), 'BA%')]", new Node[]{}); } //----------------------------< internal >---------------------------------- private void check(String[] values, String operation, String queryTerm, boolean[] matches) throws RepositoryException { if (values.length != matches.length) { throw new IllegalArgumentException("values and matches must have same length"); } // create log message StringBuffer logMsg = new StringBuffer(); logMsg.append("queryTerm: ").append(queryTerm); logMsg.append(" values: "); String separator = ""; for (int i = 0; i < values.length; i++) { logMsg.append(separator); separator = ", "; if (matches[i]) { logMsg.append("+"); } else { logMsg.append("-"); } logMsg.append(values[i]); } log.println(logMsg.toString()); for (NodeIterator it = testRootNode.getNodes(); it.hasNext();) { it.nextNode().remove(); } List matchingNodes = new ArrayList(); for (int i = 0; i < values.length; i++) { Node n = testRootNode.addNode("node" + i); n.setProperty(propertyName1, values[i]); if (matches[i]) { matchingNodes.add(n); } } testRootNode.save(); Node[] nodes = (Node[])matchingNodes.toArray(new Node[matchingNodes.size()]); String sqlOperation = operation; if (operation.equals("!=")) { sqlOperation = "<>"; } // run queries with lower-case String xpath = testPath; if (operation.equals("like")) { xpath += "/*[jcr:like(fn:lower-case(@" + propertyName1 + "), '" + queryTerm.toLowerCase() + "')]"; } else { xpath += "/*[fn:lower-case(@" + propertyName1 + ") " + operation + " '" + queryTerm.toLowerCase() + "']"; } executeXPathQuery(xpath, nodes); String sql = "select * from nt:base where jcr:path like '" + testRoot + "/%' and LOWER(" + propertyName1 + ") " + sqlOperation + " '" + queryTerm.toLowerCase() + "'"; executeSQLQuery(sql, nodes); // run queries with upper-case xpath = testPath; if (operation.equals("like")) { xpath += "/*[jcr:like(fn:upper-case(@" + propertyName1 + "), '" + queryTerm.toUpperCase() + "')]"; } else { xpath += "/*[fn:upper-case(@" + propertyName1 + ") " + operation + " '" + queryTerm.toUpperCase() + "']"; } executeXPathQuery(xpath, nodes); sql = "select * from nt:base where jcr:path like '" + testRoot + "/%' and UPPER(" + propertyName1 + ") " + sqlOperation + " '" + queryTerm.toUpperCase() + "'"; executeSQLQuery(sql, nodes); } private char getRandomChar(String pool, Random random) { return pool.charAt(random.nextInt(pool.length())); } // protected static int getOperatorForString(String operator) { // Integer i = (Integer) OPERATORS.get(operator); // if (i == null) { // throw new IllegalArgumentException("unknown operator: " + operator); // } // return i.intValue(); // } }