1 /** 2 * Copyright 2005 Steve Molloy 3 * 4 * This file is part of OV4J. 5 * 6 * OV4J is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 8 * 9 * OV4J is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License along with OV4J; if not, write to the Free Software 13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 14 * 15 */ 16 package org.ov4j.comp; 17 18 import java.util.Arrays; 19 import java.util.logging.Level; 20 import java.util.logging.Logger; 21 22 /** 23 * Base class for comparing objects. 24 * 25 * @author smolloy 26 * 27 */ 28 public abstract class ComparisonResult<T> { 29 /** 30 * Logger for this class 31 */ 32 private static final Logger logger = Logger.getLogger(ComparisonResult.class.getName()); 33 34 /** 35 * Find class to use to compare instances of the given object. 36 * 37 * @param o 38 * Object to be compared. 39 * @return Class to use for comparing the object. 40 * @throws ClassNotFoundException 41 */ 42 public static <C, T extends ComparisonResult<C>> Class<T> findComparisonResultClass(final C o) 43 throws ClassNotFoundException { 44 if (ComparisonResult.logger.isLoggable(Level.FINER)) { 45 ComparisonResult.logger.entering("ComparisonResult", "findComparisonResultClass(C=" + o + ")", "start"); 46 } 47 48 Class<T> compClass = null; 49 if (o != null) { 50 if (o instanceof String) { 51 compClass = (Class<T>) Class.forName("org.ov4j.comp.StringComparisonResult"); 52 } else if (o instanceof Number) { 53 compClass = (Class<T>) Class.forName("org.ov4j.comp.NumericComparisonResult"); 54 } else { 55 String compClassName = o.getClass().getName() + "ComparisonResult"; 56 compClassName = compClassName.replaceAll("\\.data", ".comp"); 57 try { 58 compClass = (Class<T>) Class.forName(compClassName); 59 } catch (final ClassNotFoundException e) { 60 if (ComparisonResult.logger.isLoggable(Level.FINE)) { 61 ComparisonResult.logger.logp(Level.FINE, "ComparisonResult", "findComparisonResultClass(C=" + o 62 + ")", "Exception caught", e); 63 } 64 65 compClass = (Class<T>) Class.forName("org.ov4j.comp.DefaultComparisonResult"); 66 } 67 } 68 } 69 70 if (ComparisonResult.logger.isLoggable(Level.FINER)) { 71 ComparisonResult.logger.exiting("ComparisonResult", "findComparisonResultClass(C=" + o + ")", 72 "end - return value=" + compClass); 73 } 74 return compClass; 75 } 76 77 /** Original object. */ 78 private T original; 79 80 /** Compared object. */ 81 private T changed; 82 83 /** Array of false negatives, not found in compared object. */ 84 private Object[] falseNegatives; 85 86 /** Array of false positives, which should not have been found in compared object. */ 87 private Object[] falsePositives; 88 89 /** Array of matches, each match holding more information. */ 90 private ComparisonResult<?>[] matches; 91 92 /** Precision, how much of the compared object is found in the original. */ 93 private double precision; 94 95 /** Recall, how much of the original object is found in the compared one. */ 96 private double recall; 97 98 /** 99 * Constructor. 100 */ 101 public ComparisonResult() { 102 } 103 104 /** 105 * Perform metric computation. 106 * 107 */ 108 public abstract void compute(); 109 110 /** 111 * Perform fast computation, only setting precision to 1.0 to indicate that both items are the same or to anything 112 * else otherwise. 113 * 114 */ 115 public abstract void fastCompute(); 116 117 /** 118 * @return Returns the compared object. 119 */ 120 public T getChanged() { 121 return changed; 122 } 123 124 /** 125 * @return Returns the falseNegatives. 126 */ 127 public Object[] getFalseNegatives() { 128 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 129 ComparisonResult.logger.entering("ComparisonResult", "getFalseNegatives()", "start"); 130 } 131 132 if (falseNegatives == null) { 133 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 134 ComparisonResult.logger 135 .exiting("ComparisonResult", "getFalseNegatives()", "end - return value=" + null); 136 } 137 return null; 138 } 139 final Object[] fn = new Object[falseNegatives.length]; 140 System.arraycopy(falseNegatives, 0, fn, 0, fn.length); 141 142 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 143 ComparisonResult.logger.exiting("ComparisonResult", "getFalseNegatives()", "end - return value=" 144 + Arrays.toString(fn)); 145 } 146 return fn; 147 } 148 149 /** 150 * @return Returns the falsePositives. 151 */ 152 public Object[] getFalsePositives() { 153 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 154 ComparisonResult.logger.entering("ComparisonResult", "getFalsePositives()", "start"); 155 } 156 157 if (falsePositives == null) { 158 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 159 ComparisonResult.logger 160 .exiting("ComparisonResult", "getFalsePositives()", "end - return value=" + null); 161 } 162 return null; 163 } 164 final Object[] fp = new Object[falsePositives.length]; 165 System.arraycopy(falsePositives, 0, fp, 0, fp.length); 166 167 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 168 ComparisonResult.logger.exiting("ComparisonResult", "getFalsePositives()", "end - return value=" 169 + Arrays.toString(fp)); 170 } 171 return fp; 172 } 173 174 /** 175 * @return Returns the matches. 176 */ 177 public ComparisonResult<?>[] getMatches() { 178 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 179 ComparisonResult.logger.entering("ComparisonResult", "getMatches()", "start"); 180 } 181 182 if (matches == null) { 183 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 184 ComparisonResult.logger.exiting("ComparisonResult", "getMatches()", "end - return value=" + null); 185 } 186 return null; 187 } 188 final ComparisonResult<?>[] m = new ComparisonResult[matches.length]; 189 System.arraycopy(matches, 0, m, 0, m.length); 190 191 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 192 ComparisonResult.logger.exiting("ComparisonResult", "getMatches()", "end - return value=" 193 + Arrays.toString(m)); 194 } 195 return m; 196 } 197 198 /** 199 * @return Returns the original. 200 */ 201 public T getOriginal() { 202 return original; 203 } 204 205 /** 206 * @return Returns the precision. 207 */ 208 public double getPrecision() { 209 return precision; 210 } 211 212 /** 213 * @return Returns the recall. 214 */ 215 public double getRecall() { 216 return recall; 217 } 218 219 /** 220 * @return 221 */ 222 public int numFalseNegatives() { 223 return (falseNegatives == null) ? 0 : falseNegatives.length; 224 } 225 226 /** 227 * @return 228 */ 229 public int numFalsePositives() { 230 return (falsePositives == null) ? 0 : falsePositives.length; 231 } 232 233 /** 234 * @return 235 */ 236 public int numMatches() { 237 return (matches == null) ? 0 : matches.length; 238 } 239 240 /** 241 * @param changed 242 * The compared object to set. 243 */ 244 public void setChanged(final T changed) { 245 this.changed = changed; 246 } 247 248 /** 249 * @param falseNegatives 250 * The falseNegatives to set. 251 */ 252 public void setFalseNegatives(final Object[] falseNegatives) { 253 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 254 ComparisonResult.logger.entering("ComparisonResult", "setFalseNegatives(Object[]=" 255 + Arrays.toString(falseNegatives) + ")", "start"); 256 } 257 258 if (falseNegatives == null) { 259 this.falseNegatives = null; 260 } else { 261 this.falseNegatives = new Object[falseNegatives.length]; 262 System.arraycopy(falseNegatives, 0, this.falseNegatives, 0, falseNegatives.length); 263 } 264 265 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 266 ComparisonResult.logger.exiting("ComparisonResult", "setFalseNegatives(Object[]=" 267 + Arrays.toString(falseNegatives) + ")", "end"); 268 } 269 } 270 271 /** 272 * @param falsePositives 273 * The falsePositives to set. 274 */ 275 public void setFalsePositives(final Object[] falsePositives) { 276 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 277 ComparisonResult.logger.entering("ComparisonResult", "setFalsePositives(Object[]=" 278 + Arrays.toString(falsePositives) + ")", "start"); 279 } 280 281 if (falsePositives == null) { 282 this.falsePositives = null; 283 } else { 284 this.falsePositives = new Object[falsePositives.length]; 285 System.arraycopy(falsePositives, 0, this.falsePositives, 0, falsePositives.length); 286 } 287 288 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 289 ComparisonResult.logger.exiting("ComparisonResult", "setFalsePositives(Object[]=" 290 + Arrays.toString(falsePositives) + ")", "end"); 291 } 292 } 293 294 /** 295 * @param matches 296 * The matches to set. 297 */ 298 public void setMatches(final ComparisonResult<?>[] matches) { 299 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 300 ComparisonResult.logger.entering("ComparisonResult", "setMatches(ComparisonResult<T>[]=" 301 + Arrays.toString(matches) + ")", "start"); 302 } 303 304 if (matches == null) { 305 this.matches = null; 306 } else { 307 this.matches = new ComparisonResult[matches.length]; 308 System.arraycopy(matches, 0, this.matches, 0, matches.length); 309 } 310 311 if (ComparisonResult.logger.isLoggable(Level.FINEST)) { 312 ComparisonResult.logger.exiting("ComparisonResult", "setMatches(ComparisonResult<T>[]=" 313 + Arrays.toString(matches) + ")", "end"); 314 } 315 } 316 317 /** 318 * @param original 319 * The original to set. 320 */ 321 public void setOriginal(final T original) { 322 this.original = original; 323 } 324 325 /** 326 * @param precision 327 * The precision to set. 328 */ 329 public void setPrecision(final double precision) { 330 this.precision = precision; 331 } 332 333 /** 334 * @param recall 335 * The recall to set. 336 */ 337 public void setRecall(final double recall) { 338 this.recall = recall; 339 } 340 }