View Javadoc
1   /**
2    * Copyright 2005-2015 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.krms.framework.engine.expression;
17  
18  import org.apache.commons.lang.ObjectUtils;
19  import org.kuali.rice.krms.api.engine.IncompatibleTypeException;
20  
21  import java.lang.reflect.Constructor;
22  import java.lang.reflect.InvocationTargetException;
23  import java.math.BigDecimal;
24  import java.math.BigInteger;
25  
26  /**
27   * The default {@link ComparisonOperator}.  If no other {@link EngineComparatorExtension} have been configured to handle
28   * a type, the DefaultComparisonOperator will be used.  At the moment the DefaultComparisonOperator is also the default
29   * {@link StringCoercionExtension} for coercing types.
30   *
31   * @author Kuali Rice Team (rice.collab@kuali.org)
32   */
33  
34  public class DefaultComparisonOperator implements EngineComparatorExtension, StringCoercionExtension {
35  
36      @Override
37      public int compare(Object lhs, Object rhs) {
38  
39          if (lhs == null && rhs == null) {
40              return 0;
41          } else if (lhs == null) {
42              return -1;
43          } else if (rhs == null) {
44              return 1;
45          }
46  
47          if (rhs instanceof String && !(lhs instanceof String)) {
48              rhs = coerceStringOperand(lhs, rhs.toString());
49          } else if (lhs instanceof String && !(rhs instanceof String)) {
50              lhs = coerceStringOperand(rhs, lhs.toString());
51          }
52  
53  
54          if (ObjectUtils.equals(lhs, rhs)) {
55              return 0;
56          }
57  
58          if (lhs instanceof Comparable && rhs instanceof Comparable) {
59              int result = ((Comparable)lhs).compareTo(rhs);
60              return result;
61          }
62          else {
63              throw new IncompatibleTypeException("DefaultComparisonOperator could not compare values", lhs, rhs.getClass());
64          }
65      }
66  
67      @Override
68      public boolean canCompare(Object lhs, Object rhs) {
69          try {
70              compare(lhs, rhs);
71              return true;
72          } catch (Exception e) {
73              return false;
74          }
75      }
76  
77      /**
78       * 
79       * @param objectArg
80       * @param stringArg
81       * @return Object
82       * @throws IncompatibleTypeException
83       */
84      private Object coerceStringOperand(Object objectArg, String stringArg) {
85          Object result = stringArg;
86          if (objectArg != null && stringArg != null) {
87              if  (!(objectArg instanceof String)) {
88                  result = coerceHelper(objectArg, stringArg, Double.class, Float.class, Long.class, Integer.class, Boolean.class);
89  
90                  if (result instanceof String) { // was coercion successful?
91                      if (objectArg instanceof BigDecimal) {
92                          try {
93                              result = BigDecimal.valueOf(Double.valueOf(stringArg.toString()));
94                          } catch (NumberFormatException e) {
95                              throw new IncompatibleTypeException("Could not coerce String to BigDecimal" + this, stringArg, objectArg.getClass());
96                          }
97                      } else if (objectArg instanceof BigInteger) {
98                          try {
99                              result = BigInteger.valueOf(Long.valueOf(stringArg.toString()));
100                         } catch (NumberFormatException e) {
101                             throw new IncompatibleTypeException("Could not coerce String to BigInteger" + this, stringArg, objectArg.getClass());
102                         }
103                     } else {
104                         throw new IncompatibleTypeException("Could not compare values for operator " + this, objectArg, stringArg.getClass());
105                     }
106                 }
107             }
108         }
109         return result;
110     }
111 
112     /**
113      *
114      * @param objectArg
115      * @param stringArg
116      * @param clazzes
117      * @return The object of one of the given types, whose value is stringArg
118      */
119     private Object coerceHelper(Object objectArg, String stringArg, Class<?> ... clazzes) {
120         for (Class clazz : clazzes) {
121             if (clazz.isInstance(objectArg)) {
122                 try {
123                     return clazz.getMethod("valueOf", String.class).invoke(null, stringArg);
124                 } catch (NumberFormatException e) {
125                     throw new IncompatibleTypeException("Could not coerce String to " +
126                             clazz.getSimpleName() + " " + this, stringArg, objectArg.getClass());
127                 } catch (NoSuchMethodException e) {
128                     throw new IncompatibleTypeException("Could not coerce String to " +
129                             clazz.getSimpleName() + " " + this, stringArg, objectArg.getClass());
130                 } catch (InvocationTargetException e) {
131                     throw new IncompatibleTypeException("Could not coerce String to " +
132                             clazz.getSimpleName() + " " + this, stringArg, objectArg.getClass());
133                 } catch (IllegalAccessException e) {
134                     throw new IncompatibleTypeException("Could not coerce String to " +
135                             clazz.getSimpleName() + " " + this, stringArg, objectArg.getClass());
136                 }
137             }
138         }
139         return stringArg;
140     }
141 
142     @Override
143     public Object coerce(String type, String value) {
144         try {
145             Class clazz = Class.forName(type);
146             // Constructor that takes string  a bit more generic than the coerceRhs
147             Constructor constructor = clazz.getConstructor(new Class[]{String.class});
148             Object propObject = constructor.newInstance(value);
149             return propObject;
150         } catch (Exception e) {
151             return null; // TODO EGHM dev log?
152         }
153     }
154 
155     @Override
156     public boolean canCoerce(String type, String value) {
157         return coerce(type, value) != null;
158     }
159 }