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 }