1 /*
2 * Copyright 2007 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.kns.workflow;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Properties;
22
23 import javax.xml.xpath.XPath;
24 import javax.xml.xpath.XPathExpressionException;
25 import javax.xml.xpath.XPathFactory;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.kuali.rice.kew.engine.RouteContext;
29 import org.kuali.rice.kew.rule.xmlrouting.WorkflowFunctionResolver;
30 import org.kuali.rice.kew.rule.xmlrouting.WorkflowNamespaceContext;
31 import org.kuali.rice.kns.service.KNSServiceLocator;
32 import org.kuali.rice.kns.util.FieldUtils;
33 import org.kuali.rice.kns.util.KNSConstants;
34 import org.kuali.rice.kns.util.KNSPropertyConstants;
35 import org.kuali.rice.kns.util.UrlFactory;
36 import org.kuali.rice.kns.web.ui.Field;
37 import org.kuali.rice.kns.web.ui.Row;
38 import org.w3c.dom.Document;
39
40
41 public class WorkflowUtils {
42 private static final String XPATH_ROUTE_CONTEXT_KEY = "_xpathKey";
43 public static final String XSTREAM_SAFE_PREFIX = "wf:xstreamsafe('";
44 public static final String XSTREAM_SAFE_SUFFIX = "')";
45 public static final String XSTREAM_MATCH_ANYWHERE_PREFIX = "//";
46 public static final String XSTREAM_MATCH_RELATIVE_PREFIX = "./";
47
48 /**
49 *
50 * This method sets up the XPath with the correct workflow namespace and resolver initialized. This ensures that the XPath
51 * statements can use required workflow functions as part of the XPath statements.
52 *
53 * @param document - document
54 * @return a fully initialized XPath instance that has access to the workflow resolver and namespace.
55 *
56 */
57 public final static XPath getXPath(Document document) {
58 XPath xpath = getXPath(RouteContext.getCurrentRouteContext());
59 xpath.setNamespaceContext(new WorkflowNamespaceContext());
60 WorkflowFunctionResolver resolver = new WorkflowFunctionResolver();
61 resolver.setXpath(xpath);
62 resolver.setRootNode(document);
63 xpath.setXPathFunctionResolver(resolver);
64 return xpath;
65 }
66
67 public final static XPath getXPath(RouteContext routeContext) {
68 if (routeContext == null) {
69 return XPathFactory.newInstance().newXPath();
70 }
71 if (!routeContext.getParameters().containsKey(XPATH_ROUTE_CONTEXT_KEY)) {
72 routeContext.getParameters().put(XPATH_ROUTE_CONTEXT_KEY, XPathFactory.newInstance().newXPath());
73 }
74 return (XPath) routeContext.getParameters().get(XPATH_ROUTE_CONTEXT_KEY);
75 }
76
77 /**
78 * This method will do a simple XPath.evaluate, while wrapping your xpathExpression with the xstreamSafe function. It assumes a
79 * String result, and will return such. If an XPathExpressionException is thrown, this will be re-thrown within a
80 * RuntimeException.
81 *
82 * @param xpath A correctly initialized XPath instance.
83 * @param xpathExpression Your XPath Expression that needs to be wrapped in an xstreamSafe wrapper and run.
84 * @param item The document contents you will be searching within.
85 * @return The string value of the xpath.evaluate().
86 */
87 public static final String xstreamSafeEval(XPath xpath, String xpathExpression, Object item) {
88 String xstreamSafeXPath = xstreamSafeXPath(xpathExpression);
89 String evalResult = "";
90 try {
91 evalResult = xpath.evaluate(xstreamSafeXPath, item);
92 }
93 catch (XPathExpressionException e) {
94 throw new RuntimeException("XPathExpressionException occurred on xpath: " + xstreamSafeXPath, e);
95 }
96 return evalResult;
97 }
98
99 /**
100 * This method wraps the passed-in XPath expression in XStream Safe wrappers, so that XStream generated reference links will be
101 * handled correctly.
102 *
103 * @param xpathExpression The XPath Expression you wish to use.
104 * @return Your XPath Expression wrapped in the XStreamSafe wrapper.
105 */
106 public static final String xstreamSafeXPath(String xpathExpression) {
107 return new StringBuilder(XSTREAM_SAFE_PREFIX).append(xpathExpression).append(XSTREAM_SAFE_SUFFIX).toString();
108 }
109
110 /**
111 * This method is for use by WorkflowLookupableImpl and WorkflowAttribute implementations to derive the fieldHelpUrl for use on
112 * org.kuali.rice.kns.web.ui.Fieldss.
113 *
114 * @param field The kuali field that we need to derive a help url for. @ return Returns the help url for the field.
115 */
116 public static String getHelpUrl(org.kuali.rice.kns.web.ui.Field field) {
117 Properties params = new Properties();
118 params.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "getAttributeHelpText");
119 params.put(KNSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, field.getBusinessObjectClassName());
120 params.put(KNSPropertyConstants.ATTRIBUTE_NAME, field.getPropertyName());
121 String baseUrl = KNSServiceLocator.getRiceApplicationConfigurationMediationService().getBaseHelpUrl(field.getBusinessObjectClassName());
122 if (baseUrl == null) {
123 return null;
124 }
125 return UrlFactory.parameterizeUrl(baseUrl, params);
126 }
127
128 /**
129 * This method returns a label from the data dictionary service
130 *
131 * @param businessObjectClass - class where the label should come from
132 * @param attributeName - name of the attribute you need the label for
133 * @return the label from the data dictionary for the given Class and attributeName or null if not found
134 */
135 public static final String getBusinessObjectAttributeLabel(Class businessObjectClass, String attributeName) {
136 return KNSServiceLocator.getDataDictionaryService().getAttributeLabel(businessObjectClass, attributeName);
137 }
138
139
140 /**
141 * This method builds a workflow-lookup-screen Row of type TEXT, with no quickfinder/lookup.
142 *
143 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber.
144 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for
145 * Account.accountNumber.
146 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This
147 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so
148 * everything links up correctly.
149 * @return A populated and ready-to-use workflow lookupable.Row.
150 */
151 public static org.kuali.rice.kns.web.ui.Row buildTextRow(Class propertyClass, String boPropertyName, String workflowPropertyKey) {
152 if (propertyClass == null) {
153 throw new IllegalArgumentException("Method parameter 'propertyClass' was passed a NULL value.");
154 }
155 if (StringUtils.isBlank(boPropertyName)) {
156 throw new IllegalArgumentException("Method parameter 'boPropertyName' was passed a NULL or blank value.");
157 }
158 if (StringUtils.isBlank(workflowPropertyKey)) {
159 throw new IllegalArgumentException("Method parameter 'workflowPropertyKey' was passed a NULL or blank value.");
160 }
161 List<Field> fields = new ArrayList<Field>();
162 org.kuali.rice.kns.web.ui.Field field;
163 field = FieldUtils.getPropertyField(propertyClass, boPropertyName, false);
164 fields.add(field);
165 return new Row(fields);
166 }
167
168 /**
169 * This method builds a workflow-lookup-screen Row of type TEXT, with the attached lookup icon and functionality.
170 *
171 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber.
172 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for
173 * Account.accountNumber.
174 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This
175 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so
176 * everything links up correctly.
177 * @return A populated and ready-to-use workflow lookupable.Row, which includes both the property field and the lookup icon.
178 */
179 public static org.kuali.rice.kns.web.ui.Row buildTextRowWithLookup(Class propertyClass, String boPropertyName, String workflowPropertyKey) {
180 return buildTextRowWithLookup(propertyClass, boPropertyName, workflowPropertyKey, null);
181 }
182
183 /**
184 * This method builds a workflow-lookup-screen Row of type TEXT, with the attached lookup icon and functionality.
185 *
186 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber.
187 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for
188 * Account.accountNumber.
189 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This
190 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so
191 * everything links up correctly.
192 * @param fieldConversionsByBoPropertyName A list of extra field conversions where the key is the business object property name
193 * and the value is the workflow property key
194 * @return A populated and ready-to-use workflow lookupable.Row, which includes both the property field and the lookup icon.
195 */
196 public static org.kuali.rice.kns.web.ui.Row buildTextRowWithLookup(Class propertyClass, String boPropertyName, String workflowPropertyKey, Map fieldConversionsByBoPropertyName) {
197 if (propertyClass == null) {
198 throw new IllegalArgumentException("Method parameter 'propertyClass' was passed a NULL value.");
199 }
200 if (StringUtils.isBlank(boPropertyName)) {
201 throw new IllegalArgumentException("Method parameter 'boPropertyName' was passed a NULL or blank value.");
202 }
203 if (StringUtils.isBlank(workflowPropertyKey)) {
204 throw new IllegalArgumentException("Method parameter 'workflowPropertyKey' was passed a NULL or blank value.");
205 }
206 org.kuali.rice.kns.web.ui.Field field;
207 field = FieldUtils.getPropertyField(propertyClass, boPropertyName, false);
208
209 List<Field> fields = new ArrayList<Field>();
210 fields.add(field);
211 return new Row(fields);
212 }
213
214 /**
215 * This method builds a workflow-lookup-screen Row of type DROPDOWN.
216 *
217 * @param propertyClass The Class of the BO that this row is based on. For example, Account.class for accountNumber.
218 * @param boPropertyName The property name on the BO that this row is based on. For example, accountNumber for
219 * Account.accountNumber.
220 * @param workflowPropertyKey The workflow-lookup-screen property key. For example, account_nbr for Account.accountNumber. This
221 * key can be anything, but needs to be consistent with what is used for the row/field key on the java attribute, so
222 * everything links up correctly.
223 * @param optionMap The map of value, text pairs that will be used to constuct the dropdown list.
224 * @return A populated and ready-to-use workflow lookupable.Row.
225 */
226 public static org.kuali.rice.kns.web.ui.Row buildDropdownRow(Class propertyClass, String boPropertyName, String workflowPropertyKey, Map<String, String> optionMap, boolean addBlankRow) {
227 if (propertyClass == null) {
228 throw new IllegalArgumentException("Method parameter 'propertyClass' was passed a NULL value.");
229 }
230 if (StringUtils.isBlank(boPropertyName)) {
231 throw new IllegalArgumentException("Method parameter 'boPropertyName' was passed a NULL or blank value.");
232 }
233 if (StringUtils.isBlank(workflowPropertyKey)) {
234 throw new IllegalArgumentException("Method parameter 'workflowPropertyKey' was passed a NULL or blank value.");
235 }
236 if (optionMap == null) {
237 throw new IllegalArgumentException("Method parameter 'optionMap' was passed a NULL value.");
238 }
239 List<Field> fields = new ArrayList<Field>();
240 org.kuali.rice.kns.web.ui.Field field;
241 field = FieldUtils.getPropertyField(propertyClass, boPropertyName, false);
242 fields.add(field);
243 return new Row(fields);
244 }
245 }