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.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          rhs = coerceRhs(lhs, rhs);
48          if (ObjectUtils.equals(lhs, rhs)) {
49              return 0;
50          }
51  
52          if (lhs instanceof Comparable && rhs instanceof Comparable) {
53              int result = ((Comparable)lhs).compareTo(rhs);
54              return result;
55          }
56          else {
57              throw new IncompatibleTypeException("DefaultComparisonOperator could not compare values", lhs, rhs.getClass());
58          }
59      }
60  
61      @Override
62      public boolean canCompare(Object lhs, Object rhs) {
63          try {
64              compare(lhs, rhs);
65              return true;
66          } catch (Exception e) {
67              return false;
68          }
69      }
70  
71      /**
72       * 
73       * @param lhs
74       * @param rhs
75       * @return Object
76       * @throws IncompatibleTypeException
77       */
78      private Object coerceRhs(Object lhs, Object rhs) {
79          if (lhs != null && rhs != null) {
80              if  (!lhs.getClass().equals(rhs.getClass()) && rhs instanceof String) {
81                  rhs = coerceRhsHelper(lhs, rhs.toString(), Double.class, Float.class, Long.class, Integer.class);
82  
83                  if (rhs instanceof String) { // was coercion successful?
84                      if (lhs instanceof BigDecimal) {
85                          try {
86                              rhs = BigDecimal.valueOf(Double.valueOf(rhs.toString()));
87                          } catch (NumberFormatException e) {
88                              throw new IncompatibleTypeException("Could not coerce String to BigDecimal" + this, rhs, lhs.getClass());
89                          }
90                      } else if (lhs instanceof BigInteger) {
91                          try {
92                              rhs = BigInteger.valueOf(Long.valueOf(rhs.toString()));
93                          } catch (NumberFormatException e) {
94                              throw new IncompatibleTypeException("Could not coerce String to BigInteger" + this, rhs, lhs.getClass());
95                          }
96                      } else {
97                          throw new IncompatibleTypeException("Could not compare values for operator " + this, lhs, rhs.getClass());
98                      }
99                  }
100             }
101         }
102         return rhs;
103     }
104 
105     /**
106      *
107      * @param lhs
108      * @param rhs
109      * @param clazzes
110      * @return The object of one of the given types, whose value is rhs
111      */
112     private Object coerceRhsHelper(Object lhs, String rhs, Class<?> ... clazzes) {
113         for (Class clazz : clazzes) {
114             if (clazz.isInstance(lhs)) {
115                 try {
116                     return clazz.getMethod("valueOf", String.class).invoke(null, rhs);
117                 } catch (NumberFormatException e) {
118                     throw new IncompatibleTypeException("Could not coerce String to " +
119                             clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
120                 } catch (NoSuchMethodException e) {
121                     throw new IncompatibleTypeException("Could not coerce String to " +
122                             clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
123                 } catch (InvocationTargetException e) {
124                     throw new IncompatibleTypeException("Could not coerce String to " +
125                             clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
126                 } catch (IllegalAccessException e) {
127                     throw new IncompatibleTypeException("Could not coerce String to " +
128                             clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
129                 }
130             }
131         }
132         return rhs;
133     }
134 
135     @Override
136     public Object coerce(String type, String value) {
137         try {
138             Class clazz = Class.forName(type);
139             // Constructor that takes string  a bit more generic than the coerceRhs
140             Constructor constructor = clazz.getConstructor(new Class[]{String.class});
141             Object propObject = constructor.newInstance(value);
142             return propObject;
143         } catch (Exception e) {
144             return null; // TODO EGHM dev log?
145         }
146     }
147 
148     @Override
149     public boolean canCoerce(String type, String value) {
150         return coerce(type, value) != null;
151     }
152 }