View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.impl.document.search;
17  
18  import org.apache.commons.beanutils.PropertyUtils;
19  import org.apache.commons.lang.ObjectUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.log4j.Logger;
22  import org.joda.time.DateTime;
23  import org.kuali.rice.kew.api.KEWPropertyConstants;
24  import org.kuali.rice.kew.api.document.DocumentStatus;
25  import org.kuali.rice.kew.api.document.DocumentStatusCategory;
26  import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
27  import org.kuali.rice.kew.api.document.search.RouteNodeLookupLogic;
28  import org.kuali.rice.kew.docsearch.DocumentSearchInternalUtils;
29  import org.kuali.rice.kew.api.KewApiConstants;
30  
31  import java.lang.reflect.InvocationTargetException;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collection;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  
41  /**
42   * Reference implementation of {@code DocumentSearchCriteriaTranslator}.
43   *
44   * @author Kuali Rice Team (rice.collab@kuali.org)
45   */
46  public class DocumentSearchCriteriaTranslatorImpl implements DocumentSearchCriteriaTranslator {
47  
48      private static final Logger LOG = Logger.getLogger(DocumentSearchCriteriaTranslatorImpl.class);
49  
50      private static final String DOCUMENT_STATUSES = "documentStatuses";
51      private static final String ROUTE_NODE_LOOKUP_LOGIC = "routeNodeLookupLogic";
52  
53      /**
54       * Fields which translate directory from criteria strings to properties on the DocumentSearchCriteria.
55       */
56      private static final String[] DIRECT_TRANSLATE_FIELD_NAMES = {
57              "documentId",
58              "applicationDocumentId",
59              "applicationDocumentStatus",
60              "initiatorPrincipalName",
61              "viewerPrincipalName",
62              "approverPrincipalName",
63              "routeNodeName",
64              "documentTypeName",
65              "saveName"
66      };
67      private static final Set<String> DIRECT_TRANSLATE_FIELD_NAMES_SET =
68              new HashSet<String>(Arrays.asList(DIRECT_TRANSLATE_FIELD_NAMES));
69  
70      private static final String[] DATE_RANGE_TRANSLATE_FIELD_NAMES = {
71              "dateCreated",
72              "dateLastModified",
73              "dateApproved",
74              "dateFinalized"
75      };
76      private static final Set<String> DATE_RANGE_TRANSLATE_FIELD_NAMES_SET =
77              new HashSet<String>(Arrays.asList(DATE_RANGE_TRANSLATE_FIELD_NAMES));
78  
79      @Override
80      public DocumentSearchCriteria translateFieldsToCriteria(Map<String, String> fieldValues) {
81  
82          DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
83          for (Map.Entry<String, String> field : fieldValues.entrySet()) {
84              try {
85                  if (StringUtils.isNotBlank(field.getValue())) {
86                      if (DIRECT_TRANSLATE_FIELD_NAMES_SET.contains(field.getKey())) {
87                          PropertyUtils.setNestedProperty(criteria, field.getKey(), field.getValue());
88                      } else if (DATE_RANGE_TRANSLATE_FIELD_NAMES_SET.contains(field.getKey())) {
89                          applyDateRangeField(criteria, field.getKey(), field.getValue());
90                      } else if (field.getKey().startsWith(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX)) {
91                          String documentAttributeName = field.getKey().substring(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX.length());
92                          applyDocumentAttribute(criteria, documentAttributeName, field.getValue());
93                      }
94  
95                  }
96              } catch (Exception e) {
97                  throw new IllegalStateException("Failed to set document search criteria field: " + field.getKey(), e);
98              }
99          }
100 
101         String routeNodeLookupLogic = fieldValues.get(ROUTE_NODE_LOOKUP_LOGIC);
102         if (StringUtils.isNotBlank(routeNodeLookupLogic)) {
103             criteria.setRouteNodeLookupLogic(RouteNodeLookupLogic.valueOf(routeNodeLookupLogic));
104         }
105 
106         String documentStatusesValue = fieldValues.get(KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_STATUS_CODE);
107         if (StringUtils.isNotBlank(documentStatusesValue)) {
108             String[] documentStatuses = documentStatusesValue.split(",");
109             for (String documentStatus : documentStatuses) {
110                 if (documentStatus.startsWith("category:")) {
111                     String categoryCode = StringUtils.remove(documentStatus, "category:");
112                     criteria.getDocumentStatusCategories().add(DocumentStatusCategory.fromCode(categoryCode));
113                 } else {
114                     criteria.getDocumentStatuses().add(DocumentStatus.fromCode(documentStatus));
115                 }
116             }
117         }
118 
119         return criteria.build();
120     }
121 
122     /**
123      * Converts the DocumentSearchCriteria to a Map of values that can be applied to the Lookup form fields.
124      * @param criteria the criteria to translate
125      * @return a Map of values that can be applied to the Lookup form fields.
126      */
127     public Map<String, String[]> translateCriteriaToFields(DocumentSearchCriteria criteria) {
128         Map<String, String[]> values = new HashMap<String, String[]>();
129 
130         for (String property: DIRECT_TRANSLATE_FIELD_NAMES) {
131             convertCriteriaPropertyToField(criteria, property, values);
132         }
133 
134         for (String property: DATE_RANGE_TRANSLATE_FIELD_NAMES) {
135             convertCriteriaPropertyToField(criteria, property + "From", values);
136             convertCriteriaPropertyToField(criteria, property + "To", values);
137         }
138 
139         Map<String, List<String>> docAttrValues = criteria.getDocumentAttributeValues();
140         for (Map.Entry<String, List<String>> entry: docAttrValues.entrySet()) {
141             values.put(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX + entry.getKey(), entry.getValue().toArray(new String[0]));
142         }
143 
144         RouteNodeLookupLogic lookupLogic = criteria.getRouteNodeLookupLogic();
145         if (lookupLogic != null) {
146             values.put(ROUTE_NODE_LOOKUP_LOGIC, new String[]{lookupLogic.name()});
147         }
148 
149         Collection<String> statuses = new ArrayList<String>();
150         for (DocumentStatus status: criteria.getDocumentStatuses()) {
151             statuses.add(status.getCode());
152         }
153         for (DocumentStatusCategory category: criteria.getDocumentStatusCategories()) {
154             statuses.add("category:" + category.getCode());
155         }
156         values.put(KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_STATUS_CODE, statuses.toArray(new String[0]));
157 
158         return values;
159     }
160 
161     /**
162      * Looks up a property on the criteria object and sets it as a key/value pair in the values Map
163      * @param criteria the DocumentSearchCriteria
164      * @param property the DocumentSearchCriteria property name
165      * @param values the map of values to update
166      */
167     protected static void convertCriteriaPropertyToField(DocumentSearchCriteria criteria, String property, Map<String, String[]> values) {
168         try {
169             Object val = PropertyUtils.getProperty(criteria, property);
170             if (val != null) {
171                 values.put(property, new String[] { ObjectUtils.toString(val) });
172             }
173         } catch (NoSuchMethodException nsme) {
174             LOG.error("Error reading property '" + property + "' of criteria", nsme);
175         } catch (InvocationTargetException ite) {
176             LOG.error("Error reading property '" + property + "' of criteria", ite);
177         } catch (IllegalAccessException iae) {
178             LOG.error("Error reading property '" + property + "' of criteria", iae);
179 
180         }
181     }
182 
183     protected void applyDateRangeField(DocumentSearchCriteria.Builder criteria, String fieldName, String fieldValue) throws Exception {
184         DateTime lowerDateTime = DocumentSearchInternalUtils.getLowerDateTimeBound(fieldValue);
185         DateTime upperDateTime = DocumentSearchInternalUtils.getUpperDateTimeBound(fieldValue);
186         if (lowerDateTime != null) {
187             PropertyUtils.setNestedProperty(criteria, fieldName + "From", lowerDateTime);
188         }
189         if (upperDateTime != null) {
190             PropertyUtils.setNestedProperty(criteria, fieldName + "To", upperDateTime);
191         }
192     }
193 
194     protected void applyDocumentAttribute(DocumentSearchCriteria.Builder criteria, String documentAttributeName, String attributeValue) {
195         criteria.addDocumentAttributeValue(documentAttributeName, attributeValue);
196     }
197 
198 }