View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    *
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.opensource.org/licenses/ecl2.php
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.kuali.rice.kew.docsearch;
18  
19  import java.sql.Timestamp;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.junit.Test;
26  import org.kuali.rice.kew.docsearch.service.DocumentSearchService;
27  import org.kuali.rice.kew.docsearch.xml.StandardGenericXMLSearchableAttribute;
28  import org.kuali.rice.kew.doctype.bo.DocumentType;
29  import org.kuali.rice.kew.doctype.service.DocumentTypeService;
30  import org.kuali.rice.kew.dto.NetworkIdDTO;
31  import org.kuali.rice.kew.dto.WorkflowAttributeDefinitionDTO;
32  import org.kuali.rice.kew.exception.WorkflowException;
33  import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
34  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
35  import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
36  import org.kuali.rice.kew.rule.bo.RuleAttribute;
37  import org.kuali.rice.kew.rule.service.RuleAttributeService;
38  import org.kuali.rice.kew.service.KEWServiceLocator;
39  import org.kuali.rice.kew.service.WorkflowDocument;
40  import org.kuali.rice.kim.bo.Person;
41  import org.kuali.rice.kim.service.KIMServiceLocator;
42  import org.kuali.rice.kns.util.GlobalVariables;
43  
44  
45  /**
46   * Tests the StandardGenericXMLSearchableAttribute.
47   *
48   * KULWF-654: Tests the resolution to this issue by configuring a CustomActionListAttribute as well as a
49   * searchable attribute.
50   */
51  public class SearchableAttributeTest extends DocumentSearchTestBase {
52  
53      protected void loadTestData() throws Exception {
54          loadXmlFile("SearchAttributeConfig.xml");
55          loadXmlFile("SearchableTrimTest.xml");
56      }
57  
58  //    private SearchAttributeCriteriaComponent createSearchAttributeCriteriaComponent(String key,String value,Boolean isLowerBoundValue,DocumentType docType) {
59  //    	String formKey = (isLowerBoundValue == null) ? key : ((isLowerBoundValue != null && isLowerBoundValue.booleanValue()) ? SearchableAttribute.RANGE_LOWER_BOUND_PROPERTY_PREFIX : SearchableAttribute.RANGE_UPPER_BOUND_PROPERTY_PREFIX);
60  //    	String savedKey = key;
61  //    	SearchAttributeCriteriaComponent sacc = new SearchAttributeCriteriaComponent(formKey,value,savedKey);
62  //    	Field field = getFieldByFormKey(docType, formKey);
63  //    	if (field != null) {
64  //        	sacc.setSearchableAttributeValue(DocSearchUtils.getSearchableAttributeValueByDataTypeString(field.getFieldDataType()));
65  //        	sacc.setRangeSearch(field.isMemberOfRange());
66  //        	sacc.setAllowWildcards(field.isAllowingWildcards());
67  //        	sacc.setAutoWildcardBeginning(field.isAutoWildcardAtBeginning());
68  //        	sacc.setAutoWildcardEnd(field.isAutoWildcardAtEnding());
69  //        	sacc.setCaseSensitive(field.isCaseSensitive());
70  //        	sacc.setSearchInclusive(field.isInclusive());
71  //            sacc.setSearchable(field.isSearchable());
72  //            sacc.setCanHoldMultipleValues(Field.MULTI_VALUE_FIELD_TYPES.contains(field.getFieldType()));
73  //    	}
74  //    	return sacc;
75  //    }
76  //
77  //    private Field getFieldByFormKey(DocumentType docType, String formKey) {
78  //    	if (docType == null) {
79  //    		return null;
80  //    	}
81  //		for (SearchableAttribute searchableAttribute : docType.getSearchableAttributes()) {
82  //			for (Row row : searchableAttribute.getSearchingRows()) {
83  //				for (Field field : row.getFields()) {
84  //					if (field.getPropertyName().equals(formKey)) {
85  //						return field;
86  //					}
87  //				}
88  //			}
89  //		}
90  //		return null;
91  //    }
92  
93      /**
94       * This tests the ability to get the searchableAttributeValues directly without going through the document.
95       */
96      @Test public void testSearchableAttributeSearch()throws Exception {
97      	String documentTypeName = "SearchDocType";
98          String userNetworkId = "rkirkend";
99          WorkflowDocument workflowDocument = new WorkflowDocument(getPrincipalId(userNetworkId), documentTypeName);
100         workflowDocument.setTitle("Routing style");
101         workflowDocument.routeDocument("routing this document.");
102 
103         workflowDocument = new WorkflowDocument(getPrincipalId(userNetworkId), workflowDocument.getRouteHeaderId());
104         DocumentRouteHeaderValue doc = KEWServiceLocator.getRouteHeaderService().getRouteHeader(workflowDocument.getRouteHeaderId());
105 
106         /*
107         assertEquals("Wrong number of searchable attributes", 4, doc.getSearchableAttributeValues().size());
108 
109         for (Iterator<SearchableAttributeValue> iter = doc.getSearchableAttributeValues().iterator(); iter.hasNext();) {
110             SearchableAttributeValue attributeValue = iter.next();
111             if (attributeValue instanceof SearchableAttributeStringValue) {
112                 SearchableAttributeStringValue realValue = (SearchableAttributeStringValue) attributeValue;
113 
114                 for(String value:getRouteHeaderService().getSearchableAttributeStringValuesByKey(doc.getRouteHeaderId(), realValue.getSearchableAttributeKey())){
115                 	assertEquals("Assert that the values are the same", value, attributeValue.getSearchableAttributeValue());
116                 }
117 
118             } else if (attributeValue instanceof SearchableAttributeLongValue) {
119                 SearchableAttributeLongValue realValue = (SearchableAttributeLongValue) attributeValue;
120                 for(Long value:getRouteHeaderService().getSearchableAttributeLongValuesByKey(doc.getRouteHeaderId(), realValue.getSearchableAttributeKey())){
121                 	assertEquals("Assert that the values are the same", value, attributeValue.getSearchableAttributeValue());
122                 }
123             } else if (attributeValue instanceof SearchableAttributeFloatValue) {
124                 SearchableAttributeFloatValue realValue = (SearchableAttributeFloatValue) attributeValue;
125                 for(BigDecimal value:getRouteHeaderService().getSearchableAttributeFloatValuesByKey(doc.getRouteHeaderId(), realValue.getSearchableAttributeKey())){
126                 	assertEquals("Assert that the values are the same", value, attributeValue.getSearchableAttributeValue());
127                 }
128 
129             } else if (attributeValue instanceof SearchableAttributeDateTimeValue) {
130                 SearchableAttributeDateTimeValue realValue = (SearchableAttributeDateTimeValue) attributeValue;
131                 assertEquals("The only DateTime attribute that should have been added has key '" + TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY + "'", TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, realValue.getSearchableAttributeKey());
132 
133                 Calendar testDate = Calendar.getInstance();
134                 testDate.setTimeInMillis(realValue.getSearchableAttributeValue().getTime());
135                 testDate.set(Calendar.SECOND, 0);
136                 testDate.set(Calendar.MILLISECOND, 0);
137 
138                 for(Timestamp value:getRouteHeaderService().getSearchableAttributeDateTimeValuesByKey(doc.getRouteHeaderId(), realValue.getSearchableAttributeKey())){
139                 	Calendar attributeDate = Calendar.getInstance();
140                     attributeDate.setTimeInMillis(value.getTime());
141                     attributeDate.set(Calendar.SECOND, 0);
142                     attributeDate.set(Calendar.MILLISECOND, 0);
143 
144                     assertEquals("The month value for the searchable attribute is wrong",testDate.get(Calendar.MONTH),attributeDate.get(Calendar.MONTH));
145                     assertEquals("The date value for the searchable attribute is wrong",testDate.get(Calendar.DATE),attributeDate.get(Calendar.DATE));
146                     assertEquals("The year value for the searchable attribute is wrong",testDate.get(Calendar.YEAR),attributeDate.get(Calendar.YEAR));
147                 }
148 
149             } else {
150                 fail("Searchable Attribute Value base class should be one of the four checked always");
151             }
152         }
153         */
154 
155     }
156 
157     protected RouteHeaderService getRouteHeaderService(){
158     	RouteHeaderService rRet = KEWServiceLocator.getRouteHeaderService();
159     	return rRet;
160     }
161 
162     protected String getPrincipalId(String networkId){
163     	return KIMServiceLocator.getPersonService().getPersonByPrincipalName(networkId).getPrincipalId();
164     }
165 
166     @Test public void testCustomSearchableAttributesWithDataType() throws Exception {
167         String documentTypeName = "SearchDocType";
168     	DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName);
169         String userNetworkId = "rkirkend";
170         WorkflowDocument workflowDocument = new WorkflowDocument(getPrincipalId(userNetworkId), documentTypeName);
171         workflowDocument.setTitle("Routing style");
172         workflowDocument.routeDocument("routing this document.");
173 
174         workflowDocument = new WorkflowDocument(getPrincipalId(userNetworkId), workflowDocument.getRouteHeaderId());
175         DocumentRouteHeaderValue doc = KEWServiceLocator.getRouteHeaderService().getRouteHeader(workflowDocument.getRouteHeaderId());
176         /*assertEquals("Wrong number of searchable attributes", 4, doc.getSearchableAttributeValues().size());
177         for (Iterator<SearchableAttributeValue> iter = doc.getSearchableAttributeValues().iterator(); iter.hasNext();) {
178             SearchableAttributeValue attributeValue = iter.next();
179             if (attributeValue instanceof SearchableAttributeStringValue) {
180                 SearchableAttributeStringValue realValue = (SearchableAttributeStringValue) attributeValue;
181                 assertEquals("The only String attribute that should have been added has key '" + TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY + "'", TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY, realValue.getSearchableAttributeKey());
182                 assertEquals("The only String attribute that should have been added has value '" + TestXMLSearchableAttributeString.SEARCH_STORAGE_VALUE + "'", TestXMLSearchableAttributeString.SEARCH_STORAGE_VALUE, realValue.getSearchableAttributeValue());
183             } else if (attributeValue instanceof SearchableAttributeLongValue) {
184                 SearchableAttributeLongValue realValue = (SearchableAttributeLongValue) attributeValue;
185                 assertEquals("The only Long attribute that should have been added has key '" + TestXMLSearchableAttributeLong.SEARCH_STORAGE_KEY + "'", TestXMLSearchableAttributeLong.SEARCH_STORAGE_KEY, realValue.getSearchableAttributeKey());
186                 assertEquals("The only Long attribute that should have been added has value '" + TestXMLSearchableAttributeLong.SEARCH_STORAGE_VALUE + "'", TestXMLSearchableAttributeLong.SEARCH_STORAGE_VALUE, realValue.getSearchableAttributeValue());
187             } else if (attributeValue instanceof SearchableAttributeFloatValue) {
188                 SearchableAttributeFloatValue realValue = (SearchableAttributeFloatValue) attributeValue;
189                 assertEquals("The only Float attribute that should have been added has key '" + TestXMLSearchableAttributeFloat.SEARCH_STORAGE_KEY + "'", TestXMLSearchableAttributeFloat.SEARCH_STORAGE_KEY, realValue.getSearchableAttributeKey());
190                 assertTrue("The only Float attribute that should have been added has value '" + TestXMLSearchableAttributeFloat.SEARCH_STORAGE_VALUE + "'", 0 == TestXMLSearchableAttributeFloat.SEARCH_STORAGE_VALUE.compareTo(realValue.getSearchableAttributeValue()));
191             } else if (attributeValue instanceof SearchableAttributeDateTimeValue) {
192                 SearchableAttributeDateTimeValue realValue = (SearchableAttributeDateTimeValue) attributeValue;
193                 assertEquals("The only DateTime attribute that should have been added has key '" + TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY + "'", TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, realValue.getSearchableAttributeKey());
194                 Calendar testDate = Calendar.getInstance();
195                 testDate.setTimeInMillis(TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_VALUE_IN_MILLS);
196                 testDate.set(Calendar.SECOND, 0);
197                 testDate.set(Calendar.MILLISECOND, 0);
198                 Calendar attributeDate = Calendar.getInstance();
199                 attributeDate.setTimeInMillis(realValue.getSearchableAttributeValue().getTime());
200                 attributeDate.set(Calendar.SECOND, 0);
201                 attributeDate.set(Calendar.MILLISECOND, 0);
202                 assertEquals("The month value for the searchable attribute is wrong",testDate.get(Calendar.MONTH),attributeDate.get(Calendar.MONTH));
203                 assertEquals("The date value for the searchable attribute is wrong",testDate.get(Calendar.DATE),attributeDate.get(Calendar.DATE));
204                 assertEquals("The year value for the searchable attribute is wrong",testDate.get(Calendar.YEAR),attributeDate.get(Calendar.YEAR));
205             } else {
206                 fail("Searchable Attribute Value base class should be one of the four checked always");
207             }
208         }
209 		*/
210 
211         DocumentSearchService docSearchService = (DocumentSearchService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_SEARCH_SERVICE);
212         Person user = KIMServiceLocator.getPersonService().getPersonByPrincipalName(userNetworkId);
213 
214 
215         DocSearchCriteriaDTO criteria = new DocSearchCriteriaDTO();
216         criteria.setDocTypeFullName(documentTypeName);
217         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY, TestXMLSearchableAttributeString.SEARCH_STORAGE_VALUE, null, docType));
218         DocumentSearchResultComponents result = docSearchService.getList(user.getPrincipalId(), criteria);
219         List searchResults = result.getSearchResults();
220 
221         assertEquals("Search results should have one document.", 1, searchResults.size());
222 
223         criteria = new DocSearchCriteriaDTO();
224         criteria.setDocTypeFullName(documentTypeName);
225         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY, "fred", null, docType));
226         result = docSearchService.getList(user.getPrincipalId(), criteria);
227         searchResults = result.getSearchResults();
228 
229         assertEquals("Search results should be empty.", 0, searchResults.size());
230 
231         criteria = new DocSearchCriteriaDTO();
232         criteria.setDocTypeFullName(documentTypeName);
233         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent("fakeproperty", "doesntexist", null, docType));
234         try {
235             result = docSearchService.getList(user.getPrincipalId(), criteria);
236             fail("Search results should be throwing a validation exception for use of non-existant searchable attribute");
237         } catch (WorkflowServiceErrorException e) {}
238 
239         criteria = new DocSearchCriteriaDTO();
240         criteria.setDocTypeFullName(documentTypeName);
241         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeLong.SEARCH_STORAGE_KEY, TestXMLSearchableAttributeLong.SEARCH_STORAGE_VALUE.toString(), null, docType));
242         result = docSearchService.getList(user.getPrincipalId(), criteria);
243         searchResults = result.getSearchResults();
244         assertEquals("Search results should have one document.", 1, searchResults.size());
245 
246         criteria = new DocSearchCriteriaDTO();
247         criteria.setDocTypeFullName(documentTypeName);
248         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeLong.SEARCH_STORAGE_KEY, "1111111", null, docType));
249         result = docSearchService.getList(user.getPrincipalId(), criteria);
250         searchResults = result.getSearchResults();
251         assertEquals("Search results should be empty.", 0, searchResults.size());
252 
253         criteria = new DocSearchCriteriaDTO();
254         criteria.setDocTypeFullName(documentTypeName);
255         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent("fakeymcfakefake", "99999999", null, docType));
256         try {
257             result = docSearchService.getList(user.getPrincipalId(), criteria);
258             fail("Search results should be throwing a validation exception for use of non-existant searchable attribute");
259         } catch (WorkflowServiceErrorException e) {}
260 
261         criteria = new DocSearchCriteriaDTO();
262         criteria.setDocTypeFullName(documentTypeName);
263         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeFloat.SEARCH_STORAGE_KEY, TestXMLSearchableAttributeFloat.SEARCH_STORAGE_VALUE.toString(), null, docType));
264         result = docSearchService.getList(user.getPrincipalId(), criteria);
265         searchResults = result.getSearchResults();
266         assertEquals("Search results should have one document.", 1, searchResults.size());
267 
268         criteria = new DocSearchCriteriaDTO();
269         criteria.setDocTypeFullName(documentTypeName);
270         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeFloat.SEARCH_STORAGE_KEY, "215.3548", null, docType));
271         result = docSearchService.getList(user.getPrincipalId(), criteria);
272         searchResults = result.getSearchResults();
273         assertEquals("Search results should be empty.", 0, searchResults.size());
274 
275         criteria = new DocSearchCriteriaDTO();
276         criteria.setDocTypeFullName(documentTypeName);
277         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent("fakeylostington", "9999.9999", null, docType));
278         try {
279             result = docSearchService.getList(user.getPrincipalId(), criteria);
280             fail("Search results should be throwing a validation exception for use of non-existant searchable attribute");
281         } catch (WorkflowServiceErrorException e) {}
282 
283         criteria = new DocSearchCriteriaDTO();
284         criteria.setDocTypeFullName(documentTypeName);
285         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, DocSearchUtils.getDisplayValueWithDateOnly(new Timestamp(TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_VALUE_IN_MILLS)), null, docType));
286         result = docSearchService.getList(user.getPrincipalId(), criteria);
287         searchResults = result.getSearchResults();
288         assertEquals("Search results should have one document.", 1, searchResults.size());
289 
290         criteria = new DocSearchCriteriaDTO();
291         criteria.setDocTypeFullName(documentTypeName);
292         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent(TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, "07/06/1979", null, docType));
293         result = docSearchService.getList(user.getPrincipalId(), criteria);
294         searchResults = result.getSearchResults();
295         assertEquals("Search results should be empty.", 0, searchResults.size());
296 
297         criteria = new DocSearchCriteriaDTO();
298         criteria.setDocTypeFullName(documentTypeName);
299         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent("lastingsfakerson", "07/06/2007", null, docType));
300         try {
301             result = docSearchService.getList(user.getPrincipalId(), criteria);
302             fail("Search results should be throwing a validation exception for use of non-existant searchable attribute");
303         } catch (WorkflowServiceErrorException e) {}
304     }
305 
306     /**
307      * Tests searching documents with searchable attributes
308      * @throws WorkflowException
309      */
310     @Test public void testSearchAttributesAcrossDocumentTypeVersions() throws Exception {
311         // first test searching for an initial version of the doc which does not have a searchable attribute
312         loadXmlFile("testdoc0.xml");
313 
314         String documentTypeName = "SearchDoc";
315         WorkflowDocument doc = new WorkflowDocument(new NetworkIdDTO("arh14"), documentTypeName);
316         DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName);
317         doc.routeDocument("routing");
318 
319         DocumentSearchService docSearchService = (DocumentSearchService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_SEARCH_SERVICE);
320 
321         DocSearchCriteriaDTO criteria = new DocSearchCriteriaDTO();
322         criteria.setDocTypeFullName(documentTypeName);
323         criteria.setFromDateCreated("01/01/2004");
324 
325         Person user = KIMServiceLocator.getPersonService().getPersonByPrincipalName("arh14");
326         DocumentSearchResultComponents result = docSearchService.getList(user.getPrincipalId(), criteria);
327         assertEquals(1, result.getSearchResults().size());
328 
329         // now upload the new version with a searchable attribute
330         loadXmlFile("testdoc1.xml");
331         docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName);
332 
333         // route a new doc
334         doc = new WorkflowDocument(new NetworkIdDTO("arh14"), documentTypeName);
335         doc.routeDocument("routing");
336 
337         // with no attribute criteria, both docs should be found
338         criteria = new DocSearchCriteriaDTO();
339         criteria.setDocTypeFullName(documentTypeName);
340         criteria.setFromDateCreated("01/01/2004");
341 
342         result = docSearchService.getList(user.getPrincipalId(), criteria);
343         assertEquals(2, result.getSearchResults().size());
344 
345         // search with specific SearchableAttribute value
346         criteria = new DocSearchCriteriaDTO();
347         criteria.setDocTypeFullName(documentTypeName);
348         criteria.setFromDateCreated("01/01/2004");
349         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent("MockSearchableAttributeKey", "Mock Searchable Attribute", null, docType));
350         criteria.getSearchableAttributes().set(0, createSearchAttributeCriteriaComponent("MockSearchableAttributeKey", "MockSearchableAttributeValue", null, docType));
351 
352         result = docSearchService.getList(user.getPrincipalId(), criteria);
353         assertEquals(1, result.getSearchResults().size());
354 
355         // search with any SearchableAttribute value
356         criteria = new DocSearchCriteriaDTO();
357         criteria.setDocTypeFullName(documentTypeName);
358         criteria.setFromDateCreated("01/01/2004");
359         criteria.addSearchableAttribute(createSearchAttributeCriteriaComponent("MockSearchableAttributeKey", "Mock Searchable Attribute", null, docType));
360         criteria.getSearchableAttributes().set(0, createSearchAttributeCriteriaComponent("MockSearchableAttributeKey", "", null, docType));
361 
362         result = docSearchService.getList(user.getPrincipalId(), criteria);
363         // should return two because an empty value above will return any value of the 'MockSearchableAttributeKey' key including the previous document
364         // that doesn't even have a record of that field being saved to the database
365         assertEquals(2, result.getSearchResults().size());
366     }
367 
368     /**
369      * Tests the usage of wildcards on searchable attributes of varying data types.
370      * Note that the bounds of ".."-related search expressions will not throw an exception if the lower bound is greater than the upper bound;
371      * instead, such an expression will simply return zero results.
372      * @throws Exception
373      */
374     @Test public void testWildcardsOnSearchableAttributes() throws Exception {
375         String documentTypeName = "WildcardTestDocType";
376     	DocumentType docType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
377         String principalName = "rkirkend";
378         String principalId = KIMServiceLocator.getPersonService().getPersonByPrincipalName(principalName).getPrincipalId();
379         String[][] searchableAttributeValuesAsStrings = { {"testString", "9984", "38.1357", "06/24/2009"},
380         		{"anotherStr", "33", "80000.65432", "07/08/2010"}, {"MoreText", "432", "-0.765", "12/12/2012"} };
381 
382         // Route some documents containing the searchable attribute values given by the above array.
383         for (int i = 0; i < searchableAttributeValuesAsStrings.length; i++) {
384         	WorkflowDocument workflowDocument = new WorkflowDocument(principalId, documentTypeName);
385 
386         	// Add the string searchable attribute.
387         	WorkflowAttributeDefinitionDTO wcStringXMLDef = new WorkflowAttributeDefinitionDTO("XMLSearchableAttributeWildcardString");
388         	wcStringXMLDef.addProperty("xmlSearchableAttributeWildcardString", searchableAttributeValuesAsStrings[i][0]);
389         	workflowDocument.addSearchableDefinition(wcStringXMLDef);
390         	// Add the long searchable attribute.
391         	WorkflowAttributeDefinitionDTO wcLongXMLDef = new WorkflowAttributeDefinitionDTO("XMLSearchableAttributeWildcardLong");
392         	wcLongXMLDef.addProperty("xmlSearchableAttributeWildcardLong", searchableAttributeValuesAsStrings[i][1]);
393         	workflowDocument.addSearchableDefinition(wcLongXMLDef);
394         	// Add the float searchable attribute.
395         	WorkflowAttributeDefinitionDTO wcFloatXMLDef = new WorkflowAttributeDefinitionDTO("XMLSearchableAttributeWildcardFloat");
396         	wcFloatXMLDef.addProperty("xmlSearchableAttributeWildcardFloat", searchableAttributeValuesAsStrings[i][2]);
397         	workflowDocument.addSearchableDefinition(wcFloatXMLDef);
398         	// Add the datetime searchable attribute.
399         	WorkflowAttributeDefinitionDTO wcDatetimeXMLDef = new WorkflowAttributeDefinitionDTO("XMLSearchableAttributeWildcardDatetime");
400         	wcDatetimeXMLDef.addProperty("xmlSearchableAttributeWildcardDatetime", searchableAttributeValuesAsStrings[i][3]);
401         	workflowDocument.addSearchableDefinition(wcDatetimeXMLDef);
402 
403         	workflowDocument.setTitle("Search Def Test Doc " + i);
404         	workflowDocument.routeDocument("routing search def doc " + i);
405         }
406 
407         // Ensure that wildcards work on searchable string attributes. Note that this search should be case-insensitive by default.
408         // Also note that this should be the only case where the string-specific wildcards ("!", "?", and "*") should be working, unless
409         // they are being used in a range expression.
410         assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardString",
411         		new String[]  {"TESTSTRING|moretext", "!MoreText"   , "!anotherStr!testString", "!anotherStr&&!MoreText"  , "!SomeString"      ,
412         					"*str*"                 , "More????"    , "*e*n?"                 , "???String"               , "*te*&&!????String", "!test??????"       , "anotherStr..MoreText",
413         					"testString..MoreText"  , ">=testString", "<=anotherStr|>MoreText", "<testString&&!anotherStr", ">abc"             , "<anotherOne&&>text",
414         					">More????"             , "<*test*"},
415         			new int[] {2                    , 2             , 1                       , 1                         , 3                  ,
416         					2                       , 1             , 1                       , 0                         , 1                  , 2                   , 2 /*1*/               ,
417         					0                       , 1             , 2                       , 1                         , 3                  , 0                   ,
418         					2                       , 2});
419         
420         // ensure multiple values work
421         assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardString",
422         		new String[][] { {"testString"}, {"anotherStr"}, {"MoreText"}, {"testString", "anotherStr"}, {"testString", "MoreText"}, {"anotherStr", "MoreText"}, {"testString", "anotherStr", "MoreText"}, {"monkey"}, {"monkey", "giraffe"}, {"monkey", "testString"} },
423         			new int[]  {  1,              1,              1,            2,                            2,                          2,                          3,                                        0,          0,                     1                       });
424 
425         // Ensure that wildcards work on searchable long attributes, and ensure the string-specific wildcards are not being utilized.
426         assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardLong",
427         		new String[]  {"99??", "*2"       , "!33"         , "<9984", ">432", "<=33", ">=432", ">33&&<9984", "<=100000&&>=20", ">9984&&<33", "432..9984",
428         					"9999..1", "<432|>432", ">=9000|<=100", "!", ">-76"},
429         			new int[] {-1     , -1          , 1             , 2      , 1     , 1     , 2      , 1           , 3               , 0           , 2 /*1*/    ,
430         					0        , 2          , 2             , -1 , 3});
431         
432         // ensure multiple values work
433         assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardLong",
434         		new String[][] { {"9984"}, {"33"}, {"432"}, {"9984", "33"}, {"9984", "432"}, {"33", "432"}, {"9984", "33", "432"}, {"7"}, {"7", "4488"}, {"7", "9984"} },
435         			new int[]  {  1,              1,              1,            2,                            2,                          2,                          3,                                        0,          0,                     1                       });
436 
437         // Ensure that wildcards work on searchable float attributes, and ensure the string-specific wildcards are not being utilized.
438         assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardFloat",
439         		new String[]  {"38.1???", "!-0.765", "*80*"                , "<80000.65432"   , ">0"                  , "<=-0.765", ">=38.1357", "<38.1358", "<-0.5|>0.5", ">=-0.765&&<=-0.765", ">38.1357&&<80000.65432",
440         					"-50..50"   , "100..10", "<=38.1357|>=38.1357" , ">123.4567|<0.11", "-1.1..38.1357&&<3.3"},
441         			new int[] {-1        , 1        , -1                     , 2                , 2                     , 1         , 2          , 2         , 3           , 1                   , 0                       ,
442         					2           , 0        , 3                     , 2                , 1});
443         
444         // ensure multiple values work
445         assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardFloat",
446         		new String[][] { {"38.1357"}, {"80000.65432"}, {"-0.765"}, {"38.1357", "80000.65432"}, {"38.1357", "-0.765"}, {"80000.65432", "-0.765"}, {"38.1357", "80000.65432", "-0.765"}, {"3.1415928"}, {"3.1415928", "4488.0"}, {"3.1415928", "38.1357"} },
447         			new int[]  {  1,              1,              1,            2,                            2,                          2,                          3,                                        0,          0,                     1                       });
448 
449 
450         // Ensure that wildcards work on searchable datetime attributes, and ensure the string-specific wildcards are not being utilized.
451         /* 06/24/2009, 07/08/2010, 12/12/2012 */
452         assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardDatetime",
453         		new String[]  {"??/??/20??"            , "12/12/20*"               , "!07/08/2010"           , ">06/24/2009", "<07/08/2010", ">=12/12/2012", "<=05/06/2011", ">06/24/2009&&<=07/08/2010",
454         					">=01/01/2001&&<06/24/2009", "11/29/1990..12/31/2009"  , "12/13/2100..08/09/1997",
455         					"<06/24/2009|>=12/12/2012" , "<=06/24/2009|>07/08/2010", ">02/31/2011"},
456         			new int[] {-1                      , -1                         , -1                      , 2            , 1            , 1             , 2             , 1                          ,
457         					0                          , 1                         , 0                       ,
458         					1                          , 2                         , -1});
459         
460         // ensure multiple values work
461         assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardDatetime",
462         		new String[][] { {"06/24/2009"}, {"07/08/2010"}, {"12/12/2012"}, {"06/24/2009", "07/08/2010"}, {"06/24/2009", "12/12/2012"}, {"07/08/2010", "12/12/2012"}, {"06/24/2009", "07/08/2010", "12/12/2012"}, {"12/20/2012"}, {"12/20/2012", "11/09/2009"}, {"12/20/2012", "12/12/2012"} },
463         			new int[]  {  1,              1,              1,            2,                            2,                          2,                          3,                                        0,          0,                     1                       });
464 
465     }
466 
467     /**
468      * A convenience method for testing wildcards on searchable attributes.
469      *
470      * @param docType The document type containing the attributes.
471      * @param principalId The ID of the user performing the search.
472      * @param fieldDefKey The name of the field given by the field definition on the searchable attribute.
473      * @param searchValues The wildcard-filled search strings to test.
474      * @param resultSizes The number of expected documents to be returned by the search; use -1 to indicate that an error should have occurred.
475      * @throws Exception
476      */
477     private void assertSearchableAttributeWildcardsWork(DocumentType docType, String principalId, String fieldDefKey, String[] searchValues,
478     		int[] resultSizes) throws Exception {
479     	DocSearchCriteriaDTO criteria = null;
480         DocumentSearchResultComponents result = null;
481         List<DocumentSearchResult> searchResults = null;
482         DocumentSearchService docSearchService = KEWServiceLocator.getDocumentSearchService();
483         for (int i = 0; i < resultSizes.length; i++) {
484         	criteria = new DocSearchCriteriaDTO();
485         	criteria.setDocTypeFullName(docType.getName());
486         	criteria.addSearchableAttribute(this.createSearchAttributeCriteriaComponent(fieldDefKey, searchValues[i], docType));
487         	try {
488         		result = docSearchService.getList(principalId, criteria);
489         		searchResults = result.getSearchResults();
490         		if (resultSizes[i] < 0) {
491         			fail(fieldDefKey + "'s search at loop index " + i + " should have thrown an exception");
492         		}
493         		if(resultSizes[i] != searchResults.size()){
494         			assertEquals(fieldDefKey + "'s search results at loop index " + i + " returned the wrong number of documents.", resultSizes[i], searchResults.size());
495         		}
496         	} catch (Exception ex) {
497         		if (resultSizes[i] >= 0) {
498         			fail(fieldDefKey + "'s search at loop index " + i + " should not have thrown an exception");
499         		}
500         	}
501         	GlobalVariables.clear();
502         }
503     }
504     
505     /**
506      * A convenience method for testing multiple value fields on searchable attributes.
507      *
508      * @param docType The document type containing the attributes.
509      * @param principalId The ID of the user performing the search.
510      * @param fieldDefKey The name of the field given by the field definition on the searchable attribute.
511      * @param searchValues The wildcard-filled search strings to test.
512      * @param resultSizes The number of expected documents to be returned by the search; use -1 to indicate that an error should have occurred.
513      * @throws Exception
514      */
515     private void assertSearchableAttributeMultiplesWork(DocumentType docType, String principalId, String fieldDefKey, String[][] searchValues,
516     		int[] resultSizes) throws Exception {
517     	DocSearchCriteriaDTO criteria = null;
518         DocumentSearchResultComponents result = null;
519         List<DocumentSearchResult> searchResults = null;
520         DocumentSearchService docSearchService = KEWServiceLocator.getDocumentSearchService();
521         for (int i = 0; i < resultSizes.length; i++) {
522         	criteria = new DocSearchCriteriaDTO();
523         	criteria.setDocTypeFullName(docType.getName());
524         	criteria.addSearchableAttribute(this.createSearchAttributeCriteriaComponent(fieldDefKey, searchValues[i], docType));
525         	try {
526         		result = docSearchService.getList(principalId, criteria);
527         		searchResults = result.getSearchResults();
528         		if (resultSizes[i] < 0) {
529         			fail(fieldDefKey + "'s search at loop index " + i + " should have thrown an exception");
530         		}
531         		if(resultSizes[i] != searchResults.size()){
532         			assertEquals(fieldDefKey + "'s search results at loop index " + i + " returned the wrong number of documents.", resultSizes[i], searchResults.size());
533         		}
534         	} catch (Exception ex) {
535         		if (resultSizes[i] >= 0) {
536         			fail(fieldDefKey + "'s search at loop index " + i + " should not have thrown an exception");
537         		}
538         	}
539         	GlobalVariables.clear();
540         }
541     }
542     
543     
544     
545     /**
546      * Per KULRICE-3681, tests that StandardGenericXMLSearchableAttribute throws no cast class exception when it shouldn't
547      */
548     @Test
549     public void testValidateUserSearchInputsNoCast() {
550     	StandardGenericXMLSearchableAttribute searchableAttribute = new StandardGenericXMLSearchableAttribute();
551     	final RuleAttributeService ruleAttributeService = KEWServiceLocator.getRuleAttributeService();
552     	searchableAttribute.setRuleAttribute(ruleAttributeService.findByName("SearchableAttributeVisible"));
553     	
554     	Map<Object, Object> simpleParamMap = new HashMap<Object, Object>();
555     	simpleParamMap.put("givenname", "test");
556     	List errors = new ArrayList();
557     	Exception caughtException = null;
558     	try {
559     		errors = searchableAttribute.validateUserSearchInputs(simpleParamMap, null);
560     	} catch (RuntimeException re) {
561     		caughtException = re;
562     	}
563     	assertNull("Found exception "+caughtException, caughtException);
564     	assertTrue("Found errors "+errors, (errors.size() == 0));
565     	
566     	Map<Object, Object> listParamMap = new HashMap<Object, Object>();
567     	List<String> multipleValues = new ArrayList<String>();
568     	multipleValues.add("testone");
569     	multipleValues.add("testtwo");
570     	listParamMap.put("givenname", multipleValues);
571     	errors = new ArrayList();
572     	caughtException = null;
573     	try {
574     		errors = searchableAttribute.validateUserSearchInputs(listParamMap, null);
575     	} catch (RuntimeException re) {
576     		caughtException = re;
577     	}
578     	assertNull("Found exception "+caughtException, caughtException);
579     	assertTrue("Found errors "+errors, (errors.size() == 0));
580     	
581     	Map<Object, Object> badParamMap = new HashMap<Object, Object>();
582     	badParamMap.put("givenname", new Integer(7));
583     	errors = new ArrayList();
584     	caughtException = null;
585     	try {
586     		errors = searchableAttribute.validateUserSearchInputs(badParamMap, null);
587     	} catch (RuntimeException re) {
588     		caughtException = re;
589     	}
590     	assertNotNull("Found exception "+caughtException, caughtException);
591     	assertTrue("Found errors "+errors, (errors.size() == 0));
592     }
593     
594     @Test
595     public void testSearchableAttributeTrim() {
596     	RuleAttribute trimAttribute = KEWServiceLocator.getRuleAttributeService().findByName("TrimSearchableAttribute");
597     	assert(trimAttribute.getName().equals("TrimSearchableAttribute"));
598     	assert(trimAttribute.getClassName().equals("org.kuali.rice.kew.docsearch.xml.StandardGenericXMLSearchableAttribute"));
599     	assert(trimAttribute.getLabel().equals("Unit111"));
600     	assert(trimAttribute.getType().equals("SearchableXmlAttribute"));
601     	assert(trimAttribute.getDescription().equals("Unit111"));
602     	assert(trimAttribute.getServiceNamespace().equals("NSTrimSearchableTest"));
603     	//System.out.println(trimAttribute.getName());
604     }
605 }