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.rule; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.List; 021import java.util.Map; 022 023import javax.xml.xpath.XPathExpressionException; 024 025import org.apache.commons.lang.ObjectUtils; 026import org.apache.log4j.Logger; 027import org.kuali.rice.kew.api.rule.RuleExtension; 028import org.kuali.rice.kew.routeheader.DocumentContent; 029 030 031/** 032 * Generic base class that implements common functionality to simplify implementing 033 * a WorkflowAttribute. This includes simplified template methods, as well as a generic 034 * attribute content model. 035 * 036 * <p>Control flow (for isMatch):</p> 037 * 038 * <ol> 039 * <li>{@link #isMatch(DocumentContent, List)} 040 * <ol> 041 * <li>{@link #isMatch(List, List)} 042 * <ol> 043 * <li>{@link #isMatch(Map, List)}</li> 044 * </ol> 045 * </li> 046 * </ol> 047 * </li> 048 * </ol> 049 * 050 * The default matching algorithm will match: 051 * <blockquote><i>if any single attribute's properties are a match for all rule extension values</i></blockquote> 052 * This implementation does not (yet!) implement a generic internal map of properties, so it is up to subclasses 053 * to expose specific named getters/setters to set data on an attribute of this ancestry. 054 * 055 * @author Kuali Rice Team (rice.collab@kuali.org) 056 */ 057public abstract class GenericWorkflowAttribute extends AbstractWorkflowAttribute { 058 protected final Logger log = Logger.getLogger(getClass()); 059 protected final String attributeName; 060 protected final GenericAttributeContent content; 061 062 public GenericWorkflowAttribute() { 063 this(null); // can't do getClass().getName() so we'll have to pass null...shame 064 } 065 066 public GenericWorkflowAttribute(String uniqueName) { 067 if (uniqueName != null) { 068 this.attributeName = uniqueName; 069 } else { 070 this.attributeName = getClass().getName(); 071 } 072 content = new GenericAttributeContent(attributeName); 073 } 074 075 /** 076 * Template method for subclasses to override to expose attribute state 077 * @return map exposing attribute state 078 */ 079 public abstract Map<String, String> getProperties(); 080 081 /** 082 * Simply defers to GenericAttributeContent to generate suitable XML content in a standard fashion 083 */ 084 public String getDocContent() { 085 String dc = content.generateContent(getProperties()); 086 //log.info("Generating doc content: " + dc, new Exception("Dummy exception")); 087 return dc; 088 } 089 090 public boolean isMatch(DocumentContent docContent, List<RuleExtension> ruleExtensions) { 091 log.info("isMatch: " + docContent + " " + ruleExtensions); 092 try { 093 // could be multiple attributes on the incoming doc content! 094 List<Map<String, String>> propertiesList = content.parseContent(docContent.getAttributeContent()); 095 096 return isMatch(propertiesList, ruleExtensions); 097 } catch (XPathExpressionException xpee) { 098 String message = "Error parsing attribute '" + attributeName + "' content: " + docContent.getDocContent(); 099 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}