001/**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kew.docsearch;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.kuali.rice.core.api.uif.RemotableAttributeError;
020import org.kuali.rice.kew.api.WorkflowRuntimeException;
021import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
022import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
023import org.kuali.rice.kew.framework.document.search.AttributeFields;
024import org.kuali.rice.kew.framework.document.search.DocumentSearchCriteriaConfiguration;
025import org.kuali.rice.kew.framework.document.search.DocumentSearchResultSetConfiguration;
026import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValues;
027import org.kuali.rice.kew.doctype.DocumentTypeAttributeBo;
028import org.kuali.rice.kew.doctype.bo.DocumentType;
029import org.kuali.rice.kew.framework.KewFrameworkServiceLocator;
030import org.kuali.rice.kew.framework.document.search.DocumentSearchCustomization;
031import org.kuali.rice.kew.framework.document.search.DocumentSearchCustomizationHandlerService;
032import org.kuali.rice.kew.rule.bo.RuleAttribute;
033
034import java.util.ArrayList;
035import java.util.LinkedHashMap;
036import java.util.List;
037
038/**
039 * Reference implementation of {@code DocumentSearchCustomizationMediator}.
040 *
041 * @author Kuali Rice Team (rice.collab@kuali.org)
042 */
043public class DocumentSearchCustomizationMediatorImpl implements DocumentSearchCustomizationMediator {
044
045    @Override
046    public DocumentSearchCriteriaConfiguration getDocumentSearchCriteriaConfiguration(DocumentType documentType) {
047
048        List<DocumentTypeAttributeBo> searchableAttributes = documentType.getSearchableAttributes();
049
050        // This first map is used to partition our attributes by application id.  It maps an application id to the
051        // list of searchable attribute names that are associated with that application id.  Note that 'null' is a
052        // valid key in this map for those attributes that have no application id.
053        LinkedHashMap<String, List<String>> applicationIdToAttributeNameMap = new LinkedHashMap<String, List<String>>();
054
055        // This second map is used to map the searchable attribute name to the List of RemotableAttributeFields
056        // that are returned by invocations of it's getSearchFields method.  This is a LinkedHashMap because it
057        // preserves the order of the keys as they are entered.  This allows us to return attribute fields in the
058        // proper order as defined by the order of searchable attributes on the doc type, despite the partitioning
059        // of our attributes by application id.
060        LinkedHashMap<String, AttributeFields> orderedSearchFieldMap = new LinkedHashMap<String, AttributeFields>();
061        LinkedHashMap<String, AttributeFields> orderedResultSetFieldMap = new LinkedHashMap<String, AttributeFields>();
062
063        for (DocumentTypeAttributeBo searchableAttribute : searchableAttributes) {
064            RuleAttribute ruleAttribute = searchableAttribute.getRuleAttribute();
065            String attributeName = ruleAttribute.getName();
066            String applicationId = ruleAttribute.getApplicationId();
067            if (!applicationIdToAttributeNameMap.containsKey(applicationId)) {
068                applicationIdToAttributeNameMap.put(applicationId, new ArrayList<String>());
069            }
070            applicationIdToAttributeNameMap.get(applicationId).add(attributeName);
071            // reserve a spot in the field map
072            orderedSearchFieldMap.put(attributeName, null);
073        }
074
075        for (String applicationId : applicationIdToAttributeNameMap.keySet()) {
076            DocumentSearchCustomizationHandlerService documentSearchCustomizationService = loadCustomizationService(
077                    applicationId);
078            List<String> searchableAttributeNames = applicationIdToAttributeNameMap.get(applicationId);
079            DocumentSearchCriteriaConfiguration documentSearchConfiguration = documentSearchCustomizationService.getDocumentSearchConfiguration(
080                    documentType.getName(), searchableAttributeNames);
081            mergeAttributeFields(documentSearchConfiguration.getSearchAttributeFields(), orderedSearchFieldMap);
082        }
083
084        DocumentSearchCriteriaConfiguration.Builder configBuilder = DocumentSearchCriteriaConfiguration.Builder.create();
085        configBuilder.setSearchAttributeFields(flattenOrderedFieldMap(orderedSearchFieldMap));
086        return configBuilder.build();
087    }
088
089    @Override
090    public List<RemotableAttributeError> validateLookupFieldParameters(DocumentType documentType,
091            DocumentSearchCriteria documentSearchCriteria) {
092
093        List<DocumentTypeAttributeBo> searchableAttributes = documentType.getSearchableAttributes();
094        LinkedHashMap<String, List<String>> applicationIdToAttributeNameMap = new LinkedHashMap<String, List<String>>();
095
096        for (DocumentTypeAttributeBo searchableAttribute : searchableAttributes) {
097            RuleAttribute ruleAttribute = searchableAttribute.getRuleAttribute();
098            String attributeName = ruleAttribute.getName();
099            String applicationId = ruleAttribute.getApplicationId();
100            if (!applicationIdToAttributeNameMap.containsKey(applicationId)) {
101                applicationIdToAttributeNameMap.put(applicationId, new ArrayList<String>());
102            }
103            applicationIdToAttributeNameMap.get(applicationId).add(attributeName);
104        }
105
106        List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
107        for (String applicationId : applicationIdToAttributeNameMap.keySet()) {
108            DocumentSearchCustomizationHandlerService documentSearchCustomizationService = loadCustomizationService(applicationId);
109            List<String> searchableAttributeNames = applicationIdToAttributeNameMap.get(applicationId);
110            List<RemotableAttributeError> searchErrors = documentSearchCustomizationService.validateCriteria(
111                    documentSearchCriteria, searchableAttributeNames);
112            if (!CollectionUtils.isEmpty(searchErrors)) {
113                errors.addAll(searchErrors);
114            }
115        }
116
117        return errors;
118    }
119
120    @Override
121    public DocumentSearchCriteria customizeCriteria(DocumentType documentType, DocumentSearchCriteria documentSearchCriteria) {
122        DocumentTypeAttributeBo customizerAttribute = documentType.getCustomizerAttribute();
123        if (customizerAttribute != null) {
124            DocumentSearchCustomizationHandlerService service = loadCustomizationService(customizerAttribute.getRuleAttribute().getApplicationId());
125            if (service.getEnabledCustomizations(documentType.getName(), customizerAttribute.getRuleAttribute().getName()).contains(
126                    DocumentSearchCustomization.CRITERIA)) {
127                DocumentSearchCriteria customizedCriteria = service.customizeCriteria(documentSearchCriteria, customizerAttribute.getRuleAttribute().getName());
128                if (customizedCriteria != null) {
129                    return customizedCriteria;
130                }
131            }
132        }
133        return null;
134    }
135
136    @Override
137    public DocumentSearchCriteria customizeClearCriteria(DocumentType documentType, DocumentSearchCriteria documentSearchCriteria) {
138        DocumentTypeAttributeBo customizerAttribute = documentType.getCustomizerAttribute();
139        if (customizerAttribute != null) {
140            DocumentSearchCustomizationHandlerService service = loadCustomizationService(customizerAttribute.getRuleAttribute().getApplicationId());
141            if (service.getEnabledCustomizations(documentType.getName(), customizerAttribute.getRuleAttribute().getName()).contains(
142                    DocumentSearchCustomization.CLEAR_CRITERIA)) {
143                DocumentSearchCriteria customizedCriteria = service.customizeClearCriteria(documentSearchCriteria, customizerAttribute.getRuleAttribute().getName());
144                if (customizedCriteria != null) {
145                    return customizedCriteria;
146                }
147            }
148        }
149        return null;
150    }
151
152    @Override
153    public DocumentSearchResultValues customizeResults(DocumentType documentType,
154            DocumentSearchCriteria documentSearchCriteria, DocumentSearchResults results) {
155        if (!results.getSearchResults().isEmpty()) {
156            DocumentTypeAttributeBo customizerAttribute = documentType.getCustomizerAttribute();
157            if (customizerAttribute != null) {
158                DocumentSearchCustomizationHandlerService service = loadCustomizationService(customizerAttribute.getRuleAttribute().getApplicationId());
159                if (service.getEnabledCustomizations(documentType.getName(), customizerAttribute.getRuleAttribute().getName()).contains(
160                        DocumentSearchCustomization.RESULTS)) {
161                    DocumentSearchResultValues customizedResults = service.customizeResults(documentSearchCriteria, results.getSearchResults(), customizerAttribute.getRuleAttribute().getName());
162                    if (customizedResults != null) {
163                        return customizedResults;
164                    }
165                }
166            }
167            return null;
168        }
169        return null;
170    }
171
172    @Override
173    public DocumentSearchResultSetConfiguration customizeResultSetConfiguration(DocumentType documentType,
174            DocumentSearchCriteria documentSearchCriteria) {
175        DocumentTypeAttributeBo customizerAttribute = documentType.getCustomizerAttribute();
176        if (customizerAttribute != null) {
177            DocumentSearchCustomizationHandlerService service = loadCustomizationService(customizerAttribute.getRuleAttribute().getApplicationId());
178            if (service.getEnabledCustomizations(documentType.getName(), customizerAttribute.getRuleAttribute().getName()).contains(
179                    DocumentSearchCustomization.RESULT_SET_FIELDS)) {
180                DocumentSearchResultSetConfiguration resultSetConfiguration = service.customizeResultSetConfiguration(
181                        documentSearchCriteria, customizerAttribute.getRuleAttribute().getName());
182                if (resultSetConfiguration != null) {
183                    return resultSetConfiguration;
184                }
185            }
186        }
187        return null;
188    }
189
190    protected DocumentSearchCustomizationHandlerService loadCustomizationService(String applicationId) {
191        DocumentSearchCustomizationHandlerService service = KewFrameworkServiceLocator.getDocumentSearchCustomizationHandlerService(
192                applicationId);
193        if (service == null) {
194            throw new WorkflowRuntimeException("Failed to locate DocumentSearchCustomizationService for applicationId: " + applicationId);
195        }
196        return service;
197    }
198
199    protected void mergeAttributeFields(List<AttributeFields> attributeFieldsList, LinkedHashMap<String, AttributeFields> orderedFieldMap) {
200        if (attributeFieldsList == null) {
201            return;
202        }
203        for (AttributeFields attributeFields : attributeFieldsList) {
204            orderedFieldMap.put(attributeFields.getAttributeName(), attributeFields);
205        }
206    }
207
208    protected List<AttributeFields> flattenOrderedFieldMap(LinkedHashMap<String, AttributeFields> orderedFieldMap) {
209        return new ArrayList<AttributeFields>(orderedFieldMap.values());
210    }
211
212}