View Javadoc

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.krad.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.util.FieldUtils;
32  import org.kuali.rice.kns.web.ui.Field;
33  import org.kuali.rice.kns.web.ui.Row;
34  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
35  import org.kuali.rice.krad.util.KRADConstants;
36  import org.kuali.rice.krad.util.KRADPropertyConstants;
37  import org.kuali.rice.krad.util.UrlFactory;
38  import org.w3c.dom.Document;
39  
40  
41  public final 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  	private WorkflowUtils() {
49  		throw new UnsupportedOperationException("do not call");
50  	}
51      
52      /**
53       *
54       * This method sets up the XPath with the correct workflow namespace and resolver initialized. This ensures that the XPath
55       * statements can use required workflow functions as part of the XPath statements.
56       *
57       * @param document - document
58       * @return a fully initialized XPath instance that has access to the workflow resolver and namespace.
59       *
60       */
61      public final static XPath getXPath(Document document) {
62          XPath xpath = getXPath(RouteContext.getCurrentRouteContext());
63          xpath.setNamespaceContext(new WorkflowNamespaceContext());
64          WorkflowFunctionResolver resolver = new WorkflowFunctionResolver();
65          resolver.setXpath(xpath);
66          resolver.setRootNode(document);
67          xpath.setXPathFunctionResolver(resolver);
68          return xpath;
69      }
70  
71      public final static XPath getXPath(RouteContext routeContext) {
72          if (routeContext == null) {
73              return XPathFactory.newInstance().newXPath();
74          }
75          if (!routeContext.getParameters().containsKey(XPATH_ROUTE_CONTEXT_KEY)) {
76              routeContext.getParameters().put(XPATH_ROUTE_CONTEXT_KEY, XPathFactory.newInstance().newXPath());
77          }
78          return (XPath) routeContext.getParameters().get(XPATH_ROUTE_CONTEXT_KEY);
79      }
80  
81      /**
82       * This method will do a simple XPath.evaluate, while wrapping your xpathExpression with the xstreamSafe function. It assumes a
83       * String result, and will return such. If an XPathExpressionException is thrown, this will be re-thrown within a
84       * RuntimeException.
85       *
86       * @param xpath A correctly initialized XPath instance.
87       * @param xpathExpression Your XPath Expression that needs to be wrapped in an xstreamSafe wrapper and run.
88       * @param item The document contents you will be searching within.
89       * @return The string value of the xpath.evaluate().
90       */
91      public static final String xstreamSafeEval(XPath xpath, String xpathExpression, Object item) {
92          String xstreamSafeXPath = xstreamSafeXPath(xpathExpression);
93          String evalResult = "";
94          try {
95              evalResult = xpath.evaluate(xstreamSafeXPath, item);
96          }
97          catch (XPathExpressionException e) {
98              throw new RuntimeException("XPathExpressionException occurred on xpath: " + xstreamSafeXPath, e);
99          }
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 }