001package jmri.util; 002 003/** 004 * Compare values. 005 * 006 * @author Daniel Bergqvist Copyright 2022 007 */ 008public class CompareUtil { 009 010 public static enum CompareType { 011 NumberOrString(Bundle.getMessage("CompareUtil_CompareType_NumberOrString")), 012 String(Bundle.getMessage("CompareUtil_CompareType_String")), 013 Number(Bundle.getMessage("CompareUtil_CompareType_Number")); 014 015 private final String _text; 016 017 private CompareType(String text) { 018 this._text = text; 019 } 020 021 @Override 022 public String toString() { 023 return _text; 024 } 025 026 } 027 028 public static enum CompareOperation { 029 LessThan(Bundle.getMessage("CompareUtil_CompareOperation_LessThan")), 030 LessThanOrEqual(Bundle.getMessage("CompareUtil_CompareOperation_LessThanOrEqual")), 031 Equal(Bundle.getMessage("CompareUtil_CompareOperation_Equal")), 032 GreaterThanOrEqual(Bundle.getMessage("CompareUtil_CompareOperation_GreaterThanOrEqual")), 033 GreaterThan(Bundle.getMessage("CompareUtil_CompareOperation_GreaterThan")), 034 NotEqual(Bundle.getMessage("CompareUtil_CompareOperation_NotEqual")); 035 036 private final String _text; 037 038 private CompareOperation(String text) { 039 this._text = text; 040 } 041 042 @Override 043 public String toString() { 044 return _text; 045 } 046 047 } 048 049 050 /** 051 * Compare two values. 052 * 053 * @param type the type 054 * @param oper the operation 055 * @param value1 left side of the comparison 056 * @param value2 right side of the comparison 057 * @param caseInsensitive true if comparison should be case insensitive; 058 * false otherwise 059 * @return true if values compare per _memoryOperation; false otherwise 060 */ 061 public static boolean compare(CompareType type, CompareOperation oper, Object value1, Object value2, boolean caseInsensitive) { 062 switch (type) // both are numbers 063 { 064 case NumberOrString: 065 return compareNumber(false, oper, value1, value2, caseInsensitive); 066 case String: 067 return compareString(oper, value1, value2, caseInsensitive); 068 case Number: 069 return compareNumber(true, oper, value1, value2, caseInsensitive); 070 default: 071 throw new IllegalArgumentException("type has unknown value: "+type.name()); 072 } 073 } 074 075 /** 076 * Compare two values. 077 * 078 * @param oper the operation 079 * @param value1 left side of the comparison 080 * @param value2 right side of the comparison 081 * @param caseInsensitive true if comparison should be case insensitive; 082 * false otherwise 083 * @return true if values compare per _memoryOperation; false otherwise 084 */ 085 public static boolean compareString(CompareOperation oper, Object value1, Object value2, boolean caseInsensitive) { 086 String s1; 087 String s2; 088 if (value1 == null) { 089 return value2 == null; 090 } else { 091 if (value2 == null) { 092 return false; 093 } 094 s1 = value1.toString().trim(); 095 s2 = value2.toString().trim(); 096 } 097 int compare; 098 if (caseInsensitive) { 099 compare = s1.compareToIgnoreCase(s2); 100 } else { 101 compare = s1.compareTo(s2); 102 } 103 switch (oper) { 104 case LessThan: 105 if (compare < 0) { 106 return true; 107 } 108 break; 109 case LessThanOrEqual: 110 if (compare <= 0) { 111 return true; 112 } 113 break; 114 case Equal: 115 if (compare == 0) { 116 return true; 117 } 118 break; 119 case NotEqual: 120 if (compare != 0) { 121 return true; 122 } 123 break; 124 case GreaterThanOrEqual: 125 if (compare >= 0) { 126 return true; 127 } 128 break; 129 case GreaterThan: 130 if (compare > 0) { 131 return true; 132 } 133 break; 134 default: 135 throw new IllegalArgumentException("oper has unknown value: "+oper.name()); 136 } 137 return false; 138 } 139 140 /** 141 * Compare two values. 142 * 143 * @param requireNumber true if two numbers are required, false otherwise 144 * @param oper the operation 145 * @param value1 left side of the comparison 146 * @param value2 right side of the comparison 147 * @param caseInsensitive true if comparison should be case insensitive; 148 * false otherwise 149 * @return true if values compare per _memoryOperation; false otherwise 150 */ 151 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", 152 justification = "User explicitly requested check for equality with EQUAL case") 153 public static boolean compareNumber(boolean requireNumber, CompareOperation oper, Object value1, Object value2, boolean caseInsensitive) { 154 String s1; 155 String s2; 156 if (value1 == null) { 157 return value2 == null; 158 } else { 159 if (value2 == null) { 160 return false; 161 } 162 s1 = value1.toString().trim(); 163 s2 = value2.toString().trim(); 164 } 165 try { 166 double n1; 167 if (value1 instanceof Number) { 168 n1 = ((Number)value1).doubleValue(); 169 } else { 170 n1 = Double.parseDouble(s1); 171 } 172 try { 173 double n2; 174 if (value2 instanceof Number) { 175 n2 = ((Number)value2).doubleValue(); 176 } else { 177 n2 = Double.parseDouble(s2); 178 } 179 log.debug("Compare numbers: n1= {} to n2= {}", n1, n2); 180 switch (oper) // both are numbers 181 { 182 case LessThan: 183 return (n1 < n2); 184 case LessThanOrEqual: 185 return (n1 <= n2); 186 case Equal: 187 return (n1 == n2); 188 case NotEqual: 189 return (n1 != n2); 190 case GreaterThanOrEqual: 191 return (n1 >= n2); 192 case GreaterThan: 193 return (n1 > n2); 194 default: 195 throw new IllegalArgumentException("oper has unknown value: "+oper.name()); 196 } 197 } catch (NumberFormatException nfe) { 198 if (requireNumber) throw new IllegalArgumentException( 199 Bundle.getMessage("CompareUtil_Error_Value1IsNotANumber", value1)); 200 return oper == CompareOperation.NotEqual; // n1 is a number, n2 is not 201 } 202 } catch (NumberFormatException nfe) { 203 try { 204 Integer.parseInt(s2); 205 if (requireNumber) throw new IllegalArgumentException( 206 Bundle.getMessage("CompareUtil_Error_Value1IsNotANumber", value1)); 207 return oper == CompareOperation.NotEqual; // n1 is not a number, n2 is 208 } catch (NumberFormatException ex) { // OK neither a number 209 if (requireNumber) throw new IllegalArgumentException( 210 Bundle.getMessage("CompareUtil_Error_NeitherValueIsNumber", value1, value2)); 211 } 212 } 213 214 // If here, neither value is a number and it's not required. 215 return compareString(oper, value1, value2, caseInsensitive); 216 } 217 218 219 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CompareUtil.class); 220 221}