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.krad.util;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
20  import org.kuali.rice.core.api.CoreApiServiceLocator;
21  import org.kuali.rice.core.api.encryption.EncryptionService;
22  import org.kuali.rice.core.api.util.type.KualiDecimal;
23  import org.kuali.rice.coreservice.framework.parameter.ParameterConstants;
24  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
25  import org.kuali.rice.core.web.format.BooleanFormatter;
26  import org.kuali.rice.krad.UserSession;
27  import org.kuali.rice.krad.service.KRADServiceLocator;
28  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
29  import org.kuali.rice.krad.service.KualiModuleService;
30  import org.kuali.rice.krad.service.ModuleService;
31  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
32  
33  import javax.servlet.ServletRequest;
34  import javax.servlet.http.HttpServletRequest;
35  import java.lang.reflect.Constructor;
36  import java.security.GeneralSecurityException;
37  import java.text.NumberFormat;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Collection;
41  import java.util.HashMap;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Properties;
46  import java.util.regex.Pattern;
47  
48  /**
49   * Miscellaneous Utility Methods
50   *
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  public final class KRADUtils {
54      private static KualiModuleService kualiModuleService;
55  
56      private KRADUtils() {
57          throw new UnsupportedOperationException("do not call");
58      }
59  
60      public final static String getBusinessTitleForClass(Class<? extends Object> clazz) {
61          if (clazz == null) {
62              throw new IllegalArgumentException(
63                      "The getBusinessTitleForClass method of KRADUtils requires a non-null class");
64          }
65          String className = clazz.getSimpleName();
66  
67          StringBuffer label = new StringBuffer(className.substring(0, 1));
68          for (int i = 1; i < className.length(); i++) {
69              if (Character.isLowerCase(className.charAt(i))) {
70                  label.append(className.charAt(i));
71              } else {
72                  label.append(" ").append(className.charAt(i));
73              }
74          }
75          return label.toString().trim();
76      }
77  
78      /**
79       * Picks off the filename from the full path. Takes care of different OS seperators.
80       */
81      public final static List<String> getFileNameFromPath(List<String> fullFileNames) {
82          List<String> fileNameList = new ArrayList<String>();
83  
84          for (String fullFileName : fullFileNames) {
85              if (StringUtils.contains(fullFileName, "/")) {
86                  fileNameList.add(StringUtils.substringAfterLast(fullFileName, "/"));
87              } else {
88                  fileNameList.add(StringUtils.substringAfterLast(fullFileName, "\\"));
89              }
90          }
91  
92          return fileNameList;
93      }
94  
95      private static final KualiDecimal ONE_HUNDRED = new KualiDecimal("100.00");
96  
97      /**
98       * Convert the given monney amount into an interger string. Since the return string cannot have decimal point,
99       * multiplies the
100      * amount by 100 so the decimal places are not lost, for example, 320.15 is converted into 32015.
101      *
102      * @return an integer string of the given money amount through multiplicating by 100 and removing the fraction
103      *         portion.
104      */
105     public final static String convertDecimalIntoInteger(KualiDecimal decimalNumber) {
106         KualiDecimal decimalAmount = decimalNumber.multiply(ONE_HUNDRED);
107         NumberFormat formatter = NumberFormat.getIntegerInstance();
108         String formattedAmount = formatter.format(decimalAmount);
109 
110         return StringUtils.replace(formattedAmount, ",", "");
111     }
112 
113     public static Integer getIntegerValue(String numberStr) {
114         Integer numberInt = null;
115         try {
116             numberInt = new Integer(numberStr);
117         } catch (NumberFormatException nfe) {
118             Double numberDbl = new Double(numberStr);
119             numberInt = new Integer(numberDbl.intValue());
120         }
121         return numberInt;
122     }
123 
124     public static Object createObject(Class<?> clazz, Class<?>[] argumentClasses, Object[] argumentValues) {
125         if (clazz == null)
126             return null;
127         try {
128             Constructor<?> constructor = clazz.getConstructor(argumentClasses);
129             return constructor.newInstance(argumentValues);
130         } catch (Exception e) {
131             return null;
132         }
133     }
134 
135     public static String joinWithQuotes(List<String> list) {
136         if (list == null || list.size() == 0)
137             return "";
138 
139         return KRADConstants.SINGLE_QUOTE +
140                 StringUtils.join(list.iterator(), KRADConstants.SINGLE_QUOTE + "," + KRADConstants.SINGLE_QUOTE) +
141                 KRADConstants.SINGLE_QUOTE;
142     }
143 
144     private static KualiModuleService getKualiModuleService() {
145         if (kualiModuleService == null) {
146             kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService();
147         }
148         return kualiModuleService;
149     }
150 
151     /**
152      * TODO this method will probably need to be exposed in a public KRADUtils class as it is used
153      * by several different modules.  That will have to wait until ModuleService and KualiModuleService are moved
154      * to core though.
155      */
156     public static String getNamespaceCode(Class<? extends Object> clazz) {
157         ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(clazz);
158         if (moduleService == null) {
159             return KRADConstants.DEFAULT_NAMESPACE;
160         }
161         return moduleService.getModuleConfiguration().getNamespaceCode();
162     }
163 
164     public static Map<String, String> getNamespaceAndComponentSimpleName(Class<? extends Object> clazz) {
165         Map<String, String> map = new HashMap<String, String>();
166         map.put(KRADConstants.NAMESPACE_CODE, getNamespaceCode(clazz));
167         map.put(KRADConstants.COMPONENT_NAME, getComponentSimpleName(clazz));
168         return map;
169     }
170 
171     public static Map<String, String> getNamespaceAndComponentFullName(Class<? extends Object> clazz) {
172         Map<String, String> map = new HashMap<String, String>();
173         map.put(KRADConstants.NAMESPACE_CODE, getNamespaceCode(clazz));
174         map.put(KRADConstants.COMPONENT_NAME, getComponentFullName(clazz));
175         return map;
176     }
177 
178     public static Map<String, String> getNamespaceAndActionClass(Class<? extends Object> clazz) {
179         Map<String, String> map = new HashMap<String, String>();
180         map.put(KRADConstants.NAMESPACE_CODE, getNamespaceCode(clazz));
181         map.put(KRADConstants.ACTION_CLASS, clazz.getName());
182         return map;
183     }
184 
185     private static String getComponentSimpleName(Class<? extends Object> clazz) {
186         return clazz.getSimpleName();
187     }
188 
189     private static String getComponentFullName(Class<? extends Object> clazz) {
190         return clazz.getName();
191     }
192 
193     /**
194      * Parses a string that is in map format (commas separating map entries, colon separates
195      * map key/value) to a new map instance
196      *
197      * @param parameter - string parameter to parse
198      * @return Map<String, String> instance populated from string parameter
199      */
200     public static Map<String, String> convertStringParameterToMap(String parameter) {
201         Map<String, String> map = new HashMap<String, String>();
202 
203         if (StringUtils.isNotBlank(parameter)) {
204             if (StringUtils.contains(parameter, ",")) {
205                 String[] fieldConversions = StringUtils.split(parameter, ",");
206 
207                 for (int i = 0; i < fieldConversions.length; i++) {
208                     String fieldConversionStr = fieldConversions[i];
209                     if (StringUtils.isNotBlank(fieldConversionStr)) {
210                         if (StringUtils.contains(fieldConversionStr, ":")) {
211                             String[] fieldConversion = StringUtils.split(fieldConversionStr, ":");
212                             map.put(fieldConversion[0], fieldConversion[1]);
213                         } else {
214                             map.put(fieldConversionStr, fieldConversionStr);
215                         }
216                     }
217                 }
218             } else if (StringUtils.contains(parameter, ":")) {
219                 String[] fieldConversion = StringUtils.split(parameter, ":");
220                 map.put(fieldConversion[0], fieldConversion[1]);
221             } else {
222                 map.put(parameter, parameter);
223             }
224         }
225 
226         return map;
227     }
228 
229     /**
230      * Parses a string that is in list format (commas separating list entries) to a new List instance
231      *
232      * @param parameter - string parameter to parse
233      * @return List<String> instance populated from string parameter
234      */
235     public static List<String> convertStringParameterToList(String parameter) {
236         List<String> list = new ArrayList<String>();
237 
238         if (StringUtils.isNotBlank(parameter)) {
239             if (StringUtils.contains(parameter, ",")) {
240                 String[] parameters = StringUtils.split(parameter, ",");
241                 List arraysList = Arrays.asList(parameters);
242                 list.addAll(arraysList);
243             } else {
244                 list.add(parameter);
245             }
246         }
247 
248         return list;
249     }
250 
251     /**
252      * Translates characters in the given string like brackets that will cause
253      * problems with binding to characters that do not affect the binding
254      *
255      * @param key - string to translate
256      * @return String translated string
257      */
258     public static String translateToMapSafeKey(String key) {
259         String safeKey = key;
260 
261         safeKey = StringUtils.replace(safeKey, "[", "_");
262         safeKey = StringUtils.replace(safeKey, "]", "_");
263 
264         return safeKey;
265     }
266 
267     /**
268      * Builds a string from the given map by joining each entry with a comma and
269      * each key/value pair with a colon
270      *
271      * @param map - map instance to build string for
272      * @return String of map entries
273      */
274     public static String buildMapParameterString(Map<String, String> map) {
275         String parameterString = "";
276 
277         for (Map.Entry<String, String> entry : map.entrySet()) {
278             if (StringUtils.isNotBlank(parameterString)) {
279                 parameterString += ",";
280             }
281 
282             parameterString += entry.getKey() + ":" + entry.getValue();
283         }
284 
285         return parameterString;
286     }
287 
288     /**
289      * Parses the given string into a Map by splitting on the comma to get the
290      * map entries and within each entry splitting by colon to get the key/value
291      * pairs
292      *
293      * @param parameterString - string to parse into map
294      * @return Map<String, String> map from string
295      */
296     public static Map<String, String> getMapFromParameterString(String parameterString) {
297         Map<String, String> map = new HashMap<String, String>();
298 
299         String[] entries = parameterString.split(",");
300         for (int i = 0; i < entries.length; i++) {
301             String[] keyValue = entries[i].split(":");
302             if (keyValue.length != 2) {
303                 throw new RuntimeException("malformed field conversion pair: " + Arrays.toString(keyValue));
304             }
305 
306             map.put(keyValue[0], keyValue[1]);
307         }
308 
309         return map;
310     }
311 
312     /**
313      * Retrieves value for the given parameter name in the request and attempts to convert to a Boolean using
314      * the <code>BooleanFormatter</code>
315      *
316      * @param request - servlet request containing parameters
317      * @param parameterName - name of parameter to retrieve value for
318      * @return Boolean set to value of parameter, or null if parameter was not found in request
319      */
320     public static Boolean getRequestParameterAsBoolean(ServletRequest request, String parameterName) {
321         Boolean parameterValue = null;
322 
323         String parameterValueStr = request.getParameter(parameterName);
324         if (StringUtils.isNotBlank(parameterValueStr)) {
325             parameterValue = (Boolean) new BooleanFormatter().convertFromPresentationFormat(parameterValueStr);
326         }
327 
328         return parameterValue;
329     }
330 
331     /**
332      * Translates the given Map of String keys and String array values to a Map
333      * of String key and values. If the String array contains more than one
334      * value, the single string is built by joining the values with the vertical
335      * bar character
336      *
337      * @param requestParameters - Map of request parameters to translate
338      * @return Map<String, String> translated Map
339      */
340     public static Map<String, String> translateRequestParameterMap(Map<String, String[]> requestParameters) {
341         Map<String, String> parameters = new HashMap<String, String>();
342 
343         for (Map.Entry<String, String[]> parameter : requestParameters.entrySet()) {
344             String parameterValue = "";
345             if (parameter.getValue().length > 1) {
346                 parameterValue = StringUtils.join(parameter.getValue(), "|");
347             } else {
348                 parameterValue = parameter.getValue()[0];
349             }
350             parameters.put(parameter.getKey(), parameterValue);
351         }
352 
353         return parameters;
354     }
355 
356     /**
357      * Retrieves parameter values from the request that match the requested
358      * names. In addition, based on the object class an authorization check is
359      * performed to determine if the values are secure and should be decrypted.
360      * If true, the value is decrypted before returning
361      *
362      * @param parameterNames - names of the parameters whose values should be retrieved
363      * from the request
364      * @param parentObjectClass - object class that contains the parameter names as properties
365      * and should be consulted for security checks
366      * @param requestParameters - all request parameters to pull from
367      * @return Map<String, String> populated with parameter name/value pairs
368      *         pulled from the request
369      */
370     public static Map<String, String> getParametersFromRequest(List<String> parameterNames, Class<?> parentObjectClass,
371             Map<String, String> requestParameters) {
372         Map<String, String> parameterValues = new HashMap<String, String>();
373 
374         for (Iterator<String> iter = parameterNames.iterator(); iter.hasNext(); ) {
375             String keyPropertyName = iter.next();
376 
377             if (requestParameters.get(keyPropertyName) != null) {
378                 String keyValue = requestParameters.get(keyPropertyName);
379 
380                 // Check if this element was encrypted, if it was decrypt it
381                 if (KRADServiceLocatorWeb.getDataObjectAuthorizationService()
382                         .attributeValueNeedsToBeEncryptedOnFormsAndLinks(parentObjectClass, keyPropertyName)) {
383                     try {
384                         keyValue = StringUtils.removeEnd(keyValue, EncryptionService.ENCRYPTION_POST_PREFIX);
385                         keyValue = CoreApiServiceLocator.getEncryptionService().decrypt(keyValue);
386                     } catch (GeneralSecurityException e) {
387                         throw new RuntimeException(e);
388                     }
389                 }
390 
391                 parameterValues.put(keyPropertyName, keyValue);
392             }
393         }
394 
395         return parameterValues;
396     }
397 
398     /**
399      * Builds a Map containing a key/value pair for each property given in the property names list, general
400      * security is checked to determine if the value needs to be encrypted along with applying formatting to
401      * the value
402      *
403      * @param propertyNames - list of property names to get key/value pairs for
404      * @param dataObject - object instance containing the properties for which the values will be pulled
405      * @return Map<String, String> containing entry for each property name with the property name as the map key
406      *         and the property value as the value
407      */
408     public static Map<String, String> getPropertyKeyValuesFromDataObject(List<String> propertyNames,
409             Object dataObject) {
410         Map<String, String> propertyKeyValues = new HashMap<String, String>();
411 
412         if (dataObject == null) {
413             return propertyKeyValues;
414         }
415 
416         // iterate through properties and add a map entry for each
417         for (String propertyName : propertyNames) {
418             Object propertyValue = ObjectPropertyUtils.getPropertyValue(dataObject, propertyName);
419             if (propertyValue == null) {
420                 propertyValue = StringUtils.EMPTY;
421             }
422 
423             if (KRADServiceLocatorWeb.getDataObjectAuthorizationService()
424                     .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObject.getClass(), propertyName)) {
425                 try {
426                     propertyValue = CoreApiServiceLocator.getEncryptionService().encrypt(propertyValue) +
427                             EncryptionService.ENCRYPTION_POST_PREFIX;
428                 } catch (GeneralSecurityException e) {
429                     throw new RuntimeException("Exception while trying to encrypt value for key/value map.", e);
430                 }
431             }
432 
433             // TODO: need to apply formatting to return value once util class is ready
434             propertyKeyValues.put(propertyName, propertyValue.toString());
435         }
436 
437         return propertyKeyValues;
438     }
439 
440     /**
441      * Utility method to convert a Map to a Properties object
442      *
443      * @param parameters - map to convert
444      * @return Properties object containing all the map entries
445      */
446     public static Properties convertMapToProperties(Map<String, String> parameters) {
447         Properties properties = new Properties();
448 
449         if (parameters != null) {
450             for (Map.Entry<String, String> parameter : parameters.entrySet()) {
451                 properties.put(parameter.getKey(), parameter.getValue());
452             }
453         }
454 
455         return properties;
456     }
457 
458     /**
459      * Utility method to convert a Request Parameters Map to a Properties object
460      *
461      * <p>
462      * Multiple values for a parameter are joined together with comma delimiter
463      * </p>
464      *
465      * @param requestParameters - map to convert
466      * @return Properties object containing all the map entries
467      */
468     public static Properties convertRequestMapToProperties(Map<String, String[]> requestParameters) {
469         Properties properties = new Properties();
470 
471         if (requestParameters != null) {
472             for (Map.Entry<String, String[]> parameter : requestParameters.entrySet()) {
473                 String[] parameterValue = parameter.getValue();
474                 String parameterValueString = StringUtils.join(parameterValue, ",");
475 
476                 properties.put(parameter.getKey(), parameterValueString);
477             }
478         }
479 
480         return properties;
481     }
482 
483     public static boolean containsSensitiveDataPatternMatch(String fieldValue) {
484         if (StringUtils.isBlank(fieldValue)) {
485             return false;
486         }
487         ParameterService parameterService = CoreFrameworkServiceLocator.getParameterService();
488         Collection<String> sensitiveDataPatterns = parameterService
489                 .getParameterValuesAsString(KRADConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT,
490                         KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS);
491         for (String pattern : sensitiveDataPatterns) {
492             if (Pattern.compile(pattern).matcher(fieldValue).find()) {
493                 return true;
494             }
495         }
496         return false;
497     }
498 
499     /**
500      * Gets the UserSession object from the HttpServletRequest object's
501      * associated session.
502      *
503      * <p>
504      * In some cases (different threads) the UserSession cannot be retrieved
505      * from GlobalVariables but can still be accessed via the session object
506      * </p>
507      */
508     public static final UserSession getUserSessionFromRequest(HttpServletRequest request) {
509         return (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY);
510     }
511 
512     /**
513      * @return whether the deploy environment is production
514      */
515     public static boolean isProductionEnvironment() {
516         return KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
517                 KRADConstants.PROD_ENVIRONMENT_CODE_KEY)
518                 .equalsIgnoreCase(KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
519                         KRADConstants.ENVIRONMENT_KEY));
520     }
521 
522 }