]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-4214: Support for field level searches across most services. Includes support...
authorRichard Millet <richard.millet@berkeley.edu>
Tue, 26 Jul 2011 17:29:52 +0000 (17:29 +0000)
committerRichard Millet <richard.millet@berkeley.edu>
Tue, 26 Jul 2011 17:29:52 +0000 (17:29 +0000)
services/client/src/main/java/org/collectionspace/services/client/AbstractPoxServiceClientImpl.java
services/client/src/main/java/org/collectionspace/services/client/CollectionSpaceCommonListPoxProxy.java
services/client/src/main/java/org/collectionspace/services/client/CollectionSpacePoxClient.java
services/client/src/main/java/org/collectionspace/services/client/CollectionSpacePoxProxy.java
services/client/src/main/java/org/collectionspace/services/client/IQueryManager.java
services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTestImpl.java
services/collectionobject/client/src/test/java/org/collectionspace/services/client/test/CollectionObjectSearchTest.java
services/common/src/main/java/org/collectionspace/services/common/ResourceBase.java
services/common/src/main/java/org/collectionspace/services/common/query/QueryManager.java
services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java

index ea0d973638d2778e1f97b5f23471d55636eab096..70532093bb7bd6836db656b086c546caf7fe3c6e 100644 (file)
@@ -45,4 +45,10 @@ public abstract class AbstractPoxServiceClientImpl<LT extends AbstractCommonList
         return proxy.keywordSearchIncludeDeleted(keywords, includeDeleted.toString());\r
     }\r
 \r
+    @Override\r
+    public ClientResponse<LT> advancedSearchIncludeDeleted(String whereClause, Boolean includeDeleted) {\r
+        CollectionSpacePoxProxy<LT> proxy = getProxy();\r
+        return proxy.advancedSearchIncludeDeleted(whereClause, includeDeleted.toString());\r
+    }\r
+\r
 }\r
index 8c3f46bed499edb6fbca5c6e5564c91d1cbdaa9d..6ca1698449828ec64a995932ae032ffa69b9c34c 100644 (file)
@@ -25,4 +25,10 @@ public interface CollectionSpaceCommonListPoxProxy extends CollectionSpacePoxPro
            @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords,\r
             @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
 \r
+    @Override\r
+       @GET\r
+       @Produces({ "application/xml" })\r
+       ClientResponse<AbstractCommonList> advancedSearchIncludeDeleted(\r
+                       @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_AS) String whereClause,\r
+                       @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
 }\r
index 1ca54dfe19858443394c448851a14180d7660eae..fe3b587edc32974d5dd9be24724d9993c01a501a 100644 (file)
@@ -31,4 +31,6 @@ public interface CollectionSpacePoxClient<LT extends AbstractCommonList, P exten
     public ClientResponse<LT> readIncludeDeleted(Boolean includeDeleted);\r
 \r
     public ClientResponse<LT> keywordSearchIncludeDeleted(String keywords, Boolean includeDeleted);\r
+    \r
+    public ClientResponse<LT> advancedSearchIncludeDeleted(String whereClause, Boolean includeDeleted);\r
 }\r
index 9de3e19a3e24066b27206d22333a236292cd30a9..fa71d50f1656192dba525e27242a9cf786cf671e 100644 (file)
@@ -13,47 +13,54 @@ import org.collectionspace.services.client.workflow.WorkflowClient;
 import org.collectionspace.services.jaxb.AbstractCommonList;\r
 import org.jboss.resteasy.client.ClientResponse;\r
 \r
