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.kns.workflow; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Map; 021import java.util.Properties; 022 023import javax.xml.xpath.XPath; 024import javax.xml.xpath.XPathExpressionException; 025import javax.xml.xpath.XPathFactory; 026 027import org.apache.commons.lang.StringUtils; 028import org.kuali.rice.kew.engine.RouteContext; 029import org.kuali.rice.kew.rule.xmlrouting.WorkflowFunctionResolver; 030import org.kuali.rice.kew.rule.xmlrouting.WorkflowNamespaceContext; 031import org.kuali.rice.kns.util.FieldUtils; 032import org.kuali.rice.kns.web.ui.Field; 033import org.kuali.rice.kns.web.ui.Row; 034import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 035import org.kuali.rice.krad.util.KRADConstants; 036import org.kuali.rice.krad.util.KRADPropertyConstants; 037import org.kuali.rice.krad.util.UrlFactory; 038import org.w3c.dom.Document; 039 040/** 041 * @author Kuali Rice Team (rice.collab@kuali.org) 042 */ 043public final class WorkflowUtils { 044 private static final String XPATH_ROUTE_CONTEXT_KEY = "_xpathKey"; 045 public static final String XSTREAM_SAFE_PREFIX = "wf:xstreamsafe('"; 046 public static final String XSTREAM_SAFE_SUFFIX = "')"; 047 public static final String XSTREAM_MATCH_ANYWHERE_PREFIX = "//"; 048 public static final String XSTREAM_MATCH_RELATIVE_PREFIX = "./"; 049 050 private WorkflowUtils() { 051 throw new UnsupportedOperationException("do not call"); 052 } 053 054 /** 055 * 056 * This method sets up the XPath with the correct workflow namespace and resolver initialized. This ensures that the XPath 057 * statements can use required workflow functions as part of the XPath statements. 058 * 059 * @param document - document 060 * @return a fully initialized XPath instance that has access to the workflow resolver and namespace. 061 * 062 */ 063 public final static XPath getXPath(Document document) { 064 XPath xpath = getXPath(RouteContext.getCurrentRouteContext()); 065 xpath.setNamespaceContext(new WorkflowNamespaceContext()); 066 WorkflowFunctionResolver resolver = new WorkflowFunctionResolver(); 067 resolver.setXpath(xpath); 068 resolver.setRootNode(document); 069 xpath.setXPathFunctionResolver(resolver); 070 return xpath; 071 } 072 073 public final static XPath getXPath(RouteContext routeContext) { 074 if (routeContext == null) { 075 return XPathFactory.newInstance().newXPath(); 076 } 077 if (!routeContext.getParameters().containsKey(XPATH_ROUTE_CONTEXT_KEY)) { 078 routeContext.getParameters().put(XPATH_ROUTE_CONTEXT_KEY, XPathFactory.newInstance().newXPath()); 079 } 080 return (XPath) routeContext.getParameters().get(XPATH_ROUTE_CONTEXT_KEY); 081 } 082 083 /** 084 * This method will do a simple XPath.evaluate, while wrapping your xpathExpression with the xstreamSafe function. It assumes a 085 * String result, and will return such. If an XPathExpressionException is thrown, this will be re-thrown within a 086 * RuntimeException. 087 * 088 * @param xpath A correctly initialized XPath instance. 089 * @param xpathExpression Your XPath Expression that needs to be wrapped in an xstreamSafe wrapper and run. 090 * @param item The document contents you will be searching within. 091 * @return The string value of the xpath.evaluate(). 092 */ 093 public static final String xstreamSafeEval(XPath xpath, String xpathExpression, Object item) { 094 String xstreamSafeXPath = xstreamSafeXPath(xpathExpression); 095 String evalResult = ""; 096 try { 097 evalResult = xpath.evaluate(xstreamSafeXPath, item); 098 } 099 catch (XPathExpressionException e) { 100 throw new RuntimeException("XPathExpressionException occurred on xpath: " + xstreamSafeXPath, e); 101 } 102 return evalResult; 103 } 104 105 /** 106 * This method wraps the passed-in XPath expression in XStream Safe wrappers, so that XStream generated reference links will be 107 * handled correctly. 108 * 109 * @param xpathExpression The XPath Expression you wish to use. 110 * @return Your XPath Expression wrapped in the XStreamSafe wrapper. 111 */ 112 public static final String xstreamSafeXPath(String xpathExpression) { 113 return new StringBuilder(XSTREAM_SAFE_PREFIX).append(xpathExpression).append(XSTREAM_SAFE_SUFFIX).toString(); 114 } 115 116 /** 117 * This method returns a label from the data dictionary service 118 * 119 * @param businessObjectClass - class where the label should come from 120 * @param attributeName - name of the attribute you need the label for 121 * @return the label from the data dictionary for the given Class and attributeName or null if not found 122 */ 123 public static final String getBusinessObjectAttributeLabel(Class businessObjectClass, String attributeName) { 124 return KRADServiceLocatorWeb.getDataDictionaryService().getAttributeLabel(businessObjectClass, attributeName); 125 } 126 127 128 /** 129 * This method builds a workflow-lookup-screen Row of type TEXT, with no quickfinder/lookup. 130 * 131 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber. 132 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for 133 * Account.accountNumber. 134 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This 135 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so 136 * everything links up correctly. 137 * @return A populated and ready-to-use workflow lookupable.Row. 138 */ 139 public static Row buildTextRow(Class propertyClass, String boPropertyName, String workflowPropertyKey) { 140 if (propertyClass == null) { 141 throw new IllegalArgumentException("Method parameter 'propertyClass' was passed a NULL value."); 142 } 143 if (StringUtils.isBlank(boPropertyName)) { 144 throw new IllegalArgumentException("Method parameter 'boPropertyName' was passed a NULL or blank value."); 145 } 146 if (StringUtils.isBlank(workflowPropertyKey)) { 147 throw new IllegalArgumentException("Method parameter 'workflowPropertyKey' was passed a NULL or blank value."); 148 } 149 List<Field> fields = new ArrayList<Field>(); 150 Field field; 151 field = FieldUtils.getPropertyField(propertyClass, boPropertyName, false); 152 fields.add(field); 153 return new Row(fields); 154 } 155 156 /** 157 * This method builds a workflow-lookup-screen Row of type TEXT, with the attached lookup icon and functionality. 158 * 159 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber. 160 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for 161 * Account.accountNumber. 162 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This 163 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so 164 * everything links up correctly. 165 * @return A populated and ready-to-use workflow lookupable.Row, which includes both the property field and the lookup icon. 166 */ 167 public static Row buildTextRowWithLookup(Class propertyClass, String boPropertyName, String workflowPropertyKey) { 168 return buildTextRowWithLookup(propertyClass, boPropertyName, workflowPropertyKey, null); 169 } 170 171 /** 172 * This method builds a workflow-lookup-screen Row of type TEXT, with the attached lookup icon and functionality. 173 * 174 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber. 175 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for 176 * Account.accountNumber. 177 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This 178 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so 179 * everything links up correctly. 180 * @param fieldConversionsByBoPropertyName A list of extra field conversions where the key is the business object property name 181 * and the value is the workflow property key 182 * @return A populated and ready-to-use workflow lookupable.Row, which includes both the property field and the lookup icon. 183 */ 184 public static Row buildTextRowWithLookup(Class propertyClass, String boPropertyName, String workflowPropertyKey, Map fieldConversionsByBoPropertyName) { 185 if (propertyClass == null) { 186 throw new IllegalArgumentException("Method parameter 'propertyClass' was passed a NULL value."); 187 } 188 if (StringUtils.isBlank(boPropertyName)) { 189 throw new IllegalArgumentException("Method parameter 'boPropertyName' was passed a NULL or blank value."); 190 } 191 if (StringUtils.isBlank(workflowPropertyKey)) { 192 throw new IllegalArgumentException("Method parameter 'workflowPropertyKey' was passed a NULL or blank value."); 193 } 194 Field field; 195 field = FieldUtils.getPropertyField(propertyClass, boPropertyName, false); 196 197 List<Field> fields = new ArrayList<Field>(); 198 fields.add(field); 199 return new Row(fields); 200 } 201 202 /** 203 * This method builds a workflow-lookup-screen Row of type DROPDOWN. 204 * 205 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber. 206 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for 207 * Account.accountNumber. 208 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This 209 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so 210 * everything links up correctly. 211 * @param optionMap The map of value, text pairs that will be used to constuct the dropdown list. 212 * @return A populated and ready-to-use workflow lookupable.Row. 213 */ 214 public static Row buildDropdownRow(Class propertyClass, String boPropertyName, String workflowPropertyKey, Map<String, String> optionMap, boolean addBlankRow) { 215 if (propertyClass == null) { 216 throw new IllegalArgumentException("Method parameter 'propertyClass' was passed a NULL value."); 217 } 218 if (StringUtils.isBlank(boPropertyName)) { 219 throw new IllegalArgumentException("Method parameter 'boPropertyName' was passed a NULL or blank value."); 220 } 221 if (StringUtils.isBlank(workflowPropertyKey)) { 222 throw new IllegalArgumentException("Method parameter 'workflowPropertyKey' was passed a NULL or blank value."); 223 } 224 if (optionMap == null) { 225 throw new IllegalArgumentException("Method parameter 'optionMap' was passed a NULL value."); 226 } 227 List<Field> fields = new ArrayList<Field>(); 228 Field field; 229 field = FieldUtils.getPropertyField(propertyClass, boPropertyName, false); 230 fields.add(field); 231 return new Row(fields); 232 } 233}