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