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;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.jdom.Document;
20  import org.jdom.Element;
21  import org.kuali.rice.core.api.uif.RemotableAttributeError;
22  import org.kuali.rice.core.api.util.xml.XmlHelper;
23  import org.kuali.rice.kew.api.WorkflowRuntimeException;
24  import org.kuali.rice.kew.doctype.bo.DocumentType;
25  import org.kuali.rice.kew.doctype.service.DocumentTypeService;
26  import org.kuali.rice.kew.exception.WorkflowServiceError;
27  import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
28  import org.kuali.rice.kew.routeheader.DocumentContent;
29  import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
30  import org.kuali.rice.kew.service.KEWServiceLocator;
31  import org.kuali.rice.kns.web.ui.Field;
32  import org.kuali.rice.kns.web.ui.Row;
33  
34  import javax.xml.xpath.XPath;
35  import javax.xml.xpath.XPathExpressionException;
36  import java.io.StringReader;
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.Map;
42  
43  
44  /**
45   * A {@link WorkflowRuleAttribute} which is used to route a rule based on the
46   * {@link DocumentType} of the rule which is created.
47   *
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   */
50  public class RuleRoutingAttribute implements WorkflowRuleAttribute {
51  
52  	private static final long serialVersionUID = -8884711461398770563L;
53  
54  	private static final String DOC_TYPE_NAME_PROPERTY = "docTypeFullName";//doc_type_name
55      private static final String DOC_TYPE_NAME_KEY = "docTypeFullName";
56  
57      private static final String LOOKUPABLE_CLASS = "org.kuali.rice.kew.doctype.bo.DocumentType";//DocumentTypeLookupableImplService//org.kuali.rice.kew.doctype.bo.DocumentType
58      private static final String DOC_TYPE_NAME_LABEL = "Document type name";
59  
60      private static final String DOC_TYPE_NAME_XPATH = "//newMaintainableObject/businessObject/docTypeName";
61      private static final String DOC_TYPE_NAME_DEL_XPATH = "//newMaintainableObject/businessObject/delegationRuleBaseValues/docTypeName";
62  
63      private String doctypeName;
64      private List<Row> rows;
65      private boolean required;
66  
67      public RuleRoutingAttribute(String docTypeName) {
68          this();
69          setDoctypeName(docTypeName);
70      }
71  
72      public RuleRoutingAttribute() {
73          buildRows();
74      }
75  
76      private void buildRows() {
77          rows = new ArrayList<Row>();
78  
79          List<Field> fields = new ArrayList<Field>();
80          fields.add(new Field(DOC_TYPE_NAME_LABEL, "", Field.TEXT, false, DOC_TYPE_NAME_PROPERTY, "", false, false, null, LOOKUPABLE_CLASS));
81          //fields.add(new Field(DOC_TYPE_NAME_LABEL, "", Field.TEXT, false, DOC_TYPE_NAME_KEY, "", false, false, null, LOOKUPABLE_CLASS));
82          rows.add(new Row(fields));
83      }
84  
85      @Override
86      public boolean isMatch(DocumentContent docContent, List ruleExtensions) {
87  	setDoctypeName(getRuleDocumentTypeFromRuleExtensions(ruleExtensions));
88          DocumentTypeService service = (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE);
89          
90  		try {
91  			String docTypeName = getDocTypNameFromXML(docContent);
92              if (docTypeName.equals(getDoctypeName())) {
93                  return true;
94              }
95              DocumentType documentType = service.findByName(docTypeName);
96              while (documentType != null && documentType.getParentDocType() != null) {
97                  documentType = documentType.getParentDocType();
98                  if(documentType.getName().equals(getDoctypeName())){
99                      return true;
100                 }
101             }
102 		} catch (XPathExpressionException e) {
103 			throw new WorkflowRuntimeException(e);
104 		}
105 		
106 		
107         if (ruleExtensions.isEmpty()) {
108             return true;
109         }
110         return false;
111     }
112 
113     protected String getRuleDocumentTypeFromRuleExtensions(List ruleExtensions) {
114 	for (Iterator extensionsIterator = ruleExtensions.iterator(); extensionsIterator.hasNext();) {
115             RuleExtensionBo extension = (RuleExtensionBo) extensionsIterator.next();
116             if (extension.getRuleTemplateAttribute().getRuleAttribute().getResourceDescriptor().equals(getClass().getName())) {
117                 for (Iterator valuesIterator = extension.getExtensionValues().iterator(); valuesIterator.hasNext();) {
118                     RuleExtensionValue extensionValue = (RuleExtensionValue) valuesIterator.next();
119                     String key = extensionValue.getKey();
120                     String value = extensionValue.getValue();
121                     if (key.equals(DOC_TYPE_NAME_KEY)) {
122                         return value;
123                     }
124                 }
125             }
126         }
127 	return null;
128     }
129 
130     @Override
131     public List getRuleRows() {
132         return rows;
133     }
134 
135     @Override
136     public List getRoutingDataRows() {
137         return rows;
138     }
139 
140     @Override
141     public String getDocContent() {
142         if (!org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) {
143             return "<ruleRouting><doctype>" + getDoctypeName() + "</doctype></ruleRouting>";
144         } else {
145             return "";
146         }
147     }
148   
149 
150 	private String getDocTypNameFromXML(DocumentContent docContent) throws XPathExpressionException {
151 		XPath xPath = XPathHelper.newXPath();
152 		String docTypeName = xPath.evaluate(DOC_TYPE_NAME_XPATH, docContent.getDocument());
153 				
154 		if (StringUtils.isBlank(docTypeName)) {
155 			docTypeName = xPath.evaluate(DOC_TYPE_NAME_DEL_XPATH, docContent.getDocument());
156 			
157 			if (StringUtils.isBlank(docTypeName)) {
158 				throw new WorkflowRuntimeException("Could not locate Document Type Name on the document: " + 
159 						docContent.getRouteContext().getDocument().getDocumentId());
160 			}
161 		} 
162 		return docTypeName;
163 	}
164 
165 
166     public List<RuleRoutingAttribute> parseDocContent(DocumentContent docContent) {
167         try {
168             Document doc2 = (Document) XmlHelper.buildJDocument(new StringReader(docContent.getDocContent()));
169             
170             List<RuleRoutingAttribute> doctypeAttributes = new ArrayList<RuleRoutingAttribute>();
171             Collection<Element> ruleRoutings = XmlHelper.findElements(doc2.getRootElement(), "docTypeName");
172             List<String> usedDTs = new ArrayList<String>();
173             for (Iterator<Element> iter = ruleRoutings.iterator(); iter.hasNext();) {
174                 Element ruleRoutingElement = (Element) iter.next();
175 
176                 //Element docTypeElement = ruleRoutingElement.getChild("doctype");
177                 Element docTypeElement = ruleRoutingElement;
178                 String elTxt = docTypeElement.getText();
179                 if (docTypeElement != null && !usedDTs.contains(elTxt)) {
180                 	usedDTs.add(elTxt);
181                     doctypeAttributes.add(new RuleRoutingAttribute(elTxt));
182                 }
183             }
184 
185             return doctypeAttributes;
186         } catch (Exception e) {
187             throw new RuntimeException(e);
188         }
189     }
190 
191     @Override
192     public List getRuleExtensionValues() {
193         List extensions = new ArrayList();
194 
195         if (!org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) {
196             RuleExtensionValue extension = new RuleExtensionValue();
197             extension.setKey(DOC_TYPE_NAME_KEY);
198             extension.setValue(getDoctypeName());
199             extensions.add(extension);
200         }
201 
202         return extensions;
203     }
204 
205     @Override
206     public List<RemotableAttributeError> validateRoutingData(Map paramMap) {
207         List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
208         setDoctypeName((String) paramMap.get(DOC_TYPE_NAME_PROPERTY));
209         if (isRequired() && org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) {
210             errors.add(RemotableAttributeError.Builder.create("routetemplate.ruleroutingattribute.doctype.invalid", "doc type is not valid.").build());
211         }
212 
213         if (!org.apache.commons.lang.StringUtils.isEmpty(getDoctypeName())) {
214             DocumentTypeService service = (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE);
215             DocumentType documentType = service.findByName(getDoctypeName());
216             if (documentType == null) {
217                 errors.add(RemotableAttributeError.Builder.create("routetemplate.ruleroutingattribute.doctype.invalid", "doc type is not valid").build());
218             }
219         }
220         return errors;
221     }
222 
223     @Override
224     public List<RemotableAttributeError> validateRuleData(Map paramMap) {
225         return validateRoutingData(paramMap);
226     }
227 
228     public String getDoctypeName() {
229         return this.doctypeName;
230     }
231 
232     public void setDoctypeName(String docTypeName) {
233         this.doctypeName = docTypeName;
234     }
235 
236     public void setRequired(boolean required) {
237         this.required = required;
238     }
239 
240     public boolean isRequired() {
241         return required;
242     }
243 }