-public interface CollectionSpacePoxProxy<LT extends AbstractCommonList> extends CollectionSpaceProxy<LT> {\r
-\r
-    //(C)reate\r
-    @POST\r
-    ClientResponse<Response> create(byte[] payload);\r
-\r
-    //(R)ead\r
-    @GET\r
-    @Path("/{csid}")\r
-    ClientResponse<String> read(@PathParam("csid") String csid);\r
-\r
-    //(R)ead\r
-    @GET\r
-    @Path("/{csid}")\r
-    ClientResponse<String> readIncludeDeleted(@PathParam("csid") String csid,\r
-               @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
-\r
-    //(U)pdate\r
-    @PUT\r
-    @Path("/{csid}")\r
-    ClientResponse<String> update(@PathParam("csid") String csid, byte[] payload);\r
-    \r
-    //(L)ist non-deleted items\r
-    @GET\r
-    @Produces({"application/xml"})\r
-    ClientResponse<LT> readIncludeDeleted(\r
-            @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
-\r
-\r
-    /**\r
-     * Keyword search.\r
-     *\r
-     * @param keywords keywords on which to search\r
-     * @param includeDeleted\r
-     * @return the client response\r
-     */\r
-    @GET\r
-    @Produces({"application/xml"})\r
-    ClientResponse<LT> keywordSearchIncludeDeleted(\r
-               @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords,\r
-                @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
+public interface CollectionSpacePoxProxy<LT extends AbstractCommonList> extends\r
+               CollectionSpaceProxy<LT> {\r
 \r
+       // (C)reate\r
+       @POST\r
+       ClientResponse<Response> create(byte[] payload);\r
+\r
+       // (R)ead\r
+       @GET\r
+       @Path("/{csid}")\r
+       ClientResponse<String> read(@PathParam("csid") String csid);\r
+\r
+       // (R)ead\r
+       @GET\r
+       @Path("/{csid}")\r
+       ClientResponse<String> readIncludeDeleted(\r
+                       @PathParam("csid") String csid,\r
+                       @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
+\r
+       // (U)pdate\r
+       @PUT\r
+       @Path("/{csid}")\r
+       ClientResponse<String> update(@PathParam("csid") String csid, byte[] payload);\r
+\r
+       // (L)ist non-deleted items\r
+       @GET\r
+       @Produces({ "application/xml" })\r
+       ClientResponse<LT> readIncludeDeleted(\r
+                       @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
+\r
+       /**\r
+        * Keyword search.\r
+        * \r
+        * @param keywords\r
+        *            keywords on which to search\r
+        * @param includeDeleted\r
+        * @return the client response\r
+        */\r
+       @GET\r
+       @Produces({ "application/xml" })\r
+       ClientResponse<LT> keywordSearchIncludeDeleted(\r
+                       @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords,\r
+                       @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
+\r
+       @GET\r
+       @Produces({ "application/xml" })\r
+       ClientResponse<LT> advancedSearchIncludeDeleted(\r
+                       @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_AS) String whereClause,\r
+                       @QueryParam(WorkflowClient.WORKFLOW_QUERY_NONDELETED) String includeDeleted);\r
 \r
 }\r
index a117cbe5e9193a908d8650f8015292d483eb45a0..493d5fa3af58f0c6ea68acc27f4a3aa93c397819 100644 (file)
@@ -35,6 +35,7 @@ public interface IQueryManager {
        final static String SEARCH_ILIKE = " ILIKE ";\r
     final static String SEARCH_TYPE_KEYWORDS = "keywords";\r
     final static String SEARCH_TYPE_KEYWORDS_KW = "kw";\r
+    final static String SEARCH_TYPE_KEYWORDS_AS = "as";\r
     final static String SEARCH_TYPE_PARTIALTERM = "pt";\r
     final static String SEARCH_TYPE_DOCTYPE = "doctype";\r
     final static String SEARCH_TYPE_INVCOATION_MODE = "mode";\r
@@ -52,6 +53,8 @@ public interface IQueryManager {
         * @return the string\r
         */\r
        public String createWhereClauseFromKeywords(String keywords);\r
+       \r
+       public String createWhereClauseFromAdvancedSearch(String advancedSearch);\r
 \r
        /**\r
         * Creates the where clause for partial term match.\r
index 4363ffaa8e34f0e1be48ea1ba3d958cf0d414659..b342f7f1efa9de1039a4a35628072535cabc34a9 100644 (file)
@@ -803,7 +803,7 @@ public abstract class AbstractServiceTestImpl extends BaseServiceTest implements
     /*
      * Test that searches honor the workflow deleted state.
      */
-    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
+//    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
     public void searchWorkflowDeleted(String testName) throws Exception {
 
         // FIXME: Temporarily avoid running test if client is of an authority service
index 5fbd377bcfc7657ce5a2ef52de21f2a86581afde..5afc85d2ec9c463031e72979cffb1c85141bb699 100644 (file)
@@ -27,6 +27,8 @@ import java.util.ArrayList;
 import java.util.List;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+
+import org.collectionspace.services.CollectionObjectJAXBSchema;
 import org.collectionspace.services.client.CollectionObjectClient;
 import org.collectionspace.services.client.CollectionSpaceClient;
 import org.collectionspace.services.client.PayloadOutputPart;
@@ -34,6 +36,7 @@ import org.collectionspace.services.client.PoxPayloadOut;
 import org.collectionspace.services.collectionobject.CollectionobjectsCommon;
 import org.collectionspace.services.common.AbstractCommonListUtils;
 import org.collectionspace.services.jaxb.AbstractCommonList;
+
 import org.jboss.resteasy.client.ClientResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,504 +46,587 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 /**
- * CollectionObjectSearchTest, carries out tests of keyword
- * search functionality against a deployed and running
- * CollectionObject Service.
- *
- * $LastChangedRevision: 1327 $
- * $LastChangedDate: 2010-02-12 10:35:11 -0800 (Fri, 12 Feb 2010) $
+ * CollectionObjectSearchTest, carries out tests of keyword search functionality
+ * against a deployed and running CollectionObject Service.
+ * 
+ * $LastChangedRevision: 1327 $ $LastChangedDate: 2010-02-12 10:35:11 -0800
+ * (Fri, 12 Feb 2010) $
  */
 public class CollectionObjectSearchTest extends BaseServiceTest {
 
-    /** The logger. */
-    private final String CLASS_NAME = CollectionObjectSearchTest.class.getName();
-    private final Logger logger = LoggerFactory.getLogger(CLASS_NAME);
-    final static String IDENTIFIER = getSystemTimeIdentifier();
-    final static String KEYWORD_SEPARATOR = " ";
-    final long numNoiseWordResources = 10;
-    final double pctNonNoiseWordResources = 0.5;
-    // Use this to keep track of resources to delete
-    private List<String> allResourceIdsCreated = new ArrayList<String>();
-    
-    // Constants for data used in search testing
-    
-    // Test keywords unlikely to be encountered in actual collections data,
-    // consisting of the names of mythical creatures in a 1970s role-playing
-    // game, which result in very few 'hits' in Google searches.
-    final static String KEYWORD = "Tsolyani" + IDENTIFIER;
-    final static List<String> TWO_KEYWORDS =
-            Arrays.asList(new String[]{"Cheggarra" + IDENTIFIER, "Ahoggya" + IDENTIFIER});
-    final static List<String> TWO_MORE_KEYWORDS =
-            Arrays.asList(new String[]{"Karihaya" + IDENTIFIER, "Hlikku" + IDENTIFIER});
-    final static String NOISE_WORD = "Mihalli + IDENTIFIER";
-    // Test Unicode UTF-8 term for keyword searching: a random sequence,
-    // unlikely to be encountered in actual collections data, of two USASCII
-    // characters followed by four non-USASCII range Unicode UTF-8 characters:
-    //
-    // Δ : Greek capital letter Delta (U+0394)
-    // Ж : Cyrillic capital letter Zhe with breve (U+04C1)
-    // Ŵ : Latin capital letter W with circumflex (U+0174)
-    // Ω : Greek capital letter Omega (U+03A9)
-    final String UTF8_KEYWORD = "to" + '\u0394' + '\u04C1' + '\u0174' + '\u03A9';
-    // Non-existent term unlikely to be encountered in actual collections
-    // data, consisting of two back-to-back sets of the first letters of
-    // each of the words in a short pangram for the English alphabet.
-     final static String NON_EXISTENT_KEYWORD = "jlmbsoqjlmbsoq";
-   
-    @Override
-    protected String getServiceName() {
-        throw new UnsupportedOperationException(); //FIXME: REM - See http://issues.collectionspace.org/browse/CSPACE-3498
-    }
-
-    @Override
-    protected String getServicePathComponent() {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException(); //FIXME: REM - See http://issues.collectionspace.org/browse/CSPACE-3498
-    }
-
-//    /* (non-Javadoc)
-//     * @see org.collectionspace.services.client.test.BaseServiceTest#getServicePathComponent()
-//     */
-//    @Override
-//    protected String getServicePathComponent() {
-//        return new CollectionObjectClient().getServicePathComponent(); //FIXME: REM = Remove all refs to this method.
-//    }
-
-    /* (non-Javadoc)
-     * @see org.collectionspace.services.client.test.BaseServiceTest#getClientInstance()
-     */
-    @Override
-    protected CollectionSpaceClient getClientInstance() {
-        return new CollectionObjectClient();
-    }
-
-    /* (non-Javadoc)
-     * @see org.collectionspace.services.client.test.BaseServiceTest#getAbstractCommonList(org.jboss.resteasy.client.ClientResponse)
-     */
-    @Override
-    protected AbstractCommonList getAbstractCommonList(ClientResponse<AbstractCommonList> response) {
-        return response.getEntity(AbstractCommonList.class);
-    }
-
-    /**
-     * Creates one or more resources containing a "noise" keyword,
-     * which should NOT be retrieved by keyword searches.
-     *
-     * This also helps ensure that searches will not fail, due
-     * to a database-specific constraint or otherwise, if the
-     * number of records containing a particular keyword represent
-     * too high a proportion of the total number of records.
-     */
-    @BeforeClass(alwaysRun = true)
-    public void setup() {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Creating " + numNoiseWordResources
-                    + " 'noise word' resources ...");
-        }
-        createCollectionObjects(numNoiseWordResources, NOISE_WORD);
-    }
-
-    // ---------------------------------------------------------------
-    // Search tests
-    // ---------------------------------------------------------------
-    // Success outcomes
-    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
-    groups = {"oneKeyword"})
-    public void searchWithOneKeyword(String testName) throws Exception {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(testBanner(testName, CLASS_NAME));
-        }
-
-        // Create one or more keyword retrievable resources, each containing
-        // a specified keyword.
-        long numKeywordRetrievableResources =
-                (long) (numNoiseWordResources * pctNonNoiseWordResources);
-        if (logger.isDebugEnabled()) {
-            logger.debug("Creating " + numKeywordRetrievableResources
-                    + " keyword-retrievable resources ...");
-        }
-        createCollectionObjects(numKeywordRetrievableResources, KEYWORD);
-
-        // Set the expected status code and group of valid status codes
-        testSetup(STATUS_OK, ServiceRequestType.SEARCH);
-
-        // Send the search request and receive a response
-        ClientResponse<AbstractCommonList> res = doSearch(KEYWORD);
-        int statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
-        long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-    }
-
-    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
-    public void searchWithTwoKeywordsInSameField(String testName) throws Exception {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(testBanner(testName, CLASS_NAME));
-        }
-
-        // Create one or more keyword retrievable resources, each containing
-        // two specified keywords.
-        long numKeywordRetrievableResources =
-                (long) (numNoiseWordResources * pctNonNoiseWordResources);
-        if (logger.isDebugEnabled()) {
-            logger.debug("Creating " + numKeywordRetrievableResources
-                    + " keyword-retrievable resources ...");
-        }
-        boolean keywordsInSameField = true;
-        createCollectionObjects(numKeywordRetrievableResources, TWO_KEYWORDS, keywordsInSameField);
-
-        // Set the expected status code and group of valid status codes
-        testSetup(STATUS_OK, ServiceRequestType.SEARCH);
-
-        // Search using both terms
-
-        // Send the search request and receive a response
-        ClientResponse<AbstractCommonList> res = doSearch(TWO_KEYWORDS);
-        int statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
-        long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-
-        // Search using a single term
-
-        // Send the search request and receive a response
-        res = doSearch(TWO_KEYWORDS.get(0));
-        statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
-        numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-
-    }
-
-    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
-    public void searchWithTwoKeywordsAcrossTwoFields(String testName) throws Exception {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(testBanner(testName, CLASS_NAME));
-        }
-
-        // Create one or more keyword retrievable resources, each containing
-        // two specified keywords.
-        long numKeywordRetrievableResources = 5;
-        if (logger.isDebugEnabled()) {
-            logger.debug("Creating " + numKeywordRetrievableResources
-                    + " keyword-retrievable resources ...");
-        }
-        boolean keywordsInSameField = false;
-        createCollectionObjects(numKeywordRetrievableResources, TWO_MORE_KEYWORDS, keywordsInSameField);
-
-        // Set the expected status code and group of valid status codes
-        testSetup(STATUS_OK, ServiceRequestType.SEARCH);
-
-        // Search using both terms
-
-        // Send the search request and receive a response
-        ClientResponse<AbstractCommonList> res = doSearch(TWO_MORE_KEYWORDS);
-        int statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
-        long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-
-        // Search using a single term
-
-        // Send the search request and receive a response
-        res = doSearch(TWO_MORE_KEYWORDS.get(0));
-        statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
-        numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-
-    }
-
-//    @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
-//    public void searchWithOneKeywordInRepeatableScalarField(String testName) throws Exception {
-//        BriefDescriptionList descriptionList = new BriefDescriptionList();
-//        List<String> descriptions = descriptionList.getBriefDescription();
-//        if (TWO_KEYWORDS.size() >= 2) {
-//            descriptions.add(TWO_KEYWORDS.get(0));
-//            descriptions.add(TWO_KEYWORDS.get(1));
-//        }
-//    }
-    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
-    groups = {"utf8"})
-    public void searchWithUTF8Keyword(String testName) {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(testBanner(testName, CLASS_NAME));
-        }
-
-        // Create one or more keyword retrievable resources, each containing
-        // two specified keywords.
-        long numKeywordRetrievableResources = 2;
-        if (logger.isDebugEnabled()) {
-            logger.debug("Creating " + numKeywordRetrievableResources
-                    + " keyword-retrievable resources ...");
-        }
-        createCollectionObjects(numKeywordRetrievableResources, UTF8_KEYWORD);
-
-        // Set the expected status code and group of valid status codes
-        testSetup(STATUS_OK, ServiceRequestType.SEARCH);
-
-        // Send the search request and receive a response
-        ClientResponse<AbstractCommonList> res = doSearch(UTF8_KEYWORD);
-        int statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
-        long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-    }
-
-    // Failure outcomes
-    // FIXME: Rename to searchWithNonExistentKeyword
-    @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
-    public void keywordSearchNonExistentKeyword(String testName) throws Exception {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(testBanner(testName, CLASS_NAME));
-        }
-
-        // Set the expected status code and group of valid status codes
-        testSetup(STATUS_OK, ServiceRequestType.SEARCH);
-
-        // Send the search request and receive a response
-        ClientResponse<AbstractCommonList> res = doSearch(NON_EXISTENT_KEYWORD);
-        int statusCode = res.getStatus();
-
-        // Check the status code of the response: does it match
-        // the expected response(s)?
-        if (logger.isDebugEnabled()) {
-            logger.debug(testName + ": status = " + statusCode);
-        }
-        Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
-                invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
-        Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-
-        // Verify that the number of resources matched by the search
-        // is identical to the expected result
-        long NUM_MATCHES_EXPECTED = 0;
-        long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
-        Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
-
-    }
-
-    // ---------------------------------------------------------------
-    // Cleanup of resources created during testing
-    // ---------------------------------------------------------------
-    /**
-     * Deletes all resources created by setup and tests, after all tests have been run.
-     *
-     * This cleanup method will always be run, even if one or more tests fail.
-     * For this reason, it attempts to remove all resources created
-     * at any point during testing, even if some of those resources
-     * may be expected to be deleted by certain tests.
-     */
-    @AfterClass(alwaysRun = true)
-    public void cleanUp() {
-        String noTest = System.getProperty("noTestCleanup");
-        if (Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Skipping Cleanup phase ...");
-            }
-            return;
-        }
-        if (logger.isDebugEnabled()) {
-            logger.debug("Cleaning up temporary resources created for testing ...");
-        }
-        CollectionObjectClient collectionObjectClient = new CollectionObjectClient();
-        for (String resourceId : allResourceIdsCreated) {
-            // Note: Any non-success responses are ignored and not reported.
-            collectionObjectClient.delete(resourceId).releaseConnection();
-        }
-    }
-
-    // ---------------------------------------------------------------
-    // Utility methods used by tests above
-    // ---------------------------------------------------------------
-    private void createCollectionObjects(long numToCreate, String keyword) {
-        List keywords = new ArrayList<String>();
-        keywords.add(keyword);
-        boolean keywordsInSameField = true;
-        createCollectionObjects(numToCreate, keywords, keywordsInSameField);
-    }
-
-    private void createCollectionObjects(long numToCreate, List<String> keywords,
-            boolean keywordsInSameField) {
-        testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
-        CollectionObjectClient client = new CollectionObjectClient();
-        for (long i = 0; i < numToCreate; i++) {
-            PoxPayloadOut multipart =
-                    createCollectionObjectInstance(i, keywords, keywordsInSameField);
-            ClientResponse<Response> res = client.create(multipart);
-            try {
-                int statusCode = res.getStatus();
-                Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
-                String id = extractId(res);
-                allResourceIdsCreated.add(id);
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Created new resource [" + i + "] with ID " + id);
-                }
-            } finally {
-                res.releaseConnection();
-            }
-        }
-    }
-
-    private PoxPayloadOut createCollectionObjectInstance(long i, List<String> keywords,
-            boolean keywordsInSameField) {
-        CollectionobjectsCommon collectionObject = new CollectionobjectsCommon();
-        collectionObject.setObjectNumber(createIdentifier());
-        if (keywordsInSameField) {
-            collectionObject.setDistinguishingFeatures(listToString(keywords, KEYWORD_SEPARATOR));
-        } else {
-            if (keywords.size() == 1) {
-                collectionObject.setDistinguishingFeatures(keywords.get(0));
-            } else if (keywords.size() == 2) {
-                collectionObject.setDistinguishingFeatures(keywords.get(0));
-                collectionObject.setPhysicalDescription(keywords.get(1));
-            } else {
-                Assert.fail("List of keywords must have exactly one or two members.");
-            }
-        }
-        PoxPayloadOut multipart = new PoxPayloadOut(CollectionObjectClient.SERVICE_PAYLOAD_NAME);
-        PayloadOutputPart commonPart = multipart.addPart(collectionObject,
-                MediaType.APPLICATION_XML_TYPE);
-        commonPart.setLabel(new CollectionObjectClient().getCommonPartName());
-        return multipart;
-    }
-
-    private static String listToString(List<String> list, String separator) {
-        StringBuffer sb = new StringBuffer();
-        if (list.size() > 0) {
-            sb.append(list.get(0));
-            for (int i = 1; i < list.size(); i++) {
-                sb.append(separator);
-                sb.append(list.get(i));
-            }
-        }
-        return sb.toString();
-    }
-
-    private ClientResponse<AbstractCommonList> doSearch(List<String> keywords) {
-        String searchParamValue = listToString(keywords, KEYWORD_SEPARATOR);
-        return doSearch(searchParamValue);
-    }
-
-    private ClientResponse<AbstractCommonList> doSearch(String keyword) {
-        String searchParamValue = keyword;
-        if (logger.isDebugEnabled()) {
-            logger.debug("Searching on keyword(s): " + searchParamValue + " ...");
-        }
-        CollectionObjectClient client = new CollectionObjectClient();
-        final boolean NOT_INCLUDING_DELETED_RESOURCES = false;
-        ClientResponse<AbstractCommonList> res =
-                client.keywordSearchIncludeDeleted(searchParamValue, NOT_INCLUDING_DELETED_RESOURCES);
-        return res;
-    }
-
-    private long getNumMatched(ClientResponse<AbstractCommonList> res,
-            long numExpectedMatches, String testName) {
-        AbstractCommonList list = (AbstractCommonList) res.getEntity(AbstractCommonList.class);
-        long numMatched = list.getTotalItems();
-        if (logger.isDebugEnabled()) {
-            logger.debug("Keyword search matched " + numMatched
-                    + " resources, expected to match " + numExpectedMatches);
-        }
-
-        // Optionally output additional data about list members for debugging.
-        if(logger.isTraceEnabled()){
-               AbstractCommonListUtils.ListItemsInAbstractCommonList(list, logger, testName);
-        }
-        
-        return numMatched;
-    }
-
-    private void itemizeListItems(AbstractCommonList list) {
-        List<AbstractCommonList.ListItem> items =
-                list.getListItem();
-        int i = 0;
-        for (AbstractCommonList.ListItem item : items) {
-            logger.debug("list-item[" + i + "] title="
-                    + AbstractCommonListUtils.ListItemGetElementValue(item, "title"));
-            logger.debug("list-item[" + i + "] URI="
-                    + AbstractCommonListUtils.ListItemGetElementValue(item, "uri"));
-            i++;
-        }
-    }
-
-    public static String getSystemTimeIdentifier() {
-        return Long.toString(System.currentTimeMillis());
-    }
+       /** The logger. */
+       private final String CLASS_NAME = CollectionObjectSearchTest.class
+                       .getName();
+       private final Logger logger = LoggerFactory.getLogger(CLASS_NAME);
+       final static String IDENTIFIER = getSystemTimeIdentifier();
+       final static String KEYWORD_SEPARATOR = " ";
+       final long numNoiseWordResources = 10;
+       final double pctNonNoiseWordResources = 0.5;
+       // Use this to keep track of resources to delete
+       private List<String> allResourceIdsCreated = new ArrayList<String>();
+
+       // Constants for data used in search testing
+
+       // Test keywords unlikely to be encountered in actual collections data,
+       // consisting of the names of mythical creatures in a 1970s role-playing
+       // game, which result in very few 'hits' in Google searches.
+       final static String KEYWORD = "Tsolyani" + IDENTIFIER;
+       final static List<String> TWO_KEYWORDS = Arrays.asList(new String[] {
+                       "Cheggarra" + IDENTIFIER, "Ahoggya" + IDENTIFIER });
+       final static List<String> TWO_MORE_KEYWORDS = Arrays.asList(new String[] {
+                       "Karihaya" + IDENTIFIER, "Hlikku" + IDENTIFIER });
+       final static String NOISE_WORD = "Mihalli + IDENTIFIER";
+       // Test Unicode UTF-8 term for keyword searching: a random sequence,
+       // unlikely to be encountered in actual collections data, of two USASCII
+       // characters followed by four non-USASCII range Unicode UTF-8 characters:
+       //
+       // Δ : Greek capital letter Delta (U+0394)
+       // Ж : Cyrillic capital letter Zhe with breve (U+04C1)
+       // Ŵ : Latin capital letter W with circumflex (U+0174)
+       // Ω : Greek capital letter Omega (U+03A9)
+       final String UTF8_KEYWORD = "to" + '\u0394' + '\u04C1' + '\u0174'
+                       + '\u03A9';
+       // Non-existent term unlikely to be encountered in actual collections
+       // data, consisting of two back-to-back sets of the first letters of
+       // each of the words in a short pangram for the English alphabet.
+       final static String NON_EXISTENT_KEYWORD = "jlmbsoqjlmbsoq";
+
+       @Override
+       protected String getServiceName() {
+               throw new UnsupportedOperationException(); // FIXME: REM - See
+                                                                                                       // http://issues.collectionspace.org/browse/CSPACE-3498
+       }
+
+       @Override
+       protected String getServicePathComponent() {
+               // TODO Auto-generated method stub
+               throw new UnsupportedOperationException(); // FIXME: REM - See
+                                                                                                       // http://issues.collectionspace.org/browse/CSPACE-3498
+       }
+
+       // /* (non-Javadoc)
+       // * @see
+       // org.collectionspace.services.client.test.BaseServiceTest#getServicePathComponent()
+       // */
+       // @Override
+       // protected String getServicePathComponent() {
+       // return new CollectionObjectClient().getServicePathComponent(); //FIXME:
+       // REM = Remove all refs to this method.
+       // }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * org.collectionspace.services.client.test.BaseServiceTest#getClientInstance
+        * ()
+        */
+       @Override
+       protected CollectionSpaceClient getClientInstance() {
+               return new CollectionObjectClient();
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see org.collectionspace.services.client.test.BaseServiceTest#
+        * getAbstractCommonList(org.jboss.resteasy.client.ClientResponse)
+        */
+       @Override
+       protected AbstractCommonList getAbstractCommonList(
+                       ClientResponse<AbstractCommonList> response) {
+               return response.getEntity(AbstractCommonList.class);
+       }
+
+       /**
+        * Creates one or more resources containing a "noise" keyword, which should
+        * NOT be retrieved by keyword searches.
+        * 
+        * This also helps ensure that searches will not fail, due to a
+        * database-specific constraint or otherwise, if the number of records
+        * containing a particular keyword represent too high a proportion of the
+        * total number of records.
+        */
+       @BeforeClass(alwaysRun = true)
+       public void setup() {
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Creating " + numNoiseWordResources
+                                       + " 'noise word' resources ...");
+               }
+               createCollectionObjects(numNoiseWordResources, NOISE_WORD);
+       }
+
+       // ---------------------------------------------------------------
+       // Search tests
+       // ---------------------------------------------------------------
+       // Success outcomes
+
+       @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class, groups = { "advancedSearch" })
+       public void advancedSearch(String testName) throws Exception {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testBanner(testName, CLASS_NAME));
+               }
+
+               // Create one or more keyword retrievable resources, each containing
+               // a specified keyword.
+               String theKeyword = KEYWORD + "COW";
+               long numKeywordRetrievableResources = 1;
+               createCollectionObjects(numKeywordRetrievableResources, theKeyword);
+
+               // Set the expected status code and group of valid status codes
+               testSetup(STATUS_OK, ServiceRequestType.SEARCH);
+
+               // Send the search request and receive a response
+               String propertyName = CollectionObjectClient.SERVICE_COMMON_PART_NAME + ":" +
+                       CollectionObjectJAXBSchema.DISTINGUISHING_FEATURES;
+               String propertyValue = theKeyword;
+               ClientResponse<AbstractCommonList> res = doAdvancedSearch(propertyName, propertyValue, "=");
+               int statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+       }
+
+       @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class, groups = { "oneKeyword" })
+       public void searchWithOneKeyword(String testName) throws Exception {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testBanner(testName, CLASS_NAME));
+               }
+
+               // Create one or more keyword retrievable resources, each containing
+               // a specified keyword.
+               long numKeywordRetrievableResources = (long) (numNoiseWordResources * pctNonNoiseWordResources);
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Creating " + numKeywordRetrievableResources
+                                       + " keyword-retrievable resources ...");
+               }
+               createCollectionObjects(numKeywordRetrievableResources, KEYWORD);
+
+               // Set the expected status code and group of valid status codes
+               testSetup(STATUS_OK, ServiceRequestType.SEARCH);
+
+               // Send the search request and receive a response
+               ClientResponse<AbstractCommonList> res = doSearch(KEYWORD);
+               int statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+       }
+
+       @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
+       public void searchWithTwoKeywordsInSameField(String testName)
+                       throws Exception {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testBanner(testName, CLASS_NAME));
+               }
+
+               // Create one or more keyword retrievable resources, each containing
+               // two specified keywords.
+               long numKeywordRetrievableResources = (long) (numNoiseWordResources * pctNonNoiseWordResources);
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Creating " + numKeywordRetrievableResources
+                                       + " keyword-retrievable resources ...");
+               }
+               boolean keywordsInSameField = true;
+               createCollectionObjects(numKeywordRetrievableResources, TWO_KEYWORDS,
+                               keywordsInSameField);
+
+               // Set the expected status code and group of valid status codes
+               testSetup(STATUS_OK, ServiceRequestType.SEARCH);
+
+               // Search using both terms
+
+               // Send the search request and receive a response
+               ClientResponse<AbstractCommonList> res = doSearch(TWO_KEYWORDS);
+               int statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+
+               // Search using a single term
+
+               // Send the search request and receive a response
+               res = doSearch(TWO_KEYWORDS.get(0));
+               statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+
+       }
+
+       @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
+       public void searchWithTwoKeywordsAcrossTwoFields(String testName)
+                       throws Exception {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testBanner(testName, CLASS_NAME));
+               }
+
+               // Create one or more keyword retrievable resources, each containing
+               // two specified keywords.
+               long numKeywordRetrievableResources = 5;
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Creating " + numKeywordRetrievableResources
+                                       + " keyword-retrievable resources ...");
+               }
+               boolean keywordsInSameField = false;
+               createCollectionObjects(numKeywordRetrievableResources,
+                               TWO_MORE_KEYWORDS, keywordsInSameField);
+
+               // Set the expected status code and group of valid status codes
+               testSetup(STATUS_OK, ServiceRequestType.SEARCH);
+
+               // Search using both terms
+
+               // Send the search request and receive a response
+               ClientResponse<AbstractCommonList> res = doSearch(TWO_MORE_KEYWORDS);
+               int statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+
+               // Search using a single term
+
+               // Send the search request and receive a response
+               res = doSearch(TWO_MORE_KEYWORDS.get(0));
+               statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+
+       }
+
+       // @Test(dataProvider="testName",
+       // dataProviderClass=AbstractServiceTestImpl.class)
+       // public void searchWithOneKeywordInRepeatableScalarField(String testName)
+       // throws Exception {
+       // BriefDescriptionList descriptionList = new BriefDescriptionList();
+       // List<String> descriptions = descriptionList.getBriefDescription();
+       // if (TWO_KEYWORDS.size() >= 2) {
+       // descriptions.add(TWO_KEYWORDS.get(0));
+       // descriptions.add(TWO_KEYWORDS.get(1));
+       // }
+       // }
+       @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class, groups = { "utf8" })
+       public void searchWithUTF8Keyword(String testName) {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testBanner(testName, CLASS_NAME));
+               }
+
+               // Create one or more keyword retrievable resources, each containing
+               // two specified keywords.
+               long numKeywordRetrievableResources = 2;
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Creating " + numKeywordRetrievableResources
+                                       + " keyword-retrievable resources ...");
+               }
+               createCollectionObjects(numKeywordRetrievableResources, UTF8_KEYWORD);
+
+               // Set the expected status code and group of valid status codes
+               testSetup(STATUS_OK, ServiceRequestType.SEARCH);
+
+               // Send the search request and receive a response
+               ClientResponse<AbstractCommonList> res = doSearch(UTF8_KEYWORD);
+               int statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
+               long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+       }
+
+       // Failure outcomes
+       // FIXME: Rename to searchWithNonExistentKeyword
+       @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
+       public void keywordSearchNonExistentKeyword(String testName)
+                       throws Exception {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testBanner(testName, CLASS_NAME));
+               }
+
+               // Set the expected status code and group of valid status codes
+               testSetup(STATUS_OK, ServiceRequestType.SEARCH);
+
+               // Send the search request and receive a response
+               ClientResponse<AbstractCommonList> res = doSearch(NON_EXISTENT_KEYWORD);
+               int statusCode = res.getStatus();
+
+               // Check the status code of the response: does it match
+               // the expected response(s)?
+               if (logger.isDebugEnabled()) {
+                       logger.debug(testName + ": status = " + statusCode);
+               }
+               Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
+                               invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
+               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+
+               // Verify that the number of resources matched by the search
+               // is identical to the expected result
+               long NUM_MATCHES_EXPECTED = 0;
+               long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED, testName);
+               Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
+
+       }
+
+       // ---------------------------------------------------------------
+       // Cleanup of resources created during testing
+       // ---------------------------------------------------------------
+       /**
+        * Deletes all resources created by setup and tests, after all tests have
+        * been run.
+        * 
+        * This cleanup method will always be run, even if one or more tests fail.
+        * For this reason, it attempts to remove all resources created at any point
+        * during testing, even if some of those resources may be expected to be
+        * deleted by certain tests.
+        */
+       @AfterClass(alwaysRun = true)
+       public void cleanUp() {
+               String noTest = System.getProperty("noTestCleanup");
+               if (Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
+                       if (logger.isDebugEnabled()) {
+                               logger.debug("Skipping Cleanup phase ...");
+                       }
+                       return;
+               }
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Cleaning up temporary resources created for testing ...");
+               }
+               CollectionObjectClient collectionObjectClient = new CollectionObjectClient();
+               for (String resourceId : allResourceIdsCreated) {
+                       // Note: Any non-success responses are ignored and not reported.
+                       collectionObjectClient.delete(resourceId).releaseConnection();
+               }
+       }
+
+       // ---------------------------------------------------------------
+       // Utility methods used by tests above
+       // ---------------------------------------------------------------
+       private void createCollectionObjects(long numToCreate, String keyword) {
+               List keywords = new ArrayList<String>();
+               keywords.add(keyword);
+               boolean keywordsInSameField = true;
+               createCollectionObjects(numToCreate, keywords, keywordsInSameField);
+       }
+
+       private void createCollectionObjects(long numToCreate,
+                       List<String> keywords, boolean keywordsInSameField) {
+               testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
+               CollectionObjectClient client = new CollectionObjectClient();
+               for (long i = 0; i < numToCreate; i++) {
+                       PoxPayloadOut multipart = createCollectionObjectInstance(i,
+                                       keywords, keywordsInSameField);
+                       ClientResponse<Response> res = client.create(multipart);
+                       try {
+                               int statusCode = res.getStatus();
+                               Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
+                               String id = extractId(res);
+                               allResourceIdsCreated.add(id);
+                               if (logger.isDebugEnabled()) {
+                                       logger.debug("Created new resource [" + i + "] with ID "
+                                                       + id);
+                               }
+                       } finally {
+                               res.releaseConnection();
+                       }
+               }
+       }
+
+       private PoxPayloadOut createCollectionObjectInstance(long i,
+                       List<String> keywords, boolean keywordsInSameField) {
+               CollectionobjectsCommon collectionObject = new CollectionobjectsCommon();
+               collectionObject.setObjectNumber(createIdentifier());
+               if (keywordsInSameField) {
+                       collectionObject.setDistinguishingFeatures(listToString(keywords,
+                                       KEYWORD_SEPARATOR));
+               } else {
+                       if (keywords.size() == 1) {
+                               collectionObject.setDistinguishingFeatures(keywords.get(0));
+                       } else if (keywords.size() == 2) {
+                               collectionObject.setDistinguishingFeatures(keywords.get(0));
+                               collectionObject.setPhysicalDescription(keywords.get(1));
+                       } else {
+                               Assert.fail("List of keywords must have exactly one or two members.");
+                       }
+               }
+               PoxPayloadOut multipart = new PoxPayloadOut(
+                               CollectionObjectClient.SERVICE_PAYLOAD_NAME);
+               PayloadOutputPart commonPart = multipart.addPart(collectionObject,
+                               MediaType.APPLICATION_XML_TYPE);
+               commonPart.setLabel(new CollectionObjectClient().getCommonPartName());
+               return multipart;
+       }
+
+       private static String listToString(List<String> list, String separator) {
+               StringBuffer sb = new StringBuffer();
+               if (list.size() > 0) {
+                       sb.append(list.get(0));
+                       for (int i = 1; i < list.size(); i++) {
+                               sb.append(separator);
+                               sb.append(list.get(i));
+                       }
+               }
+               return sb.toString();
+       }
+
+       private ClientResponse<AbstractCommonList> doSearch(List<String> keywords) {
+               String searchParamValue = listToString(keywords, KEYWORD_SEPARATOR);
+               return doSearch(searchParamValue);
+       }
+
+       private ClientResponse<AbstractCommonList> doAdvancedSearch(
+                       String propertyName, String propertyValue, String operator) {
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Searching on property: " + propertyName + "="
+                                       + "'" + propertyValue + "'");
+               }
+               String whereClause = propertyName + operator +
+                       "'" + propertyValue + "'";
+               CollectionObjectClient client = new CollectionObjectClient();
+               ClientResponse<AbstractCommonList> res = client
+                               .advancedSearchIncludeDeleted(whereClause, false); // NOT_INCLUDING_DELETED_RESOURCES
+               return res;
+       }
+
+       private ClientResponse<AbstractCommonList> doSearch(String keyword) {
+               String searchParamValue = keyword;
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Searching on keyword(s): " + searchParamValue
+                                       + " ...");
+               }
+               CollectionObjectClient client = new CollectionObjectClient();
+               final boolean NOT_INCLUDING_DELETED_RESOURCES = false;
+               ClientResponse<AbstractCommonList> res = client
+                               .keywordSearchIncludeDeleted(searchParamValue,
+                                               NOT_INCLUDING_DELETED_RESOURCES);
+               return res;
+       }
+
+       private long getNumMatched(ClientResponse<AbstractCommonList> res,
+                       long numExpectedMatches, String testName) {
+               AbstractCommonList list = (AbstractCommonList) res
+                               .getEntity(AbstractCommonList.class);
+               long numMatched = list.getTotalItems();
+               if (logger.isDebugEnabled()) {
+                       logger.debug("Keyword search matched " + numMatched
+                                       + " resources, expected to match " + numExpectedMatches);
+               }
+
+               // Optionally output additional data about list members for debugging.
+               if (logger.isTraceEnabled()) {
+                       AbstractCommonListUtils.ListItemsInAbstractCommonList(list, logger,
+                                       testName);
+               }
+
+               return numMatched;
+       }
+
+       private void itemizeListItems(AbstractCommonList list) {
+               List<AbstractCommonList.ListItem> items = list.getListItem();
+               int i = 0;
+               for (AbstractCommonList.ListItem item : items) {
+                       logger.debug("list-item["
+                                       + i
+                                       + "] title="
+                                       + AbstractCommonListUtils.ListItemGetElementValue(item,
+                                                       "title"));
+                       logger.debug("list-item["
+                                       + i
+                                       + "] URI="
+                                       + AbstractCommonListUtils.ListItemGetElementValue(item,
+                                                       "uri"));
+                       i++;
+               }
+       }
+
+       public static String getSystemTimeIdentifier() {
+               return Long.toString(System.currentTimeMillis());
+       }
 }
