1 | /* |
---|
2 | * This program is free software; you can redistribute it and/or modify |
---|
3 | * it under the terms of the GNU General Public License as published by |
---|
4 | * the Free Software Foundation; either version 2 of the License, or |
---|
5 | * (at your option) any later version. |
---|
6 | * |
---|
7 | * This program is distributed in the hope that it will be useful, |
---|
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | * GNU General Public License for more details. |
---|
11 | * |
---|
12 | * You should have received a copy of the GNU General Public License |
---|
13 | * along with this program; if not, write to the Free Software |
---|
14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
15 | */ |
---|
16 | |
---|
17 | /* |
---|
18 | * NumberNode.java |
---|
19 | * Copyright (C) 2006 Robert Jung |
---|
20 | * |
---|
21 | */ |
---|
22 | |
---|
23 | package weka.gui.ensembleLibraryEditor.tree; |
---|
24 | |
---|
25 | import java.math.BigDecimal; |
---|
26 | import java.text.NumberFormat; |
---|
27 | |
---|
28 | import javax.swing.tree.DefaultMutableTreeNode; |
---|
29 | |
---|
30 | /** |
---|
31 | * This subclass is responsible for allowing users to specify either a minimum, |
---|
32 | * maximum, or iterator value for Integer attributes. It stores a value that is |
---|
33 | * of type java.lang.Number to accomodate the many different number types used |
---|
34 | * by Weka classifiers. |
---|
35 | * |
---|
36 | * @author Robert Jung (mrbobjung@gmail.com) |
---|
37 | * @version $Revision: 1.1 $ |
---|
38 | */ |
---|
39 | public class NumberNode |
---|
40 | extends DefaultMutableTreeNode { |
---|
41 | |
---|
42 | /** for serialization */ |
---|
43 | private static final long serialVersionUID = -2505599954089243851L; |
---|
44 | |
---|
45 | /** the enumerated value indicating a node is not an iterator */ |
---|
46 | public static final int NOT_ITERATOR = 0; |
---|
47 | |
---|
48 | /** the enumerated value indicating a node is a *= iterator */ |
---|
49 | public static final int TIMES_EQUAL = 1; |
---|
50 | |
---|
51 | /** the enumerated value indicating a node is a += iterator */ |
---|
52 | public static final int PLUS_EQUAL = 2; |
---|
53 | |
---|
54 | /** the name of the node to be displayed */ |
---|
55 | private String m_Name; |
---|
56 | |
---|
57 | /** the iterator type, NOT_ITERATOR, TIMES_EQUAL, or PLUS_EQUAL */ |
---|
58 | private int m_IteratorType; |
---|
59 | |
---|
60 | /** this stores whether or not this node should have a checkbox */ |
---|
61 | private boolean m_Checkable; |
---|
62 | |
---|
63 | /** this stores the node's selected state */ |
---|
64 | private boolean m_Selected; |
---|
65 | |
---|
66 | /** the node's tipText */ |
---|
67 | private String m_ToolTipText; |
---|
68 | |
---|
69 | /** |
---|
70 | * This method rounds a double to the number of decimal places defined by |
---|
71 | * scale |
---|
72 | * |
---|
73 | * @param a the value to round |
---|
74 | * @return the rounded value |
---|
75 | */ |
---|
76 | public static double roundDouble(double a) { |
---|
77 | return new BigDecimal("" + a).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); |
---|
78 | } |
---|
79 | |
---|
80 | /** |
---|
81 | * This method rounds a float to the number of decimal places defined by |
---|
82 | * scale |
---|
83 | * |
---|
84 | * @param a the value to round |
---|
85 | * @return the rounded value |
---|
86 | */ |
---|
87 | public static float roundFloat(float a) { |
---|
88 | return new BigDecimal("" + a).setScale(scale, BigDecimal.ROUND_HALF_UP).floatValue(); |
---|
89 | } |
---|
90 | |
---|
91 | /** |
---|
92 | * this defines the number of decimal places we care about, we arbitrarily |
---|
93 | * chose 7 thinking that anything beyond this is overkill |
---|
94 | */ |
---|
95 | public static final int scale = 7; |
---|
96 | |
---|
97 | /** |
---|
98 | * This is the maximum floating point value that we care about when testing |
---|
99 | * for equality. |
---|
100 | */ |
---|
101 | public static final double epsilon = 0.000001; |
---|
102 | |
---|
103 | /** |
---|
104 | * The constructor simply initializes all of the member variables |
---|
105 | * |
---|
106 | * @param text the name |
---|
107 | * @param value the actual value |
---|
108 | * @param iteratorType the iterator type |
---|
109 | * @param checkable true if it's checkable |
---|
110 | * @param toolTipText the tooltip to use |
---|
111 | */ |
---|
112 | public NumberNode(String text, Number value, int iteratorType, |
---|
113 | boolean checkable, String toolTipText) { |
---|
114 | |
---|
115 | this.m_Name = text; |
---|
116 | setValue(value); |
---|
117 | this.m_IteratorType = iteratorType; |
---|
118 | this.m_Checkable = checkable; |
---|
119 | this.m_Selected = false; |
---|
120 | this.m_ToolTipText = toolTipText; |
---|
121 | } |
---|
122 | |
---|
123 | /** |
---|
124 | * getter for the node selected state |
---|
125 | * |
---|
126 | * @return whether or not this node is selected |
---|
127 | */ |
---|
128 | public boolean getSelected() { |
---|
129 | return m_Selected; |
---|
130 | } |
---|
131 | |
---|
132 | /** |
---|
133 | * setter for the node selected state |
---|
134 | * |
---|
135 | * @param newValue |
---|
136 | * the new selected state |
---|
137 | */ |
---|
138 | public void setSelected(boolean newValue) { |
---|
139 | m_Selected = newValue; |
---|
140 | } |
---|
141 | |
---|
142 | /** |
---|
143 | * getter for this node's object |
---|
144 | * |
---|
145 | * @return the current value |
---|
146 | */ |
---|
147 | public Number getValue() { |
---|
148 | return (Number) getUserObject(); |
---|
149 | } |
---|
150 | |
---|
151 | /** |
---|
152 | * setter for this nodes object |
---|
153 | * |
---|
154 | * @param newValue the new value to use |
---|
155 | */ |
---|
156 | public void setValue(Number newValue) { |
---|
157 | userObject = newValue; |
---|
158 | } |
---|
159 | |
---|
160 | /** |
---|
161 | * getter for this node's iteratorType which will be one of the three |
---|
162 | * enumerated values |
---|
163 | * |
---|
164 | * @return the iterator type |
---|
165 | */ |
---|
166 | public int getIteratorType() { |
---|
167 | return m_IteratorType; |
---|
168 | } |
---|
169 | |
---|
170 | /** |
---|
171 | * setter for this nodes iteratorType which should be one of the three |
---|
172 | * enumerated values |
---|
173 | * |
---|
174 | * @param newValue the new iterator type to use |
---|
175 | */ |
---|
176 | public void setIteratorType(int newValue) { |
---|
177 | m_IteratorType = newValue; |
---|
178 | } |
---|
179 | |
---|
180 | /** |
---|
181 | * returns whether or not this node can be toggled on and off |
---|
182 | * |
---|
183 | * @return true if it's checkable |
---|
184 | */ |
---|
185 | public boolean getCheckable() { |
---|
186 | return m_Checkable; |
---|
187 | } |
---|
188 | |
---|
189 | /** |
---|
190 | * returns the text to be displayed for this node |
---|
191 | * |
---|
192 | * @return the name |
---|
193 | */ |
---|
194 | public String getText() { |
---|
195 | return m_Name; |
---|
196 | } |
---|
197 | |
---|
198 | /** |
---|
199 | * getter for the tooltip text |
---|
200 | * |
---|
201 | * @return tooltip text |
---|
202 | */ |
---|
203 | public String getToolTipText() { |
---|
204 | return m_ToolTipText; |
---|
205 | } |
---|
206 | |
---|
207 | /** |
---|
208 | * this is a simple filter for the setUserObject method. We basically don't |
---|
209 | * want null values to be passed in. |
---|
210 | * |
---|
211 | * @param o the user object |
---|
212 | */ |
---|
213 | public void setUserObject(Object o) { |
---|
214 | if (o != null) |
---|
215 | super.setUserObject(o); |
---|
216 | } |
---|
217 | |
---|
218 | /** |
---|
219 | * returns a string representation |
---|
220 | * |
---|
221 | * @return a string representation |
---|
222 | */ |
---|
223 | public String toString() { |
---|
224 | return getClass().getName() + "[" + m_Name + ": " |
---|
225 | + getUserObject().toString() + "]"; |
---|
226 | } |
---|
227 | |
---|
228 | // *************** IMPORTANT!!! *********************** |
---|
229 | // I could not figure out a graceful way to deal with this! |
---|
230 | // we have a requirement here to add, multiply, and test for |
---|
231 | // equality various subclasses of java.lang.number the |
---|
232 | // following eight methods are extremely redundant and are |
---|
233 | // a horrible example cutting/pasting. However, this |
---|
234 | // is what I've ended up with and its very clunky looking. |
---|
235 | // If anyone knows a better way to do this then please be my |
---|
236 | // guest by all means! |
---|
237 | |
---|
238 | // I really can't beleive there's not some slick way of handling |
---|
239 | // this stuff built into the language |
---|
240 | |
---|
241 | /** |
---|
242 | * figures out the class of this node's object and returns a new instance of |
---|
243 | * it initialized with the value of "0". |
---|
244 | * |
---|
245 | * @return 0 as object |
---|
246 | * @throws NumberClassNotFoundException if number class not supported |
---|
247 | */ |
---|
248 | public Number getZeroValue() throws NumberClassNotFoundException { |
---|
249 | |
---|
250 | Number value = getValue(); |
---|
251 | Number zero = null; |
---|
252 | |
---|
253 | if (value instanceof Double) |
---|
254 | zero = new Double(0.0); |
---|
255 | else if (value instanceof Integer) |
---|
256 | zero = new Integer(0); |
---|
257 | else if (value instanceof Float) |
---|
258 | zero = new Float(0.0); |
---|
259 | else if (value instanceof Long) |
---|
260 | zero = new Long(0); |
---|
261 | else { |
---|
262 | throw new NumberClassNotFoundException(value.getClass() |
---|
263 | + " not currently supported."); |
---|
264 | } |
---|
265 | |
---|
266 | return zero; |
---|
267 | } |
---|
268 | |
---|
269 | /** |
---|
270 | * figures out the class of this node's object and returns a new instance of |
---|
271 | * it initialized with the value of "1". |
---|
272 | * |
---|
273 | * @return 1 as object |
---|
274 | * @throws NumberClassNotFoundException if number class not supported |
---|
275 | */ |
---|
276 | public Number getOneValue() throws NumberClassNotFoundException { |
---|
277 | |
---|
278 | Number value = getValue(); |
---|
279 | Number one = null; |
---|
280 | |
---|
281 | if (value instanceof Double) |
---|
282 | one = new Double(1.0); |
---|
283 | else if (value instanceof Integer) |
---|
284 | one = new Integer(1); |
---|
285 | else if (value instanceof Float) |
---|
286 | one = new Float(1.0); |
---|
287 | else if (value instanceof Long) |
---|
288 | one = new Long(1); |
---|
289 | else { |
---|
290 | throw new NumberClassNotFoundException(value.getClass() |
---|
291 | + " not currently supported."); |
---|
292 | } |
---|
293 | return one; |
---|
294 | } |
---|
295 | |
---|
296 | /** |
---|
297 | * figures out the class of this node's object and returns a new instance of |
---|
298 | * it initialized with the value of "2". |
---|
299 | * |
---|
300 | * @return 2 as object |
---|
301 | * @throws NumberClassNotFoundException if number class not supported |
---|
302 | */ |
---|
303 | public Number getTwoValue() throws NumberClassNotFoundException { |
---|
304 | |
---|
305 | Number value = getValue(); |
---|
306 | Number two = null; |
---|
307 | |
---|
308 | if (value instanceof Double) |
---|
309 | two = new Double(2.0); |
---|
310 | else if (value instanceof Integer) |
---|
311 | two = new Integer(2); |
---|
312 | else if (value instanceof Float) |
---|
313 | two = new Float(2.0); |
---|
314 | else if (value instanceof Long) |
---|
315 | two = new Long(2); |
---|
316 | else { |
---|
317 | throw new NumberClassNotFoundException(value.getClass() |
---|
318 | + " not currently supported."); |
---|
319 | } |
---|
320 | return two; |
---|
321 | } |
---|
322 | |
---|
323 | /** |
---|
324 | * adds two objects that are instances of one of the child classes of |
---|
325 | * java.lang.Number |
---|
326 | * |
---|
327 | * @param a the first number |
---|
328 | * @param b the second number |
---|
329 | * @return the sum: a+b |
---|
330 | * @throws NumberClassNotFoundException if number class not supported |
---|
331 | */ |
---|
332 | public Number addNumbers(Number a, Number b) |
---|
333 | throws NumberClassNotFoundException { |
---|
334 | |
---|
335 | Number sum = null; |
---|
336 | |
---|
337 | if (a instanceof Double && b instanceof Double) { |
---|
338 | sum = new Double(roundDouble(a.doubleValue() + b.doubleValue())); |
---|
339 | // trimNumber(sum); |
---|
340 | |
---|
341 | } else if (a instanceof Integer && b instanceof Integer) { |
---|
342 | sum = new Integer(a.intValue() + b.intValue()); |
---|
343 | } else if (a instanceof Float && b instanceof Float) { |
---|
344 | sum = new Float(roundFloat(a.floatValue() + b.floatValue())); |
---|
345 | |
---|
346 | // trimNumber(sum); |
---|
347 | |
---|
348 | } else if (a instanceof Long && b instanceof Long) { |
---|
349 | sum = new Long(a.longValue() + b.longValue()); |
---|
350 | } else { |
---|
351 | throw new NumberClassNotFoundException(a.getClass() + " and " |
---|
352 | + b.getClass() + " not currently supported."); |
---|
353 | } |
---|
354 | return sum; |
---|
355 | } |
---|
356 | |
---|
357 | /** |
---|
358 | * multiplies two objects that are instances of one of the child classes of |
---|
359 | * java.lang.Number |
---|
360 | * |
---|
361 | * @param a the first number |
---|
362 | * @param b the second number |
---|
363 | * @return the product: a*b |
---|
364 | * @throws NumberClassNotFoundException if number class not supported |
---|
365 | */ |
---|
366 | public Number multiplyNumbers(Number a, Number b) |
---|
367 | throws NumberClassNotFoundException { |
---|
368 | |
---|
369 | Number product = null; |
---|
370 | |
---|
371 | if (a instanceof Double && b instanceof Double) { |
---|
372 | product = new Double(roundDouble(a.doubleValue() * b.doubleValue())); |
---|
373 | |
---|
374 | } else if (a instanceof Integer && b instanceof Integer) { |
---|
375 | product = new Integer(a.intValue() * b.intValue()); |
---|
376 | } else if (a instanceof Float && b instanceof Float) { |
---|
377 | product = new Float(roundFloat(a.floatValue() * b.floatValue())); |
---|
378 | |
---|
379 | } else if (a instanceof Long && b instanceof Long) { |
---|
380 | product = new Long(a.longValue() * b.longValue()); |
---|
381 | } else { |
---|
382 | throw new NumberClassNotFoundException(a.getClass() + " and " |
---|
383 | + b.getClass() + " not currently supported."); |
---|
384 | } |
---|
385 | return product; |
---|
386 | } |
---|
387 | |
---|
388 | /** |
---|
389 | * tests if the first argument is greater than the second among two objects |
---|
390 | * that are instances of one of the child classes of java.lang.Number |
---|
391 | * |
---|
392 | * @param a the first number |
---|
393 | * @param b the second number |
---|
394 | * @return true if a is less than b |
---|
395 | * @throws NumberClassNotFoundException if number class not supported |
---|
396 | */ |
---|
397 | public boolean lessThan(Number a, Number b) |
---|
398 | throws NumberClassNotFoundException { |
---|
399 | |
---|
400 | boolean greater = false; |
---|
401 | |
---|
402 | if (a instanceof Double && b instanceof Double) { |
---|
403 | if (a.doubleValue() < b.doubleValue()) |
---|
404 | greater = true; |
---|
405 | } else if (a instanceof Integer && b instanceof Integer) { |
---|
406 | if (a.intValue() < b.intValue()) |
---|
407 | greater = true; |
---|
408 | } else if (a instanceof Float && b instanceof Float) { |
---|
409 | if (a.floatValue() < b.floatValue()) |
---|
410 | greater = true; |
---|
411 | } else if (a instanceof Long && b instanceof Long) { |
---|
412 | if (a.longValue() < b.longValue()) |
---|
413 | greater = true; |
---|
414 | } else { |
---|
415 | throw new NumberClassNotFoundException(a.getClass() + " and " |
---|
416 | + b.getClass() + " not currently supported."); |
---|
417 | } |
---|
418 | |
---|
419 | return greater; |
---|
420 | |
---|
421 | } |
---|
422 | |
---|
423 | /** |
---|
424 | * tests for equality among two objects that are instances of one of the |
---|
425 | * child classes of java.lang.Number |
---|
426 | * |
---|
427 | * @param a the first number |
---|
428 | * @param b the second number |
---|
429 | * @return true if the two values are equal |
---|
430 | * @throws NumberClassNotFoundException if number class not supported |
---|
431 | */ |
---|
432 | public boolean equals(Number a, Number b) |
---|
433 | throws NumberClassNotFoundException { |
---|
434 | |
---|
435 | boolean equals = false; |
---|
436 | |
---|
437 | if (a instanceof Double && b instanceof Double) { |
---|
438 | if (Math.abs(a.doubleValue() - b.doubleValue()) < epsilon) |
---|
439 | equals = true; |
---|
440 | } else if (a instanceof Integer && b instanceof Integer) { |
---|
441 | if (a.intValue() == b.intValue()) |
---|
442 | equals = true; |
---|
443 | } else if (a instanceof Float && b instanceof Float) { |
---|
444 | if (Math.abs(a.floatValue() - b.floatValue()) < epsilon) |
---|
445 | equals = true; |
---|
446 | } else if (a instanceof Long && b instanceof Long) { |
---|
447 | if (a.longValue() == b.longValue()) |
---|
448 | equals = true; |
---|
449 | } else { |
---|
450 | throw new NumberClassNotFoundException(a.getClass() + " and " |
---|
451 | + b.getClass() + " not currently supported."); |
---|
452 | } |
---|
453 | |
---|
454 | return equals; |
---|
455 | } |
---|
456 | |
---|
457 | /** |
---|
458 | * A helper method to figure out what number format should be used to |
---|
459 | * display the numbers value in a formatted text box. |
---|
460 | * |
---|
461 | * @return the number format |
---|
462 | * @throws NumberClassNotFoundException if number class not supported |
---|
463 | */ |
---|
464 | public NumberFormat getNumberFormat() throws NumberClassNotFoundException { |
---|
465 | NumberFormat numberFormat = null; |
---|
466 | |
---|
467 | Number value = getValue(); |
---|
468 | |
---|
469 | if (value instanceof Double) { |
---|
470 | numberFormat = NumberFormat.getInstance(); |
---|
471 | numberFormat.setMaximumFractionDigits(7); |
---|
472 | } else if (value instanceof Integer) { |
---|
473 | numberFormat = NumberFormat.getIntegerInstance(); |
---|
474 | } else if (value instanceof Float) { |
---|
475 | numberFormat = NumberFormat.getInstance(); |
---|
476 | numberFormat.setMaximumFractionDigits(7); |
---|
477 | } else if (value instanceof Long) { |
---|
478 | numberFormat = NumberFormat.getIntegerInstance(); |
---|
479 | } else { |
---|
480 | throw new NumberClassNotFoundException(value.getClass() |
---|
481 | + " not currently supported."); |
---|
482 | } |
---|
483 | |
---|
484 | return numberFormat; |
---|
485 | } |
---|
486 | } |
---|