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