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.krad.workflow.attribute;
017
018import org.kuali.rice.kew.engine.RouteContext;
019import org.kuali.rice.krad.datadictionary.DocumentEntry;
020import org.kuali.rice.krad.datadictionary.RoutingTypeDefinition;
021import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
022import org.kuali.rice.krad.document.Document;
023import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
024import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031/**
032 * QualifierResolver which uses Data Dictionary defined workflow attributes to gather a collection
033 * of qualifiers to use to determine the responsibility for a document at a given workflow route node.
034 * 
035 * WorkflowAttributes can be defined in the data dictionary like so (this has been abbreviated):
036 * 
037 * <!-- Exported Workflow Attributes -->
038 *   <bean id="DisbursementVoucherDocument-workflowAttributes" parent="DisbursementVoucherDocument-workflowAttributes-parentBean"/>
039 *
040 *   <bean id="DisbursementVoucherDocument-workflowAttributes-parentBean" abstract="true" parent="WorkflowAttributes">
041 *       <property name="routingTypeDefinitions">
042 *           <map>
043 *               <!-- no qualifiers for purchasing node -->
044 *               <entry key="Account" value-ref="RoutingType-AccountingDocument-Account-sourceOnly"/>
045 *               <entry key="AccountingOrganizationHierarchy" value-ref="RoutingType-AccountingDocument-OrganizationHierarchy-sourceOnly"/>
046 *               <entry key="Campus" value-ref="DisbursementVoucherDocument-RoutingType-Campus"/>
047 *               <!-- no qualifiers for tax review -->
048 *               <!-- no qualifiers for travel review -->
049 *               <entry key="PaymentMethod" value-ref="DisbursementVoucherDocument-RoutingType-PaymentMethod"/>
050 *               <entry key="Award" value-ref="RoutingType-AccountingDocument-Award"/>
051 *           </map>
052 *       </property>
053 *   </bean>
054 * 
055 *   <bean id="DisbursementVoucherDocument-RoutingType-PaymentMethod" class="org.kuali.rice.krad.datadictionary.RoutingTypeDefinition">
056 *       <property name="routingAttributes">
057 *           <list>
058 *               <bean class="org.kuali.rice.krad.datadictionary.RoutingAttribute">
059 *                   <property name="qualificationAttributeName" value="disbVchrPaymentMethodCode"/>
060 *               </bean>
061 *           </list>
062 *       </property>
063 *       <property name="documentValuePathGroups">
064 *           <list>
065 *               <bean class="org.kuali.rice.krad.datadictionary.DocumentValuePathGroup">
066 *                   <property name="documentValues">
067 *                       <list>
068 *                           <value>disbVchrPaymentMethodCode</value>
069 *                       </list>
070 *                   </property>
071 *               </bean>
072 *           </list>
073 *       </property>
074 *   </bean> 
075 * 
076 * At the PaymentMethod node of the document, the DisbursementVoucherDocument-RoutingType-PaymentMethod RoutingTypeDefinition will be
077 * consulted; it will pull values from the document (in this case, document.disbVchrPaymentMethodCode) and populate those
078 * into the role qualifier Map<String, String>, with the key being the qualificationAttributeName and the value being the value of the property
079 * listed in the documentValuePathGroups in the document.
080 */
081public class DataDictionaryQualifierResolver extends QualifierResolverBase {
082//    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataDictionaryQualifierResolver.class);
083    
084
085    /**
086     * Given the RouteContext, determines the document type of the document being routed and the current
087     * route nodes; generates a List of qualifier Map<String, String>s based on the the contents of the document.
088     * @see org.kuali.rice.kew.role.QualifierResolver#resolve(org.kuali.rice.kew.engine.RouteContext)
089     */
090    public List<Map<String, String>> resolve(RouteContext context) {
091        final String routeLevel = context.getNodeInstance().getName();
092        final DocumentEntry documentEntry = getDocumentEntry(context);
093        final RoutingTypeDefinition routingTypeDefinition = getWorkflowAttributeDefintion(documentEntry, routeLevel);
094        final Document document = getDocument(context);
095        List<Map<String, String>> qualifiers = null;
096        
097        if (document != null && routingTypeDefinition != null) {
098            qualifiers = KRADServiceLocatorInternal.getWorkflowAttributePropertyResolutionService().resolveRoutingTypeQualifiers(document, routingTypeDefinition);
099        } else {
100            qualifiers = new ArrayList<Map<String, String>>();
101            Map<String, String> basicQualifier = new HashMap<String, String>();
102            qualifiers.add(basicQualifier);
103        }
104        decorateWithCommonQualifiers(qualifiers, document, documentEntry, routeLevel);
105        return qualifiers;
106    }
107
108    /**
109     * Retrieves the data dictionary entry for the document being operated on by the given route context
110     * @param context the current route context
111     * @return the data dictionary document entry
112     */
113    protected DocumentEntry getDocumentEntry(RouteContext context) {
114        return KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(context.getDocument().getDocumentType().getName());
115    }
116
117    /**
118     * Retrieves the proper List of WorkflowAttributes for the given route level from the data dictionary
119     * document entry
120     * @param documentEntry the data dictionary document entry for the currently routed document
121     * @param routeLevelName the name of the route level
122     * @return a WorkflowAttributeDefinition if one could be found for the route level; otherwise, nothing
123     */
124    protected RoutingTypeDefinition getWorkflowAttributeDefintion(DocumentEntry documentEntry, String routeLevelName) {
125       final WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes();
126       if ( workflowAttributes == null ) {
127           return null;
128       }
129       final Map<String, RoutingTypeDefinition> routingTypeMap = workflowAttributes.getRoutingTypeDefinitions();
130       if (routingTypeMap.containsKey(routeLevelName)) return routingTypeMap.get(routeLevelName);
131       return null;
132    }
133    
134    /**
135     * Add common qualifiers to every Map<String, String> in the given List of Map<String, String>
136     * @param qualifiers a List of Map<String, String>s to add common qualifiers to
137     * @param document the document currently being routed
138     * @param documentEntry the data dictionary entry of the type of document currently being routed
139     * @param routeLevel the document's current route level
140     */
141    protected void decorateWithCommonQualifiers(List<Map<String, String>> qualifiers, Document document, DocumentEntry documentEntry, String routeLevel) {
142        for (Map<String, String> qualifier : qualifiers) {
143            addCommonQualifiersToMap(qualifier, document, documentEntry, routeLevel);
144        }
145    }
146    
147    /**
148     * Adds common qualifiers to a given Map<String, String>
149     * @param qualifier an Map<String, String> to add common qualifiers to
150     * @param document the document currently being routed
151     * @param documentEntry the data dictionary entry of the type of document currently being routed
152     * @param routeLevel the document's current route level
153     */
154    protected void addCommonQualifiersToMap(Map<String, String> qualifier, Document document, DocumentEntry documentEntry, String routeLevel) {
155        if ( document != null ) {
156            qualifier.put(KIM_ATTRIBUTE_DOCUMENT_NUMBER, document.getDocumentNumber());
157        }
158        if ( documentEntry != null ) {
159            qualifier.put(KIM_ATTRIBUTE_DOCUMENT_TYPE_NAME, documentEntry.getDocumentTypeName());
160        }
161        qualifier.put(KIM_ATTRIBUTE_ROUTE_LEVEL_NAME, routeLevel);
162    }
163}