001    /**
002     * Copyright 2005-2013 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     */
016    package org.kuali.rice.kns.workflow.attribute;
017    
018    import org.kuali.rice.kew.engine.RouteContext;
019    import org.kuali.rice.kns.service.KNSServiceLocator;
020    import org.kuali.rice.krad.datadictionary.DocumentEntry;
021    import org.kuali.rice.krad.datadictionary.RoutingTypeDefinition;
022    import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
023    import org.kuali.rice.krad.document.Document;
024    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025    
026    import java.util.ArrayList;
027    import java.util.HashMap;
028    import java.util.List;
029    import 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     */
081    public 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 = KNSServiceLocator.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    }