View Javadoc

1   /**
2    * Copyright 2005-2011 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.core.api.mo.common.Coded;
20  import org.kuali.rice.krms.api.engine.IncompatibleTypeException;
21  
22  import java.lang.reflect.InvocationTargetException;
23  import java.math.BigDecimal;
24  import java.math.BigInteger;
25  
26  public enum ComparisonOperator implements Coded {
27  
28  	EQUALS("="),
29  	NOT_EQUALS("!="),
30  	GREATER_THAN(">"),
31  	GREATER_THAN_EQUAL(">="),
32  	LESS_THAN("<"),
33  	LESS_THAN_EQUAL("<=");
34  	
35  	private final String code;
36  	
37  	private ComparisonOperator(String code) {
38  		this.code = code;
39  	}
40  	
41  	public String getCode() {
42  		return code;
43  	}
44  	
45  	public static ComparisonOperator fromCode(String code) {
46  		if (code == null) {
47  			return null;
48  		}
49  		for (ComparisonOperator comparisonOperator : values()) {
50  			if (comparisonOperator.code.equals(code)) {
51  				return comparisonOperator;
52  			}
53  		}
54  		throw new IllegalArgumentException("Failed to locate the ComparisionOperator with the given code: " + code);
55  	}
56  
57      private Object coerceRhs(Object lhs, Object rhs) {
58          if (lhs != null && rhs != null) {
59              if  (!lhs.getClass().equals(rhs.getClass()) && rhs instanceof String) {
60                  rhs = coerceRhsHelper(lhs, rhs.toString(), Double.class, Float.class, Long.class, Integer.class);
61  
62                  if (rhs instanceof String) { // was coercion successful?
63                      if (lhs instanceof BigDecimal) {
64                          try {
65                              rhs = BigDecimal.valueOf(Double.valueOf(rhs.toString()));
66                          } catch (NumberFormatException e) {
67                              throw new IncompatibleTypeException("Could not coerce String to BigDecimal" + this, rhs, lhs.getClass());
68                          }
69                      } else if (lhs instanceof BigInteger) {
70                          try {
71                              rhs = BigInteger.valueOf(Long.valueOf(rhs.toString()));
72                          } catch (NumberFormatException e) {
73                              throw new IncompatibleTypeException("Could not coerce String to BigInteger" + this, rhs, lhs.getClass());
74                          }
75                      } else {
76                          throw new IncompatibleTypeException("Could not compare values for operator " + this, lhs, rhs.getClass());
77                      }
78                  }
79              }
80          }
81          return rhs;
82      }
83  
84      private Object coerceRhsHelper(Object lhs, String rhs, Class<?> ... clazzes) {
85          for (Class clazz : clazzes) {
86              if (clazz.isInstance(lhs)) {
87                  try {
88                      return clazz.getMethod("valueOf", String.class).invoke(null, rhs);
89                  } catch (NumberFormatException e) {
90                      throw new IncompatibleTypeException("Could not coerce String to " +
91                              clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
92                  } catch (NoSuchMethodException e) {
93                      throw new IncompatibleTypeException("Could not coerce String to " +
94                              clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
95                  } catch (InvocationTargetException e) {
96                      throw new IncompatibleTypeException("Could not coerce String to " +
97                              clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
98                  } catch (IllegalAccessException e) {
99                      throw new IncompatibleTypeException("Could not coerce String to " +
100                             clazz.getSimpleName() + " " + this, rhs, lhs.getClass());
101                 }
102             }
103         }
104         return rhs;
105     }
106 	
107 	public boolean compare(Object lhs, Object rhs) {
108 		
109 		// TODO this implementation seems largely incomplete, it seems we are need to have some kind of engine
110 		// or utility which can coerce types to possible forms for comparision purposes?  For now, let's verify
111 		// they are of the same type
112 
113         rhs = coerceRhs(lhs, rhs);
114 
115         if (this == EQUALS) {
116 			return ObjectUtils.equals(lhs, rhs);
117 		} else if (this == NOT_EQUALS) {
118 			return ObjectUtils.notEqual(lhs, rhs);
119 		} else if (lhs == null || rhs == null) {
120 			// any other operation besides equals and not equals will evaluate to false in the case of null
121 			return false;
122 		}
123 		if (lhs instanceof Comparable && rhs instanceof Comparable) {
124 			
125 			// TODO not sure what to do about this cast and whether or not it will safe,
126 			// be sure to hit this in unit testing!
127 			
128 			int result = ((Comparable)lhs).compareTo(rhs);
129 			if (this == GREATER_THAN) {
130 				return result > 0;
131 			}
132 			if (this == GREATER_THAN_EQUAL) {
133 				return result >= 0;
134 			}
135 			if (this == LESS_THAN) {
136 				return result < 0;
137 			}
138 			if (this == LESS_THAN_EQUAL) {
139 				return result <= 0;
140 			}			
141 		} else {
142 			throw new IncompatibleTypeException("Could not compare values, they are not comparable for operator " + this, lhs, rhs.getClass());
143 		}
144 		throw new IllegalStateException("Invalid operator detected: " + this);
145 	}
146 
147 }