index 09c4ba6e9335dccced34db48344be34561c583ba..efb29fa76e8e0cbf3ee293687cf79a3732b40f8d 100644 (file)
@@ -227,9 +227,10 @@ public abstract class ResourceBase
     public AbstractCommonList getList(@Context UriInfo ui) {\r
         MultivaluedMap<String, String> queryParams = ui.getQueryParameters();\r
         String keywords = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_KW);\r
+        String advancedSearch = queryParams.getFirst(IQueryManager.SEARCH_TYPE_KEYWORDS_AS);\r
         AbstractCommonList list;\r
-        if (keywords != null) {\r
-            list = search(queryParams, keywords);\r
+        if (keywords != null || advancedSearch != null) {\r
+            list = search(queryParams, keywords, advancedSearch);\r
         } else {\r
             list = getList(queryParams);\r
         }\r
@@ -257,7 +258,9 @@ public abstract class ResourceBase
         }\r
     }\r
 \r
-    protected AbstractCommonList search(MultivaluedMap<String, String> queryParams, String keywords) {\r
+    protected AbstractCommonList search(MultivaluedMap<String, String> queryParams,\r
+               String keywords,\r
+               String advancedSearch) {\r
         try {\r
             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(queryParams);\r
             DocumentHandler handler = createDocumentHandler(ctx);\r
@@ -270,6 +273,14 @@ public abstract class ResourceBase
                     logger.debug("The WHERE clause is: " + documentFilter.getWhereClause());\r
                 }\r
             }\r
+            if (advancedSearch != null && !advancedSearch.isEmpty()) {\r
+                String whereClause = QueryManager.createWhereClauseFromAdvancedSearch(advancedSearch);\r
+                DocumentFilter documentFilter = handler.getDocumentFilter();\r
+                documentFilter.appendWhereClause(whereClause, IQueryManager.SEARCH_QUALIFIER_AND);\r
+                if (logger.isDebugEnabled()) {\r
+                    logger.debug("The WHERE clause is: " + documentFilter.getWhereClause());\r
+                }\r
+            }\r
             getRepositoryClient(ctx).getFiltered(ctx, handler);\r
             return (AbstractCommonList) handler.getCommonPartList();\r
         } catch (Exception e) {\r
index cf88207f09108ff40c634e34c0043fc197122b8d..8a0e0c0a0c262d3a0048392e165a1b54bc7df90a 100644 (file)
@@ -47,6 +47,10 @@ public class QueryManager {
                return queryManager.createWhereClauseFromKeywords(keywords);\r
        }\r
        \r
+       static public String createWhereClauseFromAdvancedSearch(String keywords) {\r
+               return queryManager.createWhereClauseFromAdvancedSearch(keywords);\r
+       }\r
+       \r
        /**\r
         * Creates the where clause for partial term match.\r
         * \r
index 3c396ab7e9c28388ec4db923acfe730625bb9a5a..16be5b55abd8fda7e0376e214a0b19b639a5588d 100644 (file)
@@ -45,28 +45,31 @@ import org.collectionspace.services.common.storage.DatabaseProductType;
 import org.collectionspace.services.common.storage.JDBCTools;\r
 \r
 public class QueryManagerNuxeoImpl implements IQueryManager {\r
-       \r
-       private static String ECM_FULLTEXT_LIKE = \r
-               "ecm:fulltext" + SEARCH_TERM_SEPARATOR + IQueryManager.SEARCH_LIKE;\r
+\r
+       private static String ECM_FULLTEXT_LIKE = "ecm:fulltext"\r
+                       + SEARCH_TERM_SEPARATOR + IQueryManager.SEARCH_LIKE;\r
        private static String SEARCH_LIKE_FORM = null;\r
 \r
        private final Logger logger = LoggerFactory\r
                        .getLogger(QueryManagerNuxeoImpl.class);\r
-       \r
-       // Consider that letters, letter-markers, numbers, '_' and apostrophe are words  \r
-       private static Pattern nonWordChars = Pattern.compile("[^\\p{L}\\p{M}\\p{N}_']");\r
+\r
+       // Consider that letters, letter-markers, numbers, '_' and apostrophe are\r
+       // words\r
+       private static Pattern nonWordChars = Pattern\r
+                       .compile("[^\\p{L}\\p{M}\\p{N}_']");\r
        private static Pattern unescapedDblQuotes = Pattern.compile("(?<!\\\\)\"");\r
        private static Pattern unescapedSingleQuote = Pattern.compile("(?<!\\\\)'");\r
-       private static Pattern kwdSearchProblemChars = Pattern.compile("[\\:\\(\\)]");\r
+       private static Pattern kwdSearchProblemChars = Pattern\r
+                       .compile("[\\:\\(\\)]");\r
        private static Pattern kwdSearchHyphen = Pattern.compile(" - ");\r
-       \r
+\r
        private static String getLikeForm() {\r
-               if(SEARCH_LIKE_FORM == null) {\r
+               if (SEARCH_LIKE_FORM == null) {\r
                        try {\r
                                DatabaseProductType type = JDBCTools.getDatabaseProductType();\r
-                               if(type == DatabaseProductType.MYSQL) {\r
+                               if (type == DatabaseProductType.MYSQL) {\r
                                        SEARCH_LIKE_FORM = IQueryManager.SEARCH_LIKE;\r
-                               } else if(type == DatabaseProductType.POSTGRESQL) {\r
+                               } else if (type == DatabaseProductType.POSTGRESQL) {\r
                                        SEARCH_LIKE_FORM = IQueryManager.SEARCH_ILIKE;\r
                                }\r
                        } catch (Exception e) {\r
@@ -76,168 +79,221 @@ public class QueryManagerNuxeoImpl implements IQueryManager {
                return SEARCH_LIKE_FORM;\r
        }\r
 \r
-       //TODO: This is currently just an example fixed query.  This should eventually be\r
+       // TODO: This is currently just an example fixed query. This should\r
+       // eventually be\r
        // removed or replaced with a more generic method.\r
-       /* (non-Javadoc)\r
-        * @see org.collectionspace.services.common.query.IQueryManager#execQuery(java.lang.String)\r
+       /*\r
+        * (non-Javadoc)\r
+        * \r
+        * @see\r
+        * org.collectionspace.services.common.query.IQueryManager#execQuery(java\r
+        * .lang.String)\r
         */\r
+       @Override\r
+       @Deprecated\r
        public void execQuery(String queryString) {\r
                NuxeoClient client = null;\r
                try {\r
                        client = NuxeoConnector.getInstance().getClient();\r
                        RepositoryInstance repoSession = client.openRepository();\r
-                       \r
-                       DocumentModelList docModelList = repoSession.query("SELECT * FROM Relation WHERE relation:relationtype.documentId1='updated-Subject-1'");\r
-//                     DocumentModelList docModelList = repoSession.query("SELECT * FROM Relation");\r
-//                     DocumentModelList docModelList = repoSession.query("SELECT * FROM CollectionObject WHERE collectionobject:objectNumber='objectNumber-1251305545865'");\r
+\r
+                       DocumentModelList docModelList = repoSession\r
+                                       .query("SELECT * FROM Relation WHERE relation:relationtype.documentId1='updated-Subject-1'");\r
+                       // DocumentModelList docModelList =\r
+                       // repoSession.query("SELECT * FROM Relation");\r
+                       // DocumentModelList docModelList =\r
+                       // repoSession.query("SELECT * FROM CollectionObject WHERE collectionobject:objectNumber='objectNumber-1251305545865'");\r
                        for (DocumentModel docModel : docModelList) {\r
-                               System.out.println("--------------------------------------------");\r
+                               System.out\r
+                                               .println("--------------------------------------------");\r
                                System.out.println(docModel.getPathAsString());\r
                                System.out.println(docModel.getName());\r
                                System.out.println(docModel.getPropertyValue("dc:title"));\r
-//                             System.out.println("documentId1=" + docModel.getProperty("relation", "relationtype/documentId1").toString());\r
+                               // System.out.println("documentId1=" +\r
+                               // docModel.getProperty("relation",\r
+                               // "relationtype/documentId1").toString());\r
                        }\r
-                       \r
+\r
                } catch (Exception e) {\r
                        // TODO Auto-generated catch block\r
                        e.printStackTrace();\r
-               }               \r
+               }\r
+       }\r
+\r
+       @Override\r
+       public String createWhereClauseFromAdvancedSearch(String advancedSearch) {\r
+               String result = null;\r
+               //\r
+               // Process search term.  FIXME: REM - Do we need to perform and string filtering here?\r
+               //\r
+               if (advancedSearch != null && !advancedSearch.isEmpty()) {\r
+                       StringBuffer advancedSearchWhereClause = new StringBuffer(\r
+                                       advancedSearch);\r
+                       result = advancedSearchWhereClause.toString();\r
+               }\r
+               \r
+               return result;\r
        }\r
 \r
