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.kew.rule; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.jdom.Document; 020 import org.jdom.Element; 021 import org.kuali.rice.core.api.uif.RemotableAttributeError; 022 import org.kuali.rice.core.api.util.xml.XmlHelper; 023 import org.kuali.rice.kew.api.WorkflowRuntimeException; 024 import org.kuali.rice.kew.api.rule.RuleExtension; 025 import org.kuali.rice.kew.doctype.bo.DocumentType; 026 import org.kuali.rice.kew.doctype.service.DocumentTypeService; 027 import org.kuali.rice.kew.exception.WorkflowServiceError; 028 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl; 029 import org.kuali.rice.kew.routeheader.DocumentContent; 030 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper; 031 import org.kuali.rice.kew.service.KEWServiceLocator; 032 import org.kuali.rice.kns.web.ui.Field; 033 import org.kuali.rice.kns.web.ui.Row; 034 035 import javax.xml.xpath.XPath; 036 import javax.xml.xpath.XPathExpressionException; 037 import java.io.StringReader; 038 import java.util.ArrayList; 039 import java.util.Collection; 040 import java.util.Iterator; 041 import java.util.List; 042 import java.util.Map; 043 044 045 /** 046 * A {@link WorkflowRuleAttribute} which is used to route a rule based on the 047 * {@link DocumentType} of the rule which is created. 048 * 049 * @author Kuali Rice Team (rice.collab@kuali.org) 050 */ 051 public class RuleRoutingAttribute implements WorkflowRuleAttribute { 052 053 private static final long serialVersionUID = -8884711461398770563L; 054 055 private static final String DOC_TYPE_NAME_PROPERTY = "docTypeFullName";//doc_type_name 056 private static final String DOC_TYPE_NAME_KEY = "docTypeFullName"; 057 058 private static final String LOOKUPABLE_CLASS = "org.kuali.rice.kew.doctype.bo.DocumentType";//DocumentTypeLookupableImplService//org.kuali.rice.kew.doctype.bo.DocumentType 059 private static final String DOC_TYPE_NAME_LABEL = "Document type name"; 060 061 private static final String DOC_TYPE_NAME_XPATH = "//newMaintainableObject/businessObject/docTypeName"; 062 private static final String DOC_TYPE_NAME_DEL_XPATH = "//newMaintainableObject/businessObject/delegationRule/docTypeName"; 063 064 private String doctypeName; 065 private List<Row> rows; 066 private boolean required; 067 068 public RuleRoutingAttribute(String docTypeName) { 069 this(); 070 setDoctypeName(docTypeName); 071 } 072 073 public RuleRoutingAttribute() { 074 buildRows(); 075 } 076 077 private void buildRows() { 078 rows = new ArrayList<Row>(); 079 080 List<Field> fields = new ArrayList<Field>(); 081 fields.add(new Field(DOC_TYPE_NAME_LABEL, "", Field.TEXT, false, DOC_TYPE_NAME_PROPERTY, "", false, false, null, LOOKUPABLE_CLASS)); 082 //fields.add(new Field(DOC_TYPE_NAME_LABEL, "", Field.TEXT, false, DOC_TYPE_NAME_KEY, "", false, false, null, LOOKUPABLE_CLASS)); 083 rows.add(new Row(fields)); 084 } 085 086 @Override 087 public boolean isMatch(DocumentContent docContent, List<RuleExtension> ruleExtensions) { 088 setDoctypeName(getRuleDocumentTypeFromRuleExtensions(ruleExtensions)); 089 DocumentTypeService service = (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE); 090 091 try { 092 String docTypeName = getDocTypNameFromXML(docContent); 093 if (docTypeName.equals(getDoctypeName())) { 094 return true; 095 } 096 DocumentType documentType = service.findByName(docTypeName); 097 while (documentType != null && documentType.getParentDocType() != null) { 098 documentType = documentType.getParentDocType(); 099 if(documentType.getName().equals(getDoctypeName())){ 100 return true; 101 } 102 } 103 } catch (XPathExpressionException e) { 104 throw new WorkflowRuntimeException(e); 105 } 106 107 108 if (ruleExtensions.isEmpty()) { 109 return true; 110 } 111 return false; 112 } 113 114 protected String getRuleDocumentTypeFromRuleExtensions(List<RuleExtension> ruleExtensions) { 115 for (RuleExtension extension : ruleExtensions) { 116 if (extension.getRuleTemplateAttribute().getRuleAttribute().getResourceDescriptor().equals(getClass().getName())) { 117 for (Map.Entry<String, String> extensionValue : extension.getExtensionValuesMap().entrySet()) { 118 String key = extensionValue.getKey(); 119 String value = extensionValue.getValue(); 120 if (key.equals(DOC_TYPE_NAME_KEY)) { 121 return value; 122 } 123 } 124 } 125 } 126 return null; 127 } 128 129 @Override 130 public List getRuleRows() { 131 return rows; 132 } 133 134 @Override 135 public List getRoutingDataRows() { 136 return rows; 137 } 138 139 @Override 140 public String getDocContent() { 141 if (!org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) { 142 return "<ruleRouting><doctype>" + getDoctypeName() + "</doctype></ruleRouting>"; 143 } else { 144 return ""; 145 } 146 } 147 148 149 private String getDocTypNameFromXML(DocumentContent docContent) throws XPathExpressionException { 150 XPath xPath = XPathHelper.newXPath(); 151 String docTypeName = xPath.evaluate(DOC_TYPE_NAME_XPATH, docContent.getDocument()); 152 153 if (StringUtils.isBlank(docTypeName)) { 154 docTypeName = xPath.evaluate(DOC_TYPE_NAME_DEL_XPATH, docContent.getDocument()); 155 156 if (StringUtils.isBlank(docTypeName)) { 157 throw new WorkflowRuntimeException("Could not locate Document Type Name on the document: " + 158 docContent.getRouteContext().getDocument().getDocumentId()); 159 } 160 } 161 return docTypeName; 162 } 163 164 165 public List<RuleRoutingAttribute> parseDocContent(DocumentContent docContent) { 166 try { 167 Document doc2 = (Document) XmlHelper.buildJDocument(new StringReader(docContent.getDocContent())); 168 169 List<RuleRoutingAttribute> doctypeAttributes = new ArrayList<RuleRoutingAttribute>(); 170 Collection<Element> ruleRoutings = XmlHelper.findElements(doc2.getRootElement(), "docTypeName"); 171 List<String> usedDTs = new ArrayList<String>(); 172 for (Iterator<Element> iter = ruleRoutings.iterator(); iter.hasNext();) { 173 Element ruleRoutingElement = (Element) iter.next(); 174 175 //Element docTypeElement = ruleRoutingElement.getChild("doctype"); 176 Element docTypeElement = ruleRoutingElement; 177 String elTxt = docTypeElement.getText(); 178 if (docTypeElement != null && !usedDTs.contains(elTxt)) { 179 usedDTs.add(elTxt); 180 doctypeAttributes.add(new RuleRoutingAttribute(elTxt)); 181 } 182 } 183 184 return doctypeAttributes; 185 } catch (Exception e) { 186 throw new RuntimeException(e); 187 } 188 } 189 190 @Override 191 public List getRuleExtensionValues() { 192 List extensions = new ArrayList(); 193 194 if (!org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) { 195 RuleExtensionValue extension = new RuleExtensionValue(); 196 extension.setKey(DOC_TYPE_NAME_KEY); 197 extension.setValue(getDoctypeName()); 198 extensions.add(extension); 199 } 200 201 return extensions; 202 } 203 204 @Override 205 public List<RemotableAttributeError> validateRoutingData(Map paramMap) { 206 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>(); 207 setDoctypeName((String) paramMap.get(DOC_TYPE_NAME_PROPERTY)); 208 if (isRequired() && org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) { 209 errors.add(RemotableAttributeError.Builder.create("routetemplate.ruleroutingattribute.doctype.invalid", "doc type is not valid.").build()); 210 } 211 212 if (!org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) { 213 DocumentTypeService service = (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE); 214 DocumentType documentType = service.findByName(getDoctypeName()); 215 if (documentType == null) { 216 errors.add(RemotableAttributeError.Builder.create("routetemplate.ruleroutingattribute.doctype.invalid", "doc type is not valid").build()); 217 } 218 } 219 return errors; 220 } 221 222 @Override 223 public List<RemotableAttributeError> validateRuleData(Map paramMap) { 224 return validateRoutingData(paramMap); 225 } 226 227 public String getDoctypeName() { 228 return this.doctypeName; 229 } 230 231 public void setDoctypeName(String docTypeName) { 232 this.doctypeName = docTypeName; 233 } 234 235 public void setRequired(boolean required) { 236 this.required = required; 237 } 238 239 public boolean isRequired() { 240 return required; 241 } 242 }