001/**
002 * Copyright 2005-2016 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.impl.actionlist;
017
018import com.google.common.collect.ArrayListMultimap;
019import com.google.common.collect.ListMultimap;
020import org.apache.commons.lang.StringUtils;
021import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
022import org.kuali.rice.kew.api.action.ActionItem;
023import org.kuali.rice.kew.api.action.ActionItemCustomization;
024import org.kuali.rice.kew.doctype.bo.DocumentType;
025import org.kuali.rice.kew.doctype.service.DocumentTypeService;
026import org.kuali.rice.kew.framework.KewFrameworkServiceLocator;
027import org.kuali.rice.kew.framework.actionlist.ActionListCustomizationHandlerService;
028import org.kuali.rice.kew.framework.actionlist.ActionListCustomizationMediator;
029import org.kuali.rice.kew.rule.bo.RuleAttribute;
030
031import java.util.Collections;
032import java.util.HashMap;
033import java.util.List;
034import java.util.Map;
035
036/**
037 * Internal utility class that partitions ActionItems by application id, and calls the appropriate
038 * {@link ActionListCustomizationHandlerService} for each parition to retrieve any customizations.
039 */
040public class ActionListCustomizationMediatorImpl implements ActionListCustomizationMediator {
041
042    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ActionListCustomizationMediatorImpl.class);
043
044    private DocumentTypeService documentTypeService;
045    private ActionListCustomizationHandlerServiceChooser actionListCustomizationHandlerServiceChooser =
046            new ActionListCustomizationHandlerServiceChooser();
047
048    /**
049     * <p>partitions ActionItems by application id, and calls the appropriate
050     * {@link ActionListCustomizationHandlerService} for each parition, merging the results.</p>
051     *
052     * <dl><dt><b>inherited docs:</b></dt><dd>{@inheritDoc}</dd></dl>
053     */
054    @Override
055    public Map<String, ActionItemCustomization> getActionListCustomizations(String principalId,
056            List<ActionItem> actionItems) throws RiceIllegalArgumentException {
057        if (StringUtils.isBlank(principalId)) {
058            throw new RiceIllegalArgumentException("invalid principalId: " + principalId);
059        }
060        if (actionItems == null) {
061            actionItems = Collections.emptyList();
062        }
063
064        // map from action item ID to ActionItemCustomization
065        Map<String, ActionItemCustomization> results = new HashMap<String, ActionItemCustomization>();
066
067        // group each action item by application id that needs to be called for action list customizations (note that
068        // the application id comes from the extension/rule attribute record, most action lists will have doc types
069        // with no custom action list attribute, though the default still needs to be run in this case)
070
071        ListMultimap<String, ActionItem> itemsByApplicationId = ArrayListMultimap.create();
072
073        for (ActionItem actionItem : actionItems) {
074            //DocumentType docType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(actionItem.getDocName());
075            DocumentType docType = getDocumentTypeService().findByName(actionItem.getDocName());
076            if (docType == null) {
077                LOG.error(String.format("Action item %s has an invalid document type name of %s",
078                        actionItem.getId(), actionItem.getDocName()));
079                // OK to have a null key, this represents the default app id
080                itemsByApplicationId.put(null, actionItem);
081            } else {
082                // OK to have a null key, this represents the default app id
083                itemsByApplicationId.put(getActionListCustomizationApplicationId(docType), actionItem);
084            }
085        }
086
087        // For each application id, pass all action items which might need to be customized (because they have a
088        // document type, which declares an action list attribute, which has an application id declared) to the
089        // appropriate ActionListCustomizationHandlerService endpoint
090
091        for (String applicationId : itemsByApplicationId.keySet()) {
092            ActionListCustomizationHandlerService actionListCustomizationHandler =
093                    getActionListCustomizationHandlerServiceChooser().getByApplicationId(applicationId);
094
095            if (actionListCustomizationHandler == null) {
096                // get the local ActionListCustomizationHandlerService as a fallback
097                actionListCustomizationHandler =
098                        getActionListCustomizationHandlerServiceChooser().getByApplicationId(null);
099            }
100
101            List<ActionItemCustomization> customizations =
102                    actionListCustomizationHandler.customizeActionList(principalId, itemsByApplicationId.get(
103                            applicationId));
104
105
106            // Get back the customized results and reassemble with customized results from all different application
107            // customizations (as well as default customizations)
108            if (customizations != null) for (ActionItemCustomization customization : customizations) {
109                results.put(customization.getActionItemId(), customization);
110            }
111        }
112
113        return results;
114    }
115
116    // CustomActionListAttributes are configured in RuleAttributes, so that is the
117    // applicationId we need to use
118    private String getActionListCustomizationApplicationId(DocumentType docType) {
119        String applicationId = null;
120        RuleAttribute ruleAttribute = docType.getCustomActionListRuleAttribute();
121        if (ruleAttribute != null) {
122            applicationId = ruleAttribute.getApplicationId();
123        }
124        // we may return null
125        return applicationId;
126    }
127
128    public DocumentTypeService getDocumentTypeService() {
129        return documentTypeService;
130    }
131
132    public void setDocumentTypeService(DocumentTypeService documentTypeService) {
133        this.documentTypeService = documentTypeService;
134    }
135
136    /**
137     * Need this to make our class testable without having to wire the universe up through spring.
138     */
139    public static class ActionListCustomizationHandlerServiceChooser {
140        public ActionListCustomizationHandlerService getByApplicationId(String applicationId) {
141            return KewFrameworkServiceLocator.getActionListCustomizationHandlerService(applicationId);
142        }
143    }
144
145    public ActionListCustomizationHandlerServiceChooser getActionListCustomizationHandlerServiceChooser() {
146        return actionListCustomizationHandlerServiceChooser;
147    }
148
149    public void setActionListCustomizationHandlerServiceChooser(
150            ActionListCustomizationHandlerServiceChooser actionListCustomizationHandlerServiceChooser) {
151        this.actionListCustomizationHandlerServiceChooser = actionListCustomizationHandlerServiceChooser;
152    }
153}