View Javadoc

1   /**
2    * Copyright 2005-2012 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.web;
17  
18  import org.apache.commons.beanutils.BeanUtils;
19  import org.apache.commons.beanutils.PropertyUtils;
20  import org.apache.commons.lang.ArrayUtils;
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.rice.core.api.exception.RiceRuntimeException;
23  import org.kuali.rice.core.api.uif.RemotableAttributeError;
24  import org.kuali.rice.kew.api.action.ActionRequestPolicy;
25  import org.kuali.rice.kew.api.rule.RuleTemplateAttributeContract;
26  import org.kuali.rice.kew.doctype.bo.DocumentType;
27  import org.kuali.rice.kew.exception.WorkflowServiceError;
28  import org.kuali.rice.kew.rule.GroupRuleResponsibility;
29  import org.kuali.rice.kew.rule.PersonRuleResponsibility;
30  import org.kuali.rice.kew.rule.RoleRuleResponsibility;
31  import org.kuali.rice.kew.rule.RuleBaseValues;
32  import org.kuali.rice.kew.rule.RuleDelegationBo;
33  import org.kuali.rice.kew.rule.RuleExtensionBo;
34  import org.kuali.rice.kew.rule.RuleExtensionValue;
35  import org.kuali.rice.kew.rule.RuleResponsibilityBo;
36  import org.kuali.rice.kew.rule.WorkflowRuleAttribute;
37  import org.kuali.rice.kew.rule.bo.RuleAttribute;
38  import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
39  import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
40  import org.kuali.rice.kew.rule.service.RuleServiceInternal;
41  import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
42  import org.kuali.rice.kew.service.KEWServiceLocator;
43  import org.kuali.rice.kew.api.KewApiConstants;
44  import org.kuali.rice.kim.api.group.Group;
45  import org.kuali.rice.kim.api.identity.principal.Principal;
46  import org.kuali.rice.kns.web.ui.Field;
47  import org.kuali.rice.kns.web.ui.Row;
48  import org.kuali.rice.kns.web.ui.Section;
49  
50  import java.lang.reflect.InvocationTargetException;
51  import java.util.ArrayList;
52  import java.util.Collections;
53  import java.util.HashMap;
54  import java.util.Iterator;
55  import java.util.List;
56  import java.util.Map;
57  
58  /**
59   * Some utilities which are utilized by the {@link RuleAction}.
60   *
61   * @author Kuali Rice Team (rice.collab@kuali.org)
62   */
63  public final class WebRuleUtils {
64  
65  	public static final String RULE_TEMPLATE_ID_PARAM = "ruleCreationValues.ruleTemplateId";
66  	public static final String RULE_TEMPLATE_NAME_PARAM = "ruleCreationValues.ruleTemplateName";
67  	public static final String DOCUMENT_TYPE_NAME_PARAM = "ruleCreationValues.docTypeName";
68  	public static final String RESPONSIBILITY_ID_PARAM = "ruleCreationValues.responsibilityId";
69  	
70  	private static final String ID_SEPARATOR = ":";
71  	private static final String RULE_ATTRIBUTES_SECTION_ID = "RuleAttributes";
72  	private static final String RULE_ATTRIBUTES_SECTION_TITLE = "Rule Attributes";
73  	private static final String ROLES_MAINTENANCE_SECTION_ID = "RolesMaintenance";
74  	
75  	private WebRuleUtils() {
76  		throw new UnsupportedOperationException("do not call");
77  	}
78  	
79  	/**
80  	 * Copies the existing rule onto the current document.  This is used within the web-based rule GUI to make a
81  	 * copy of a rule on the existing document.  Essentially, this method makes a copy of the rule and all
82  	 * delegates but preserves the document ID of the original rule.
83  	 */
84      public static WebRuleBaseValues copyRuleOntoExistingDocument(WebRuleBaseValues rule) throws Exception {
85          WebRuleBaseValues ruleCopy = new WebRuleBaseValues();
86          PropertyUtils.copyProperties(ruleCopy, rule);
87          ruleCopy.setPreviousRuleId(null);
88          ruleCopy.setCurrentInd(null);
89          ruleCopy.setVersionNbr(null);
90  
91          List responsibilities = new ArrayList();
92          for (Iterator iter = ruleCopy.getRuleResponsibilities().iterator(); iter.hasNext();) {
93              WebRuleResponsibility responsibility = (WebRuleResponsibility) iter.next();
94              WebRuleResponsibility responsibilityCopy = new WebRuleResponsibility();
95              PropertyUtils.copyProperties(responsibilityCopy, responsibility);
96  
97              responsibilityCopy.setResponsibilityId(null);
98              responsibilityCopy.setId(null);
99              
100             List delegations = new ArrayList();
101             for (Iterator iterator = responsibilityCopy.getDelegationRules().iterator(); iterator.hasNext();) {
102                 RuleDelegationBo delegation = (RuleDelegationBo) iterator.next();
103                 RuleDelegationBo delegationCopy = new RuleDelegationBo();
104                 PropertyUtils.copyProperties(delegationCopy, delegation);
105 
106                 delegationCopy.setDelegateRuleId(null);
107                 delegationCopy.setVersionNumber(null);
108                 delegationCopy.setRuleDelegationId(null);
109                 delegationCopy.setResponsibilityId(null);
110 
111                 WebRuleBaseValues delegationRule = ((WebRuleBaseValues) delegation.getDelegationRule());
112                 WebRuleBaseValues ruleDelegateCopy = new WebRuleBaseValues();
113                 PropertyUtils.copyProperties(ruleDelegateCopy, delegationRule);
114 
115                 ruleDelegateCopy.setPreviousRuleId(null);
116                 ruleDelegateCopy.setCurrentInd(null);
117                 ruleDelegateCopy.setVersionNbr(null);
118 
119                 List delegateResps = new ArrayList();
120                 for (Iterator iterator1 = ruleDelegateCopy.getRuleResponsibilities().iterator(); iterator1.hasNext();) {
121                     WebRuleResponsibility delegateResp = (WebRuleResponsibility) iterator1.next();
122                     WebRuleResponsibility delegateRespCopy = new WebRuleResponsibility();
123                     PropertyUtils.copyProperties(delegateRespCopy, delegateResp);
124 
125                     delegateRespCopy.setResponsibilityId(null);
126                     delegateRespCopy.setId(null);
127                     delegateResps.add(delegateRespCopy);
128                 }
129                 ruleDelegateCopy.setRuleResponsibilities(delegateResps);
130                 delegationCopy.setDelegationRule(ruleDelegateCopy);
131                 delegations.add(delegationCopy);
132             }
133             //responsibilityCopy.setDelegationRules(delegations);
134             responsibilities.add(responsibilityCopy);
135         }
136         ruleCopy.setRuleResponsibilities(responsibilities);
137         return ruleCopy;
138     }
139     
140     /**
141      * Makes a copy of the rule and clears the document id on the rule and any of its delegates.
142      * This method is used for making a copy of a rule for a new document.  It essentially calls
143      * the copyRuleOntoExistingDocument method and then clears out the document IDs.
144      * 
145      * @param webRuleBaseValues
146      */
147     public static WebRuleBaseValues copyToNewRule(WebRuleBaseValues webRuleBaseValues) throws Exception {
148     	WebRuleBaseValues newRule = copyRuleOntoExistingDocument(webRuleBaseValues);
149     	// clear out all document IDs on the rule and it's delegates
150     	newRule.setDocumentId(null);
151     	for (Iterator iterator = newRule.getRuleResponsibilities().iterator(); iterator.hasNext(); ) {
152 			RuleResponsibilityBo responsibility = (RuleResponsibilityBo) iterator.next();
153 			for (Iterator iterator2 = responsibility.getDelegationRules().iterator(); iterator2.hasNext(); ) {
154 				RuleDelegationBo delegation = (RuleDelegationBo) iterator2.next();
155 				delegation.getDelegationRule().setDocumentId(null);
156 			}
157 		}
158     	return newRule;
159     }
160 
161     public static void validateRuleTemplateAndDocumentType(RuleBaseValues oldRule, RuleBaseValues newRule, Map<String, String[]> parameters) {
162 		String[] ruleTemplateIds = parameters.get(RULE_TEMPLATE_ID_PARAM);
163 		String[] ruleTemplateNames = parameters.get(RULE_TEMPLATE_NAME_PARAM);
164 		String[] documentTypeNames = parameters.get(DOCUMENT_TYPE_NAME_PARAM);
165 		if (ArrayUtils.isEmpty(ruleTemplateIds) && ArrayUtils.isEmpty(ruleTemplateNames)) {
166 			throw new RiceRuntimeException("Rule document must be initiated with a valid rule template id or rule template name.");
167 		}
168 		if (ArrayUtils.isEmpty(documentTypeNames)) {
169 			throw new RiceRuntimeException("Rule document must be initiated with a valid document type name.");
170 		}
171 		RuleTemplateBo ruleTemplate = null;
172 		if (!ArrayUtils.isEmpty(ruleTemplateIds)) {
173 			String ruleTemplateId = ruleTemplateIds[0];
174 			ruleTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateId(ruleTemplateId);
175 			if (ruleTemplate == null) {
176 				throw new RiceRuntimeException("Failed to load rule template with id '" + ruleTemplateId + "'");
177 			}
178 		}
179 		if (ruleTemplate == null) {
180 			String ruleTemplateName = ruleTemplateNames[0];
181 			ruleTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateName(ruleTemplateName);
182 			if (ruleTemplate == null) {
183 				throw new RiceRuntimeException("Failed to load rule template with name '" + ruleTemplateName + "'");
184 			}
185 		}
186 		String documentTypeName = documentTypeNames[0];
187 		DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
188 		if (documentType == null) {
189 			throw new RiceRuntimeException("Failed to locate document type with name '" + documentTypeName + "'");
190 		}
191 		
192 		// it appears that there is always an old maintainable, even in the case of a new document creation,
193 		// if we don't initialize both the old and new versions we get errors during meshSections
194 		initializeRuleAfterNew(oldRule, ruleTemplate, documentTypeName);
195 		initializeRuleAfterNew(newRule, ruleTemplate, documentTypeName);
196 	}
197     
198 	private static void initializeRuleAfterNew(RuleBaseValues rule, RuleTemplateBo ruleTemplate, String documentTypeName) {
199 		rule.setRuleTemplate(ruleTemplate);
200 		rule.setRuleTemplateId(ruleTemplate.getId());
201 		rule.setDocTypeName(documentTypeName);
202 	}
203 	
204 	public static void validateRuleAndResponsibility(RuleDelegationBo oldRuleDelegation, RuleDelegationBo newRuleDelegation, Map<String, String[]> parameters) {
205 		String[] responsibilityIds = parameters.get(RESPONSIBILITY_ID_PARAM);
206 		if (ArrayUtils.isEmpty(responsibilityIds)) {
207 			throw new RiceRuntimeException("Delegation rule document must be initiated with a valid responsibility ID to delegate from.");
208 		}
209 		if (!ArrayUtils.isEmpty(responsibilityIds)) {
210 			String responsibilityId = responsibilityIds[0];
211 			RuleResponsibilityBo ruleResponsibility = KEWServiceLocator.getRuleService().findRuleResponsibility(responsibilityId);
212 			if (ruleResponsibility == null) {
213 				throw new RiceRuntimeException("Failed to locate a rule responsibility for responsibility ID " + responsibilityId);
214 			}
215 			oldRuleDelegation.setResponsibilityId(responsibilityId);
216 			newRuleDelegation.setResponsibilityId(responsibilityId);
217 		}
218 		
219 	}
220 
221 	public static void establishDefaultRuleValues(RuleBaseValues rule) {
222 		rule.setActive(true);
223 
224         RuleBaseValues defaultRule = ((RuleServiceInternal) KEWServiceLocator.getService(KEWServiceLocator.RULE_SERVICE)).findDefaultRuleByRuleTemplateId(
225         		rule.getRuleTemplate().getDelegationTemplateId());
226         if (defaultRule != null) {
227             defaultRule.setActivationDate(null);
228             defaultRule.setCurrentInd(null);
229             defaultRule.setDeactivationDate(null);
230             defaultRule.setDocTypeName(null);
231             defaultRule.setVersionNumber(null);
232             defaultRule.setId(null);
233             defaultRule.setTemplateRuleInd(Boolean.FALSE);
234             defaultRule.setVersionNbr(null);
235             try {
236 				PropertyUtils.copyProperties(rule, defaultRule);
237 			} catch (IllegalAccessException e) {
238 				throw new RuntimeException(e);
239 			} catch (InvocationTargetException e) {
240 				throw new RuntimeException(e);
241 			} catch (NoSuchMethodException e) {
242 				throw new RuntimeException(e);
243 			}
244         }
245 	}
246 	
247 
248 	public static List<Section> customizeSections(RuleBaseValues rule, List<Section> sections, boolean delegateRule) {
249 
250 		List<Section> finalSections = new ArrayList<Section>();
251 		for (Section section : sections) {
252 			// unfortunately, in the case of an inquiry the sectionId will always be null so we have to check section title
253 			if (section.getSectionTitle().equals(RULE_ATTRIBUTES_SECTION_TITLE) || 
254 					RULE_ATTRIBUTES_SECTION_ID.equals(section.getSectionId())) {
255 				List<Row> ruleTemplateRows = getRuleTemplateRows(rule, delegateRule);
256 				if (!ruleTemplateRows.isEmpty()) {
257 					section.setRows(ruleTemplateRows);
258 					finalSections.add(section);
259 				}
260 			} else if (ROLES_MAINTENANCE_SECTION_ID.equals(section.getSectionId())) {
261 				if (hasRoles(rule)) {
262 					finalSections.add(section);
263 				}
264 			} else {
265 				finalSections.add(section);
266 			}
267 		}
268 		
269 		return finalSections;
270 	}
271 	
272 	
273 	
274 
275 	public static List<Row> getRuleTemplateRows(RuleBaseValues rule, boolean delegateRule) {
276 
277 		List<Row> rows = new ArrayList<Row>();
278 		RuleTemplateBo ruleTemplate = rule.getRuleTemplate();
279 		Map<String, String> fieldNameMap = new HashMap<String, String>();
280 		// refetch rule template from service because after persistence in KNS, it comes back without any rule template attributes
281 		if (ruleTemplate != null){
282 			ruleTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateId(ruleTemplate.getId());
283 			if (ruleTemplate != null) {
284 				
285 				List<RuleTemplateAttributeBo> ruleTemplateAttributes = ruleTemplate.getActiveRuleTemplateAttributes();
286 				Collections.sort(ruleTemplateAttributes);
287 
288 				for (RuleTemplateAttributeBo ruleTemplateAttribute : ruleTemplateAttributes) {
289 					if (!ruleTemplateAttribute.isWorkflowAttribute()) {
290 						continue;
291 					}
292 					WorkflowRuleAttribute workflowAttribute = ruleTemplateAttribute.getWorkflowAttribute();
293 					RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
294 					if (ruleAttribute.getType().equals(KewApiConstants.RULE_XML_ATTRIBUTE_TYPE)) {
295 						((GenericXMLRuleAttribute) workflowAttribute).setExtensionDefinition(RuleAttribute.to(
296                                 ruleAttribute));
297 					}
298 					Map<String, String> parameterMap = getFieldMapForRuleTemplateAttribute(rule, ruleTemplateAttribute);
299 					workflowAttribute.validateRuleData(parameterMap);
300 					List<Row> attributeRows = transformAndPopulateAttributeRows(workflowAttribute.getRuleRows(), ruleTemplateAttribute, rule, fieldNameMap, delegateRule);
301 					rows.addAll(attributeRows);
302 
303 				}
304 			}
305 			transformFieldConversions(rows, fieldNameMap);
306 		}
307 		return rows;
308 	}
309 	
310 	public static void transformFieldConversions(List<Row> rows, Map<String, String> fieldNameMap) {
311 		for (Row row : rows) {
312 			Map<String, String> transformedFieldConversions = new HashMap<String, String>();
313 			for (Field field : row.getFields()) {
314 				Map<String, String> fieldConversions = field.getFieldConversionMap();
315 				for (String lookupFieldName : fieldConversions.keySet()) {
316 					String localFieldName = fieldConversions.get(lookupFieldName);
317 					if (fieldNameMap.containsKey(localFieldName)) {
318 						// set the transformed value
319 						transformedFieldConversions.put(lookupFieldName, fieldNameMap.get(localFieldName));
320 					} else {
321 						// set the original value (not sure if this case will happen, but just in case)
322 						transformedFieldConversions.put(lookupFieldName, fieldConversions.get(lookupFieldName));
323 					}
324 				}
325 				field.setFieldConversions(transformedFieldConversions);
326 			}
327 		}
328 	}
329 	
330 
331 
332 	
333 	private static boolean hasRoles(RuleBaseValues rule) {
334 		RuleTemplateBo ruleTemplate = rule.getRuleTemplate();
335 		return !ruleTemplate.getRoles().isEmpty();
336 	}
337 	
338 	/**
339 	 * Processes the Fields on the various attributes Rows to assign an appropriate field name to them so that the
340 	 * field name rendered in the maintenance HTML will properly assign the value to RuleBaseValues.fieldValues.
341 	 */
342 	
343 	public static List<Row> transformAndPopulateAttributeRows(List<Row> attributeRows, RuleTemplateAttributeBo ruleTemplateAttribute, RuleBaseValues rule, Map<String, String> fieldNameMap, boolean delegateRule) {
344 
345 		for (Row row : attributeRows) {
346 			for (Field field : row.getFields()) {
347 				String fieldName = field.getPropertyName();
348 				if (!StringUtils.isBlank(fieldName)) {
349 					String valueKey = ruleTemplateAttribute.getId() + ID_SEPARATOR + fieldName;
350 
351 					String propertyName;
352 					
353 					if (delegateRule) {
354 						propertyName = "delegationRuleBaseValues.fieldValues(" + valueKey + ")"; 
355 					} else {
356 						propertyName = "fieldValues(" + valueKey + ")"; 
357 					}
358 
359 					fieldNameMap.put(fieldName, propertyName);
360 					field.setPropertyName(propertyName);
361 					field.setPropertyValue(rule.getFieldValues().get(valueKey));
362 				}
363 			}
364 		}
365 		return attributeRows;
366 	}
367 	
368 	/**
369 	 * Since editing of a Rule should actually result in a rule with a new ID and new
370 	 * entries in the rule and rule responsibility tables, we need to clear out
371 	 * the primary keys of the rule and related objects.
372 	 */
373 	public static void clearKeysForSave(RuleBaseValues rule) {
374 		rule.setId(null);
375 		rule.setActivationDate(null);
376 		rule.setDeactivationDate(null);
377 		rule.setCurrentInd(false);
378 		rule.setVersionNbr(null);
379 		rule.setObjectId(null);
380 		rule.setVersionNumber(0L);
381 	}
382 	
383 	public static void clearKeysForSave(RuleDelegationBo ruleDelegation) {
384 		ruleDelegation.setRuleDelegationId(null);
385 		ruleDelegation.setObjectId(null);
386 		ruleDelegation.setVersionNumber(0L);
387 		clearKeysForSave(ruleDelegation.getDelegationRule());
388 	}
389 	
390     public static void translateResponsibilitiesForSave(RuleBaseValues rule) {
391 		rule.getRuleResponsibilities().clear();
392 		for (PersonRuleResponsibility responsibility : rule.getPersonResponsibilities()) {
393 			RuleResponsibilityBo ruleResponsibility = new RuleResponsibilityBo();
394 			ruleResponsibility.setActionRequestedCd(responsibility.getActionRequestedCd());
395 			ruleResponsibility.setPriority(responsibility.getPriority());
396 			ruleResponsibility.setResponsibilityId(responsibility.getResponsibilityId());
397 			if (ruleResponsibility.getResponsibilityId() == null) {
398 				ruleResponsibility.setResponsibilityId(KEWServiceLocator.getResponsibilityIdService().getNewResponsibilityId());
399 			}
400 			String principalId = KEWServiceLocator.getIdentityHelperService().getIdForPrincipalName(responsibility.getPrincipalName());
401 			ruleResponsibility.setRuleResponsibilityName(principalId);
402 			ruleResponsibility.setRuleResponsibilityType(KewApiConstants.RULE_RESPONSIBILITY_WORKFLOW_ID);
403 			// default the approve policy to First Approve
404 			ruleResponsibility.setApprovePolicy(ActionRequestPolicy.FIRST.getCode());
405 			rule.getRuleResponsibilities().add(ruleResponsibility);
406 		}
407 		for (GroupRuleResponsibility responsibility : rule.getGroupResponsibilities()) {
408 			RuleResponsibilityBo ruleResponsibility = new RuleResponsibilityBo();
409 			ruleResponsibility.setActionRequestedCd(responsibility.getActionRequestedCd());
410 			ruleResponsibility.setPriority(responsibility.getPriority());
411 			ruleResponsibility.setResponsibilityId(responsibility.getResponsibilityId());
412 			if (ruleResponsibility.getResponsibilityId() == null) {
413 				ruleResponsibility.setResponsibilityId(KEWServiceLocator.getResponsibilityIdService().getNewResponsibilityId());
414 			}
415 			Group group = KEWServiceLocator.getIdentityHelperService().getGroupByName(responsibility.getNamespaceCode(), responsibility.getName());
416 			ruleResponsibility.setRuleResponsibilityName(group.getId());
417 			ruleResponsibility.setRuleResponsibilityType(KewApiConstants.RULE_RESPONSIBILITY_GROUP_ID);
418 			ruleResponsibility.setApprovePolicy(ActionRequestPolicy.FIRST.getCode());
419 			rule.getRuleResponsibilities().add(ruleResponsibility);
420 		}
421 		for (RoleRuleResponsibility responsibility : rule.getRoleResponsibilities()) {
422 			RuleResponsibilityBo ruleResponsibility = new RuleResponsibilityBo();
423 			ruleResponsibility.setActionRequestedCd(responsibility.getActionRequestedCd());
424 			ruleResponsibility.setPriority(responsibility.getPriority());
425 			ruleResponsibility.setResponsibilityId(responsibility.getResponsibilityId());
426 			if (ruleResponsibility.getResponsibilityId() == null) {
427 				ruleResponsibility.setResponsibilityId(KEWServiceLocator.getResponsibilityIdService().getNewResponsibilityId());
428 			}
429 			ruleResponsibility.setRuleResponsibilityName(responsibility.getRoleName());
430 			ruleResponsibility.setRuleResponsibilityType(KewApiConstants.RULE_RESPONSIBILITY_ROLE_ID);
431 			ruleResponsibility.setApprovePolicy(responsibility.getApprovePolicy());
432 			rule.getRuleResponsibilities().add(ruleResponsibility);
433 		}
434 	}
435     
436     public static void translateFieldValuesForSave(RuleBaseValues rule) {
437     	RuleTemplateBo ruleTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateId(rule.getRuleTemplateId());
438 
439 		/** Populate rule extension values * */
440 		List extensions = new ArrayList();
441 		for (Iterator iterator = ruleTemplate.getActiveRuleTemplateAttributes().iterator(); iterator.hasNext();) {
442 			RuleTemplateAttributeBo ruleTemplateAttribute = (RuleTemplateAttributeBo) iterator.next();
443 			if (!ruleTemplateAttribute.isWorkflowAttribute()) {
444 				continue;
445 			}
446 			WorkflowRuleAttribute workflowAttribute = ruleTemplateAttribute.getWorkflowAttribute();
447 
448 			RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
449 			if (ruleAttribute.getType().equals(KewApiConstants.RULE_XML_ATTRIBUTE_TYPE)) {
450 				((GenericXMLRuleAttribute) workflowAttribute).setExtensionDefinition(RuleAttribute.to(ruleAttribute));
451 			}
452 
453 			Map<String, String> parameterMap = getFieldMapForRuleTemplateAttribute(rule, ruleTemplateAttribute);
454 						
455 			// validate rule data populates the rule extension values for us
456 			List<RemotableAttributeError> attValidationErrors = workflowAttribute.validateRuleData(parameterMap);
457 
458 			// because validation should be handled by business rules now, if we encounter a validation error at this point in
459 			// time, let's throw an exception
460 			if (attValidationErrors != null && !attValidationErrors.isEmpty()) {
461 				throw new RiceRuntimeException("Encountered attribute validation errors when attempting to save the Rule!");
462 			}
463 			
464 			List ruleExtensionValues = workflowAttribute.getRuleExtensionValues();
465 			if (ruleExtensionValues != null && !ruleExtensionValues.isEmpty()) {
466 				RuleExtensionBo ruleExtension = new RuleExtensionBo();
467 				ruleExtension.setRuleTemplateAttributeId(ruleTemplateAttribute.getId());
468 
469 				ruleExtension.setExtensionValues(ruleExtensionValues);
470 				extensions.add(ruleExtension);
471 			}
472 				
473 		}
474 		rule.setRuleExtensions(extensions);
475 
476 		for (Iterator iterator = rule.getRuleExtensions().iterator(); iterator.hasNext();) {
477 			RuleExtensionBo ruleExtension = (RuleExtensionBo) iterator.next();
478 			ruleExtension.setRuleBaseValues(rule);
479 
480 			for (Iterator iterator2 = ruleTemplate.getActiveRuleTemplateAttributes().iterator(); iterator2.hasNext();) {
481 				RuleTemplateAttributeBo ruleTemplateAttribute = (RuleTemplateAttributeBo) iterator2.next();
482 				if (StringUtils.equals(ruleTemplateAttribute.getId(), ruleExtension.getRuleTemplateAttributeId())) {
483 					ruleExtension.setRuleTemplateAttribute(ruleTemplateAttribute);
484 					break;
485 				}
486 			}
487 
488 			for (Iterator iterator2 = ruleExtension.getExtensionValues().iterator(); iterator2.hasNext();) {
489 				RuleExtensionValue ruleExtensionValue = (RuleExtensionValue) iterator2.next();
490 				ruleExtensionValue.setExtension(ruleExtension);
491 			}
492 		}
493     }
494 
495     /**
496      * Based on original logic implemented in Rule system.  Essentially constructs a Map of field values related
497      * to the given RuleTemplateAttribute.
498      */
499     public static Map<String, String> getFieldMapForRuleTemplateAttribute(RuleBaseValues rule, RuleTemplateAttributeContract ruleTemplateAttribute) {
500     	Map<String, String> fieldMap = new HashMap<String, String>();
501     	for (String fieldKey : rule.getFieldValues().keySet()) {
502     		String ruleTemplateAttributeId = fieldKey.substring(0, fieldKey.indexOf(ID_SEPARATOR));
503     		String fieldName = fieldKey.substring(fieldKey.indexOf(ID_SEPARATOR) + 1);
504     		if (ruleTemplateAttribute.getId().equals(ruleTemplateAttributeId)) {
505     			fieldMap.put(fieldName, rule.getFieldValues().get(fieldKey));
506     		}
507     	}
508     	return fieldMap;
509     }
510     
511     public static void processRuleForDelegationSave(RuleDelegationBo ruleDelegation) {
512     	RuleBaseValues rule = ruleDelegation.getDelegationRule();
513     	rule.setDelegateRule(true);
514     	// certain items on a delegated rule responsibility are inherited from parent responsibility, set them to null
515     	for (RuleResponsibilityBo responsibility : rule.getRuleResponsibilities()) {
516     		responsibility.setActionRequestedCd(null);
517     		responsibility.setPriority(null);
518     	}
519     }
520     
521     public static void populateForCopyOrEdit(RuleBaseValues oldRule, RuleBaseValues newRule) {
522 		populateRuleMaintenanceFields(oldRule);
523 		populateRuleMaintenanceFields(newRule);
524 		// in the case of copy, our fields which are marked read only are cleared, this includes the rule template
525 		// name and the document type name but we don't want these cleared
526 		if (newRule.getRuleTemplate().getName() == null) {
527 			newRule.getRuleTemplate().setName(oldRule.getRuleTemplate().getName());
528 		}
529 		if (newRule.getDocTypeName() == null) {
530 			newRule.setDocTypeName(oldRule.getDocTypeName());
531 		}
532 	}
533     
534     /**
535 	 * This method populates fields on RuleBaseValues which are used only for
536 	 * maintenance purposes.  In otherwords, it populates the non-persistent fields
537 	 * on the RuleBaseValues which the maintenance document needs to function
538 	 * (such as the extension field values and responsibilities).
539 	 */
540 	public static void populateRuleMaintenanceFields(RuleBaseValues rule) {
541 		translateResponsibilitiesForLoad(rule);
542 		translateRuleExtensionsForLoad(rule);
543 	}
544 	
545 	public static void translateResponsibilitiesForLoad(RuleBaseValues rule) {
546 		for (RuleResponsibilityBo responsibility : rule.getRuleResponsibilities()) {
547 			if (responsibility.getRuleResponsibilityType().equals(KewApiConstants.RULE_RESPONSIBILITY_WORKFLOW_ID)) {
548 				PersonRuleResponsibility personResponsibility = new PersonRuleResponsibility();
549 				copyResponsibility(responsibility, personResponsibility);
550 				Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipal(personResponsibility.getRuleResponsibilityName());
551 				personResponsibility.setPrincipalName(principal.getPrincipalName());
552 				rule.getPersonResponsibilities().add(personResponsibility);
553 			} else if (responsibility.getRuleResponsibilityType().equals(KewApiConstants.RULE_RESPONSIBILITY_GROUP_ID)) {
554 				GroupRuleResponsibility groupResponsibility = new GroupRuleResponsibility();
555 				copyResponsibility(responsibility, groupResponsibility);
556 				Group group = KEWServiceLocator.getIdentityHelperService().getGroup(groupResponsibility.getRuleResponsibilityName());
557 				groupResponsibility.setNamespaceCode(group.getNamespaceCode());
558 				groupResponsibility.setName(group.getName());
559 				rule.getGroupResponsibilities().add(groupResponsibility);
560 			} else if (responsibility.getRuleResponsibilityType().equals(KewApiConstants.RULE_RESPONSIBILITY_ROLE_ID)) {
561 				RoleRuleResponsibility roleResponsibility = new RoleRuleResponsibility();
562 				copyResponsibility(responsibility, roleResponsibility);
563 				rule.getRoleResponsibilities().add(roleResponsibility);
564 			} else {
565 				throw new RiceRuntimeException("Original responsibility with id '" + responsibility.getId() + "' contained a bad type code of '" + responsibility.getRuleResponsibilityType());
566 			}
567 		}
568 		// since we've loaded the responsibilities, let's clear the originals so they don't get serialized to the maint doc XML
569 		rule.getRuleResponsibilities().clear();
570 	}
571 	
572 	public static void copyResponsibility(RuleResponsibilityBo source, RuleResponsibilityBo target) {
573 		try {
574 			BeanUtils.copyProperties(target, source);
575 		} catch (Exception e) {
576 			throw new RiceRuntimeException("Failed to copy properties from source to target responsibility", e);
577 		}
578 	}
579 	
580 	public static void translateRuleExtensionsForLoad(RuleBaseValues rule) {
581 		for (RuleExtensionBo ruleExtension : rule.getRuleExtensions()) {
582 			String ruleTemplateAttributeId = ruleExtension.getRuleTemplateAttributeId();
583 			for (RuleExtensionValue ruleExtensionValue : ruleExtension.getExtensionValues()) {
584 				String fieldMapKey = ruleTemplateAttributeId + ID_SEPARATOR + ruleExtensionValue.getKey();
585 				rule.getFieldValues().put(fieldMapKey, ruleExtensionValue.getValue());
586 			}
587 		}
588 		// since we've loaded the extensions, let's clear the originals so that they don't get serialized to the maint doc XML
589 		rule.getRuleExtensions().clear();
590 	}
591 	
592 	public static void processRuleForCopy(String documentNumber, RuleBaseValues oldRule, RuleBaseValues newRule) {
593 		WebRuleUtils.populateForCopyOrEdit(oldRule, newRule);
594 		clearKeysForCopy(newRule);
595 		newRule.setDocumentId(documentNumber);
596 	}
597 	
598 	public static void clearKeysForCopy(RuleBaseValues rule) {    	
599     	rule.setId(null);
600     	rule.setPreviousRuleId(null);
601     	rule.setPreviousVersion(null);
602     	rule.setName(null);
603     	for (PersonRuleResponsibility responsibility : rule.getPersonResponsibilities()) {
604     		clearResponsibilityKeys(responsibility);
605     	}
606     	for (GroupRuleResponsibility responsibility : rule.getGroupResponsibilities()) {
607     		clearResponsibilityKeys(responsibility);
608     	}
609     	for (RoleRuleResponsibility responsibility : rule.getRoleResponsibilities()) {
610     		clearResponsibilityKeys(responsibility);
611     	}
612     }
613 
614     private static void clearResponsibilityKeys(RuleResponsibilityBo responsibility) {
615 		responsibility.setResponsibilityId(null);
616 		responsibility.setId(null);
617 		responsibility.setRuleBaseValuesId(null);
618     }
619     
620 }