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.datadictionary.validation;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.datetime.DateTimeService;
20  import org.kuali.rice.core.api.uif.DataType;
21  import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
22  import org.kuali.rice.krad.uif.UifConstants;
23  
24  import java.math.BigDecimal;
25  import java.text.ParseException;
26  import java.util.Collection;
27  import java.util.Date;
28  
29  /**
30   * ValidationUtils provides static utility methods for validation processing
31   *
32   * <p>Inherited from Kuali Student and adapted extensively</p>
33   *
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   */
36  public class ValidationUtils {
37  
38      /**
39       * constructs a path by appending the attribute name to the provided path
40       *
41       * @param attributePath - a string representation of specifically which attribute (at some depth) is being accessed
42       * @param attributeName - the attribute name
43       * @return the path
44       */
45      public static String buildPath(String attributePath, String attributeName) {
46          if (StringUtils.isNotBlank(attributeName)) {
47              if (StringUtils.isNotBlank(attributePath)) {
48                  return new StringBuilder(attributePath).append(".").append(attributeName).toString();
49              }
50  
51              return attributeName;
52          }
53          return attributePath;
54      }
55  
56      /**
57       * Used to get the rightmost index value of an attribute path.
58       *
59       * @param attributePath
60       * @return the right index of value of attribute path, -1 if path has no index
61       */
62      public static int getLastPathIndex(String attributePath) {
63          int index = -1;
64  
65          int leftBracket = attributePath.lastIndexOf("[");
66          int rightBracket = attributePath.lastIndexOf("]");
67  
68          if (leftBracket > 0 && rightBracket > leftBracket) {
69              String indexString = attributePath.substring(leftBracket + 1, rightBracket);
70              try {
71                  index = Integer.valueOf(indexString).intValue();
72              } catch (NumberFormatException e) {
73                  // Will just return -1
74              }
75          }
76  
77          return index;
78      }
79  
80      /**
81       * compares the value provided by the user and the one specified by the {@code WhenConstraint}
82       *
83       * @param fieldValue - the value found in the field specified by a {@code CaseConstraint}'s {@code propertyName}
84       * @param whenValue - the value specified by a {@code WhenConstraint}
85       * @param dataType - the data type of the field which caseConstraint's propertyName refers to
86       * @param operator - the relationship to check between the {@code fieldValue} and the {@code whenValue}
87       * @param isCaseSensitive - whether string comparison will be carried out in a case sensitive fashion
88       * @param dateTimeService - used to convert strings to dates
89       * @return
90       */
91      public static boolean compareValues(Object fieldValue, Object whenValue, DataType dataType, String operator,
92              boolean isCaseSensitive, DateTimeService dateTimeService) {
93  
94          boolean result = false;
95          Integer compareResult = null;
96  
97          if (UifConstants.CaseConstraintOperators.HAS_VALUE.equalsIgnoreCase(operator)) {
98              if (fieldValue == null) {
99                  return "false".equals(whenValue.toString().toLowerCase());
100             }
101             if (fieldValue instanceof String && ((String) fieldValue).isEmpty()) {
102                 return "false".equals(whenValue.toString().toLowerCase());
103             }
104             if (fieldValue instanceof Collection && ((Collection<?>) fieldValue).isEmpty()) {
105                 return "false".equals(whenValue.toString().toLowerCase());
106             }
107             return "true".equals(whenValue.toString().toLowerCase());
108         }
109         // Convert objects into appropriate data types
110         if (null != dataType) {
111             if (DataType.STRING.equals(dataType)) {
112                 String v1 = getString(fieldValue);
113                 String v2 = getString(whenValue);
114 
115                 if (!isCaseSensitive) {
116                     v1 = v1.toUpperCase();
117                     v2 = v2.toUpperCase();
118                 }
119 
120                 compareResult = v1.compareTo(v2);
121             } else if (DataType.INTEGER.equals(dataType)) {
122                 Integer v1 = getInteger(fieldValue);
123                 Integer v2 = getInteger(whenValue);
124                 compareResult = v1.compareTo(v2);
125             } else if (DataType.LONG.equals(dataType)) {
126                 Long v1 = getLong(fieldValue);
127                 Long v2 = getLong(whenValue);
128                 compareResult = v1.compareTo(v2);
129             } else if (DataType.DOUBLE.equals(dataType)) {
130                 Double v1 = getDouble(fieldValue);
131                 Double v2 = getDouble(whenValue);
132                 compareResult = v1.compareTo(v2);
133             } else if (DataType.FLOAT.equals(dataType)) {
134                 Float v1 = getFloat(fieldValue);
135                 Float v2 = getFloat(whenValue);
136                 compareResult = v1.compareTo(v2);
137             } else if (DataType.BOOLEAN.equals(dataType)) {
138                 Boolean v1 = getBoolean(fieldValue);
139                 Boolean v2 = getBoolean(whenValue);
140                 compareResult = v1.compareTo(v2);
141             } else if (DataType.DATE.equals(dataType)) {
142                 Date v1 = getDate(fieldValue, dateTimeService);
143                 Date v2 = getDate(whenValue, dateTimeService);
144                 compareResult = v1.compareTo(v2);
145             }
146         }
147 
148         if (null != compareResult) {
149             if ((UifConstants.CaseConstraintOperators.EQUALS.equalsIgnoreCase(operator) || UifConstants
150                     .CaseConstraintOperators.GREATER_THAN_EQUAL.equalsIgnoreCase(operator) || UifConstants
151                     .CaseConstraintOperators.LESS_THAN_EQUAL.equalsIgnoreCase(operator)) && 0 == compareResult) {
152                 result = true;
153             }
154 
155             if ((UifConstants.CaseConstraintOperators.NOT_EQUAL.equalsIgnoreCase(operator) || UifConstants
156                     .CaseConstraintOperators.NOT_EQUALS.equalsIgnoreCase(operator) || UifConstants
157                     .CaseConstraintOperators.GREATER_THAN.equalsIgnoreCase(operator)) && compareResult >= 1) {
158                 result = true;
159             }
160 
161             if ((UifConstants.CaseConstraintOperators.NOT_EQUAL.equalsIgnoreCase(operator) || UifConstants
162                     .CaseConstraintOperators.NOT_EQUALS.equalsIgnoreCase(operator) || UifConstants
163                     .CaseConstraintOperators.LESS_THAN.equalsIgnoreCase(operator)) && compareResult <= -1) {
164                 result = true;
165             }
166         }
167 
168         return result;
169     }
170 
171     /**
172      * converts the provided object into an integer
173      *
174      * @param o - the object to convert
175      * @return the integer value
176      */
177     public static Integer getInteger(Object o) {
178         Integer result = null;
179         if (o instanceof Integer) {
180             return (Integer) o;
181         }
182         if (o == null) {
183             return null;
184         }
185         if (o instanceof Number) {
186             return ((Number) o).intValue();
187         }
188         String s = o.toString();
189         if (s != null && s.trim().length() > 0) {
190             result = Integer.valueOf(s.trim());
191         }
192         return result;
193     }
194 
195     /**
196      * converts the provided object into a long
197      *
198      * @param o - the object to convert
199      * @return the long value
200      */
201     public static Long getLong(Object o) {
202         Long result = null;
203         if (o instanceof Long) {
204             return (Long) o;
205         }
206         if (o == null) {
207             return null;
208         }
209         if (o instanceof Number) {
210             return ((Number) o).longValue();
211         }
212         String s = o.toString();
213         if (s != null && s.trim().length() > 0) {
214             result = Long.valueOf(s.trim());
215         }
216         return result;
217     }
218 
219     /**
220      * converts the provided object into an float
221      *
222      * @param o - the object to convert
223      * @return the float value
224      */
225     public static Float getFloat(Object o) {
226         Float result = null;
227         if (o instanceof Float) {
228             return (Float) o;
229         }
230         if (o == null) {
231             return null;
232         }
233         if (o instanceof Number) {
234             return ((Number) o).floatValue();
235         }
236         String s = o.toString();
237         if (s != null && s.trim().length() > 0) {
238             result = Float.valueOf(s.trim());
239         }
240         return result;
241     }
242 
243     /**
244      * converts the provided object into a double
245      *
246      * @param o - the object to convert
247      * @return the double value
248      */
249     public static Double getDouble(Object o) {
250         Double result = null;
251         if (o instanceof BigDecimal) {
252             return ((BigDecimal) o).doubleValue();
253         }
254         if (o instanceof Double) {
255             return (Double) o;
256         }
257         if (o == null) {
258             return null;
259         }
260         if (o instanceof Number) {
261             return ((Number) o).doubleValue();
262         }
263         String s = o.toString();
264         if (s != null && s.trim().length() > 0) {
265             result = Double.valueOf(s.trim());
266         }
267         return result;
268     }
269 
270     /**
271      * determines whether the provided object is a date and tries to converts non-date values
272      *
273      * @param object - the object to convert/cast into a date
274      * @param dateTimeService - used to convert strings to dates
275      * @return a date object
276      * @throws IllegalArgumentException
277      */
278     public static Date getDate(Object object, DateTimeService dateTimeService) throws IllegalArgumentException {
279         Date result = null;
280         if (object instanceof Date) {
281             return (Date) object;
282         }
283         if (object == null) {
284             return null;
285         }
286         String s = object.toString();
287         if (s != null && s.trim().length() > 0) {
288             try {
289                 result = dateTimeService.convertToDate(s.trim());
290             } catch (ParseException e) {
291                 throw new IllegalArgumentException(e);
292             }
293         }
294         return result;
295     }
296 
297     /**
298      * converts the provided object into a string
299      *
300      * @param o - the object to convert
301      * @return the string value
302      */
303     public static String getString(Object o) {
304         if (o instanceof String) {
305             return (String) o;
306         }
307         if (o == null) {
308             return null;
309         }
310         return o.toString();
311     }
312 
313     /**
314      * converts the provided object into a boolean
315      *
316      * @param o - the object to convert
317      * @return the boolean value
318      */
319     public static Boolean getBoolean(Object o) {
320         Boolean result = null;
321         if (o instanceof Boolean) {
322             return (Boolean) o;
323         }
324         if (o == null) {
325             return null;
326         }
327         String s = o.toString();
328         if (s != null && s.trim().length() > 0) {
329             result = Boolean.parseBoolean(s.trim());
330         }
331         return result;
332     }
333 
334     /**
335      * checks whether the string contains non-whitespace characters
336      *
337      * @param string
338      * @return true if the string contains at least one none-whitespace character, false otherwise
339      */
340     public static boolean hasText(String string) {
341 
342         if (string == null || string.length() < 1) {
343             return false;
344         }
345         int stringLength = string.length();
346 
347         for (int i = 0; i < stringLength; i++) {
348             char currentChar = string.charAt(i);
349             if (' ' != currentChar || '\t' != currentChar || '\n' != currentChar) {
350                 return true;
351             }
352         }
353 
354         return false;
355     }
356 
357     /**
358      * checks whether the provided object is null or empty
359      *
360      * @param value - the object to check
361      * @return true if the object is null or empty, false otherwise
362      */
363     public static boolean isNullOrEmpty(Object value) {
364         return value == null || (value instanceof String && StringUtils.isBlank(((String) value).trim()));
365     }
366 
367     /**
368      * defines possible result values of a comparison operation
369      */
370     public static enum Result {
371         VALID, INVALID, UNDEFINED
372     }
373 
374     ;
375 
376     /**
377      * attempts to convert the provided value to the given dataType
378      *
379      * @param value - the object to convert
380      * @param dataType - the data type to convert into
381      * @param dateTimeService - used to convert strings to dates
382      * @return the converted value if null or successful, otherwise throws an exception
383      * @throws AttributeValidationException
384      */
385     public static Object convertToDataType(Object value, DataType dataType,
386             DateTimeService dateTimeService) throws AttributeValidationException {
387         Object returnValue = value;
388 
389         if (null == value) {
390             return null;
391         }
392 
393         switch (dataType) {
394             case BOOLEAN:
395                 if (!(value instanceof Boolean)) {
396                     returnValue = Boolean.valueOf(value.toString());
397 
398                     // Since the Boolean.valueOf is exceptionally loose - it basically takes any string and makes it false
399                     if (!value.toString().equalsIgnoreCase("TRUE") && !value.toString().equalsIgnoreCase("FALSE")) {
400                         throw new AttributeValidationException("Value " + value.toString() + " is not a boolean!");
401                     }
402                 }
403                 break;
404             case INTEGER:
405                 if (!(value instanceof Number)) {
406                     returnValue = Integer.valueOf(value.toString());
407                 }
408                 break;
409             case LONG:
410                 if (!(value instanceof Number)) {
411                     returnValue = Long.valueOf(value.toString());
412                 }
413                 break;
414             case DOUBLE:
415                 if (!(value instanceof Number)) {
416                     returnValue = Double.valueOf(value.toString());
417                 }
418                 if (((Double) returnValue).isNaN()) {
419                     throw new AttributeValidationException("Infinite Double values are not valid!");
420                 }
421                 if (((Double) returnValue).isInfinite()) {
422                     throw new AttributeValidationException("Infinite Double values are not valid!");
423                 }
424                 break;
425             case FLOAT:
426                 if (!(value instanceof Number)) {
427                     returnValue = Float.valueOf(value.toString());
428                 }
429                 if (((Float) returnValue).isNaN()) {
430                     throw new AttributeValidationException("NaN Float values are not valid!");
431                 }
432                 if (((Float) returnValue).isInfinite()) {
433                     throw new AttributeValidationException("Infinite Float values are not valid!");
434                 }
435                 break;
436             case TRUNCATED_DATE:
437             case DATE:
438                 if (!(value instanceof Date)) {
439                     try {
440                         returnValue = dateTimeService.convertToDate(value.toString());
441                     } catch (ParseException pe) {
442                         throw new AttributeValidationException("Value " + value.toString() + " is not a date!");
443                     }
444                 }
445                 break;
446             case STRING:
447         }
448 
449         return returnValue;
450     }
451 
452     /**
453      * checks whether the provided value is greater than the limit given
454      *
455      * @param value - the object to check
456      * @param limit - the limit to use
457      * @param <T>
458      * @return one of the values in {@link  Result}
459      */
460     public static <T> Result isGreaterThan(T value, Comparable<T> limit) {
461         return limit == null ? Result.UNDEFINED : (limit.compareTo(value) < 0 ? Result.VALID : Result.INVALID);
462     }
463 
464     /**
465      * checks whether the provided value is greater than or equal to the limit given
466      *
467      * @param value - the object to check
468      * @param limit - the limit to use
469      * @param <T>
470      * @return one of the values in {@link  Result}
471      */
472     public static <T> Result isGreaterThanOrEqual(T value, Comparable<T> limit) {
473         return limit == null ? Result.UNDEFINED : (limit.compareTo(value) <= 0 ? Result.VALID : Result.INVALID);
474     }
475 
476     /**
477      * checks whether the provided value is less than the limit given
478      *
479      * @param value - the object to check
480      * @param limit - the limit to use
481      * @param <T>
482      * @return one of the values in {@link  Result}
483      */
484     public static <T> Result isLessThan(T value, Comparable<T> limit) {
485         return limit == null ? Result.UNDEFINED : (limit.compareTo(value) > 0 ? Result.VALID : Result.INVALID);
486     }
487 
488     /**
489      * checks whether the provided value is greater than the limit given
490      *
491      * @param value - the object to check
492      * @param limit - the limit to use
493      * @param <T>
494      * @return one of the values in {@link  Result}
495      */
496     public static <T> Result isLessThanOrEqual(T value, Comparable<T> limit) {
497         return limit == null ? Result.UNDEFINED : (limit.compareTo(value) >= 0 ? Result.VALID : Result.INVALID);
498     }
499 
500     /**
501      * converts a path into an array of its path components
502      *
503      * @param fieldPath - a string representation of specifically which attribute (at some depth) is being accessed
504      * @return the array of path components
505      */
506     public static String[] getPathTokens(String fieldPath) {
507         return (fieldPath != null && fieldPath.contains(".") ? fieldPath.split("\\.") : new String[]{fieldPath});
508     }
509 
510 }
511