View Javadoc
1   /**
2    * Copyright 2005-2015 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.rule;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.xml.xpath.XPathExpressionException;
24  
25  import org.apache.commons.lang.ObjectUtils;
26  import org.apache.log4j.Logger;
27  import org.kuali.rice.kew.api.rule.RuleExtension;
28  import org.kuali.rice.kew.routeheader.DocumentContent;
29  
30  
31  /**
32   * Generic base class that implements common functionality to simplify implementing
33   * a WorkflowAttribute.  This includes simplified template methods, as well as a generic
34   * attribute content model.
35   * 
36   * <p>Control flow (for isMatch):</p>
37   * 
38   * <ol>
39   *   <li>{@link #isMatch(DocumentContent, List)}
40   *     <ol>
41   *       <li>{@link #isMatch(List, List)}
42   *         <ol>
43   *           <li>{@link #isMatch(Map, List)}</li>
44   *         </ol>
45   *       </li>
46   *     </ol>
47   *   </li>
48   * </ol>
49   * 
50   * The default matching algorithm will match:
51   * <blockquote><i>if any single attribute's properties are a match for all rule extension values</i></blockquote>
52   * This implementation does not (yet!) implement a generic internal map of properties, so it is up to subclasses
53   * to expose specific named getters/setters to set data on an attribute of this ancestry.
54   * 
55   * @author Kuali Rice Team (rice.collab@kuali.org)
56   */
57  public abstract class GenericWorkflowAttribute extends AbstractWorkflowAttribute {
58      protected final Logger log = Logger.getLogger(getClass());
59      protected final String attributeName;
60      protected final GenericAttributeContent content;
61      
62      public GenericWorkflowAttribute() {
63          this(null); // can't do getClass().getName() so we'll have to pass null...shame
64      }
65  
66      public GenericWorkflowAttribute(String uniqueName) {
67          if (uniqueName != null) {
68              this.attributeName = uniqueName;
69          } else {
70              this.attributeName = getClass().getName();
71          }
72          content = new GenericAttributeContent(attributeName);
73      }
74  
75      /**
76       * Template method for subclasses to override to expose attribute state
77       * @return map exposing attribute state
78       */
79      public abstract Map<String, String> getProperties();
80  
81      /**
82       * Simply defers to GenericAttributeContent to generate suitable XML content in a standard fashion
83       */
84      public String getDocContent() {
85          String dc = content.generateContent(getProperties());
86          //log.info("Generating doc content: " + dc, new Exception("Dummy exception"));
87          return dc;
88      }
89  
90      public boolean isMatch(DocumentContent docContent, List<RuleExtension> ruleExtensions) {
91          log.info("isMatch: " + docContent + " " + ruleExtensions);
92          try {
93              // could be multiple attributes on the incoming doc content!
94              List<Map<String, String>> propertiesList = content.parseContent(docContent.getAttributeContent());
95              
96              return isMatch(propertiesList, ruleExtensions);
97          } catch (XPathExpressionException xpee) {
98              String message = "Error parsing attribute '" + attributeName + "' content: " + docContent.getDocContent();
99              log.error(message, xpee);
100             throw new RuntimeException(xpee);
101         }
102     }
103 
104     /**
105      * Returns true if any single incoming attribute's properties are a match for all rule extension values
106      * @param propertiesList the list of incoming attributes' properties
107      * @param ruleExtensions the rule extensions
108      * @return true if any single attribute's properties are a match for all rule extension values
109      */
110     protected boolean isMatch(List<Map<String, String>> propertiesList, List<RuleExtension> ruleExtensions) {
111         for (Map<String, String> properties: propertiesList) {
112             return isMatch(properties, ruleExtensions);
113         }
114         return false;
115     }
116 
117     /**
118      * Returns true if all key/value pairs defined by the specified rule extensions are present in the incoming attribute's
119      * properties
120      * @param properties incoming attribute's properties
121      * @param ruleExtensions list of rule extensions
122      * @return true if all key/value pairs defined by the specified rule extensions are present in the incoming attribute's
123      */
124     protected boolean isMatch(Map<String, String> properties, List<RuleExtension> ruleExtensions) {
125         for (RuleExtension ruleExtension: ruleExtensions) {
126             for (Map.Entry<String, String> ruleExtensionValue: ruleExtension.getExtensionValuesMap().entrySet()) {
127                 if (!ObjectUtils.equals(ruleExtensionValue.getValue(), properties.get(ruleExtensionValue.getKey()))) {
128                     return false;
129                 }
130             }
131         }
132         return true;
133     }
134 
135     /**
136      * These guys should probably be implemented to set the parameters on an internal member property map this attribute
137      * should use to contain all properties set on it, like StandardGenericXmlAttribute.
138      * @see #getProperties()
139      * TODO: implement me!
140      */
141     public List validateRoutingData(Map paramMap) {
142         return Collections.EMPTY_LIST;
143     }
144     public List validateRuleData(Map paramMap) {
145         return Collections.EMPTY_LIST;
146     }
147 
148     //public List validateClientRouting....
149 
150     /**
151      * I think the job of this method is to marshal the current state of the attribute into a representative list of rule extension
152      * values.  On that assumption, this method should simply create a list of RuleExtensionValues based on the the property map
153      * this attribute uses to hold property values.
154      * 
155      * TODO: this is not fully implemented! e.g. generic property map like StandardGenericXmlAttribute
156      */
157     public List<RuleExtensionValue> getRuleExtensionValues() {
158         log.info("getRuleExtensionValues");
159         List<RuleExtensionValue> exts = new ArrayList<RuleExtensionValue>();
160         Map<String, String> props = getProperties();
161         if (props != null) {
162             for (Map.Entry<String, String> entry: props.entrySet()) {
163                 if (entry.getValue() != null) {
164                     RuleExtensionValue ruleVal = new RuleExtensionValue();
165                     ruleVal.setKey(entry.getKey());
166                     ruleVal.setValue(entry.getValue());
167                     exts.add(ruleVal);
168                 }
169             }
170         }
171         return exts;
172     }
173 }