-       /* (non-Javadoc)\r
-        * @see org.collectionspace.services.common.query.IQueryManager#createWhereClauseFromKeywords(java.lang.String)\r
+       /*\r
+        * (non-Javadoc)\r
+        * \r
+        * @see org.collectionspace.services.common.query.IQueryManager#\r
+        * createWhereClauseFromKeywords(java.lang.String)\r
         */\r
-       // TODO handle keywords containing escaped punctuation chars, then we need to qualify the\r
+       // TODO handle keywords containing escaped punctuation chars, then we need\r
+       // to qualify the\r
        // search by matching on the fulltext.simpletext field.\r
-       // TODO handle keywords containing unescaped double quotes by matching the phrase\r
+       // TODO handle keywords containing unescaped double quotes by matching the\r
+       // phrase\r
        // against the fulltext.simpletext field.\r
-       // Both these require using JDBC, since we cannot get to the fulltext table in NXQL\r
+       // Both these require using JDBC, since we cannot get to the fulltext table\r
+       // in NXQL\r
+       @Override\r
        public String createWhereClauseFromKeywords(String keywords) {\r
                String result = null;\r
                StringBuffer fullTextWhereClause = new StringBuffer(SEARCH_GROUP_OPEN);\r
-               //StringBuffer phraseWhereClause = new StringBuffer(SEARCH_GROUP_OPEN);\r
+               // StringBuffer phraseWhereClause = new StringBuffer(SEARCH_GROUP_OPEN);\r
                boolean phrasesToAdd = false;\r
                // Split on unescaped double quotes to handle phrases\r
                String[] phrases = unescapedDblQuotes.split(keywords.trim());\r
                boolean first = true;\r
-               for(String phrase : phrases ) {\r
+               for (String phrase : phrases) {\r
                        String trimmed = phrase.trim();\r
                        // Ignore empty strings from match, or goofy input\r
-                       if(trimmed.isEmpty())\r
+                       if (trimmed.isEmpty())\r
                                continue;\r
                        // Add the phrase to the string to pass in for full text matching.\r
-                       // Note that we can pass in a set of words and it will do the OR for us.\r
-                       if(first) {\r
-                               fullTextWhereClause.append(ECM_FULLTEXT_LIKE +"'");\r
+                       // Note that we can pass in a set of words and it will do the OR for\r
+                       // us.\r
+                       if (first) {\r
+                               fullTextWhereClause.append(ECM_FULLTEXT_LIKE + "'");\r
                                first = false;\r
                        } else {\r
                                fullTextWhereClause.append(SEARCH_TERM_SEPARATOR);\r
                        }\r
                        // ignore the special chars except single quote here - can't hurt\r
                        // TODO this should become a special function that strips things the\r
-                       // fulltext will ignore, including non-word chars and too-short words,\r
-                       // and escaping single quotes. Can return a boolean for anything stripped,\r
-                       // which triggers the back-up search. We can think about whether stripping\r
+                       // fulltext will ignore, including non-word chars and too-short\r
+                       // words,\r
+                       // and escaping single quotes. Can return a boolean for anything\r
+                       // stripped,\r
+                       // which triggers the back-up search. We can think about whether\r
+                       // stripping\r
                        // short words not in a quoted phrase should trigger the backup.\r
                        trimmed = unescapedSingleQuote.matcher(trimmed).replaceAll("\\\\'");\r
                        // If there are non-word chars in the phrase, we need to match the\r
                        // phrase exactly against the fulltext table for this object\r
-                       //if(nonWordChars.matcher(trimmed).matches()) {\r
-                       //}\r
-                       // Replace problem chars with spaces. Patches CSPACE-4147, CSPACE-4106 \r
+                       // if(nonWordChars.matcher(trimmed).matches()) {\r
+                       // }\r
+                       // Replace problem chars with spaces. Patches CSPACE-4147,\r
+                       // CSPACE-4106\r
                        trimmed = kwdSearchProblemChars.matcher(trimmed).replaceAll(" ");\r
                        trimmed = kwdSearchHyphen.matcher(trimmed).replaceAll(" ");\r
 \r
                        fullTextWhereClause.append(trimmed);\r
                        if (logger.isTraceEnabled() == true) {\r
-                               logger.trace("Current built whereClause is: " + fullTextWhereClause.toString());\r
+                               logger.trace("Current built whereClause is: "\r
+                                               + fullTextWhereClause.toString());\r
                        }\r
                }\r
-               if(first) {\r
-                       throw new RuntimeException("No usable keywords specified in string:["\r
-                                       +keywords+"]");\r
+               if (first) {\r
+                       throw new RuntimeException(\r
+                                       "No usable keywords specified in string:[" + keywords + "]");\r
                }\r
-               fullTextWhereClause.append("'"+SEARCH_GROUP_CLOSE);\r
-               \r
+               fullTextWhereClause.append("'" + SEARCH_GROUP_CLOSE);\r
+\r
                result = fullTextWhereClause.toString();\r
-           if (logger.isDebugEnabled()) {\r
-               logger.debug("Final built WHERE clause is: " + result);\r
-           }\r
-           \r
-           return result;\r
+               if (logger.isDebugEnabled()) {\r
+                       logger.debug("Final built WHERE clause is: " + result);\r
+               }\r
+\r
+               return result;\r
        }\r
 \r
-       /* (non-Javadoc)\r
-        * @see org.collectionspace.services.common.query.IQueryManager#createWhereClauseFromKeywords(java.lang.String)\r
+       /*\r
+        * (non-Javadoc)\r
+        * \r
+        * @see org.collectionspace.services.common.query.IQueryManager#\r
+        * createWhereClauseFromKeywords(java.lang.String)\r
         */\r
-       // TODO handle keywords containing escaped punctuation chars, then we need to qualify the\r
+       // TODO handle keywords containing escaped punctuation chars, then we need\r
+       // to qualify the\r
        // search by matching on the fulltext.simpletext field.\r
-       // TODO handle keywords containing unescaped double quotes by matching the phrase\r
+       // TODO handle keywords containing unescaped double quotes by matching the\r
+       // phrase\r
        // against the fulltext.simpletext field.\r
-       // Both these require using JDBC, since we cannot get to the fulltext table in NXQL\r
-       public String createWhereClauseForPartialMatch(String field, String partialTerm) {\r
-               String trimmed = (partialTerm == null)?"":partialTerm.trim(); \r
+       // Both these require using JDBC, since we cannot get to the fulltext table\r
+       // in NXQL\r
+       @Override\r
+       public String createWhereClauseForPartialMatch(String field,\r
+                       String partialTerm) {\r
+               String trimmed = (partialTerm == null) ? "" : partialTerm.trim();\r
                if (trimmed.isEmpty()) {\r
                        throw new RuntimeException("No partialTerm specified.");\r
                }\r
-               if (field==null || field.isEmpty()) {\r
+               if (field == null || field.isEmpty()) {\r
                        throw new RuntimeException("No match field specified.");\r
                }\r
-               String ptClause = field\r
-                       + getLikeForm()\r
-                       + "'%" + unescapedSingleQuote.matcher(trimmed).replaceAll("\\\\'") + "%'";\r
+               String ptClause = field + getLikeForm() + "'%"\r
+                               + unescapedSingleQuote.matcher(trimmed).replaceAll("\\\\'")\r
+                               + "%'";\r
                return ptClause;\r
        }\r
 \r
        /**\r
         * Creates a filtering where clause from docType, for invocables.\r
         * \r
-        * @param docType the docType\r
+        * @param docType\r
+        *            the docType\r
         * \r
         * @return the string\r
         */\r
-       public String createWhereClauseForInvocableByDocType(String schema, String docType) {\r
-               String trimmed = (docType == null)?"":docType.trim(); \r
+       @Override\r
+       public String createWhereClauseForInvocableByDocType(String schema,\r
+                       String docType) {\r
+               String trimmed = (docType == null) ? "" : docType.trim();\r
                if (trimmed.isEmpty()) {\r
                        throw new RuntimeException("No docType specified.");\r
                }\r
-               if (schema==null || schema.isEmpty()) {\r
+               if (schema == null || schema.isEmpty()) {\r
                        throw new RuntimeException("No match schema specified.");\r
                }\r
-               String wClause = schema+":"+InvocableJAXBSchema.FOR_DOC_TYPES + " = '" + trimmed + "'";\r
+               String wClause = schema + ":" + InvocableJAXBSchema.FOR_DOC_TYPES\r
+                               + " = '" + trimmed + "'";\r
                return wClause;\r
        }\r
-       \r
+\r
        /**\r
         * Creates a filtering where clause from invocation mode, for invocables.\r
         * \r
-        * @param mode the mode\r
+        * @param mode\r
+        *            the mode\r
         * \r
         * @return the string\r
         */\r
+       @Override\r
        public String createWhereClauseForInvocableByMode(String schema, String mode) {\r
-               String trimmed = (mode == null)?"":mode.trim(); \r
+               String trimmed = (mode == null) ? "" : mode.trim();\r
                if (trimmed.isEmpty()) {\r
                        throw new RuntimeException("No docType specified.");\r
                }\r
-               if (schema==null || schema.isEmpty()) {\r
+               if (schema == null || schema.isEmpty()) {\r
                        throw new RuntimeException("No match schema specified.");\r
                }\r
-               String wClause = \r
-                       InvocableUtils.getPropertyNameForInvocationMode(schema, trimmed)\r
-                       + " != 0";\r
+               String wClause = InvocableUtils.getPropertyNameForInvocationMode(\r
+                               schema, trimmed) + " != 0";\r
                return wClause;\r
        }\r
-       \r
-       \r
+\r
        /**\r
         * @param input\r
         * @return true if there were any chars filtered, that will require a backup\r
-        *  qualifying search on the actual text.\r
+        *         qualifying search on the actual text.\r
         */\r
        private boolean filterForFullText(String input) {\r
                boolean fFilteredChars = false;\r
-               \r
+\r
                return fFilteredChars;\r
        }\r
 }\r