/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* NumberNode.java
* Copyright (C) 2006 Robert Jung
*
*/
package weka.gui.ensembleLibraryEditor.tree;
import javax.swing.tree.DefaultMutableTreeNode;
import java.math.BigDecimal;
import java.text.NumberFormat;
/**
* This subclass is responsible for allowing users to specify either a minimum,
* maximum, or iterator value for Integer attributes. It stores a value that is
* of type java.lang.Number to accomodate the many different number types used
* by Weka classifiers.
*
* @author Robert Jung (mrbobjung@gmail.com)
* @version $Revision: 1.1 $
*/
public class NumberNode
extends DefaultMutableTreeNode {
/** for serialization */
private static final long serialVersionUID = -2505599954089243851L;
/** the enumerated value indicating a node is not an iterator */
public static final int NOT_ITERATOR = 0;
/** the enumerated value indicating a node is a *= iterator */
public static final int TIMES_EQUAL = 1;
/** the enumerated value indicating a node is a += iterator */
public static final int PLUS_EQUAL = 2;
/** the name of the node to be displayed */
private String m_Name;
/** the iterator type, NOT_ITERATOR, TIMES_EQUAL, or PLUS_EQUAL */
private int m_IteratorType;
/** this stores whether or not this node should have a checkbox */
private boolean m_Checkable;
/** this stores the node's selected state */
private boolean m_Selected;
/** the node's tipText */
private String m_ToolTipText;
/**
* This method rounds a double to the number of decimal places defined by
* scale
*
* @param a the value to round
* @return the rounded value
*/
public static double roundDouble(double a) {
return new BigDecimal("" + a).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* This method rounds a float to the number of decimal places defined by
* scale
*
* @param a the value to round
* @return the rounded value
*/
public static float roundFloat(float a) {
return new BigDecimal("" + a).setScale(scale, BigDecimal.ROUND_HALF_UP).floatValue();
}
/**
* this defines the number of decimal places we care about, we arbitrarily
* chose 7 thinking that anything beyond this is overkill
*/
public static final int scale = 7;
/**
* This is the maximum floating point value that we care about when testing
* for equality.
*/
public static final double epsilon = 0.000001;
/**
* The constructor simply initializes all of the member variables
*
* @param text the name
* @param value the actual value
* @param iteratorType the iterator type
* @param checkable true if it's checkable
* @param toolTipText the tooltip to use
*/
public NumberNode(String text, Number value, int iteratorType,
boolean checkable, String toolTipText) {
this.m_Name = text;
setValue(value);
this.m_IteratorType = iteratorType;
this.m_Checkable = checkable;
this.m_Selected = false;
this.m_ToolTipText = toolTipText;
}
/**
* getter for the node selected state
*
* @return whether or not this node is selected
*/
public boolean getSelected() {
return m_Selected;
}
/**
* setter for the node selected state
*
* @param newValue
* the new selected state
*/
public void setSelected(boolean newValue) {
m_Selected = newValue;
}
/**
* getter for this node's object
*
* @return the current value
*/
public Number getValue() {
return (Number) getUserObject();
}
/**
* setter for this nodes object
*
* @param newValue the new value to use
*/
public void setValue(Number newValue) {
userObject = newValue;
}
/**
* getter for this node's iteratorType which will be one of the three
* enumerated values
*
* @return the iterator type
*/
public int getIteratorType() {
return m_IteratorType;
}
/**
* setter for this nodes iteratorType which should be one of the three
* enumerated values
*
* @param newValue the new iterator type to use
*/
public void setIteratorType(int newValue) {
m_IteratorType = newValue;
}
/**
* returns whether or not this node can be toggled on and off
*
* @return true if it's checkable
*/
public boolean getCheckable() {
return m_Checkable;
}
/**
* returns the text to be displayed for this node
*
* @return the name
*/
public String getText() {
return m_Name;
}
/**
* getter for the tooltip text
*
* @return tooltip text
*/
public String getToolTipText() {
return m_ToolTipText;
}
/**
* this is a simple filter for the setUserObject method. We basically don't
* want null values to be passed in.
*
* @param o the user object
*/
public void setUserObject(Object o) {
if (o != null)
super.setUserObject(o);
}
/**
* returns a string representation
*
* @return a string representation
*/
public String toString() {
return getClass().getName() + "[" + m_Name + ": "
+ getUserObject().toString() + "]";
}
// *************** IMPORTANT!!! ***********************
// I could not figure out a graceful way to deal with this!
// we have a requirement here to add, multiply, and test for
// equality various subclasses of java.lang.number the
// following eight methods are extremely redundant and are
// a horrible example cutting/pasting. However, this
// is what I've ended up with and its very clunky looking.
// If anyone knows a better way to do this then please be my
// guest by all means!
// I really can't beleive there's not some slick way of handling
// this stuff built into the language
/**
* figures out the class of this node's object and returns a new instance of
* it initialized with the value of "0".
*
* @return 0 as object
* @throws NumberClassNotFoundException if number class not supported
*/
public Number getZeroValue() throws NumberClassNotFoundException {
Number value = getValue();
Number zero = null;
if (value instanceof Double)
zero = new Double(0.0);
else if (value instanceof Integer)
zero = new Integer(0);
else if (value instanceof Float)
zero = new Float(0.0);
else if (value instanceof Long)
zero = new Long(0);
else {
throw new NumberClassNotFoundException(value.getClass()
+ " not currently supported.");
}
return zero;
}
/**
* figures out the class of this node's object and returns a new instance of
* it initialized with the value of "1".
*
* @return 1 as object
* @throws NumberClassNotFoundException if number class not supported
*/
public Number getOneValue() throws NumberClassNotFoundException {
Number value = getValue();
Number one = null;
if (value instanceof Double)
one = new Double(1.0);
else if (value instanceof Integer)
one = new Integer(1);
else if (value instanceof Float)
one = new Float(1.0);
else if (value instanceof Long)
one = new Long(1);
else {
throw new NumberClassNotFoundException(value.getClass()
+ " not currently supported.");
}
return one;
}
/**
* figures out the class of this node's object and returns a new instance of
* it initialized with the value of "2".
*
* @return 2 as object
* @throws NumberClassNotFoundException if number class not supported
*/
public Number getTwoValue() throws NumberClassNotFoundException {
Number value = getValue();
Number two = null;
if (value instanceof Double)
two = new Double(2.0);
else if (value instanceof Integer)
two = new Integer(2);
else if (value instanceof Float)
two = new Float(2.0);
else if (value instanceof Long)
two = new Long(2);
else {
throw new NumberClassNotFoundException(value.getClass()
+ " not currently supported.");
}
return two;
}
/**
* adds two objects that are instances of one of the child classes of
* java.lang.Number
*
* @param a the first number
* @param b the second number
* @return the sum: a+b
* @throws NumberClassNotFoundException if number class not supported
*/
public Number addNumbers(Number a, Number b)
throws NumberClassNotFoundException {
Number sum = null;
if (a instanceof Double && b instanceof Double) {
sum = new Double(roundDouble(a.doubleValue() + b.doubleValue()));
// trimNumber(sum);
} else if (a instanceof Integer && b instanceof Integer) {
sum = new Integer(a.intValue() + b.intValue());
} else if (a instanceof Float && b instanceof Float) {
sum = new Float(roundFloat(a.floatValue() + b.floatValue()));
// trimNumber(sum);
} else if (a instanceof Long && b instanceof Long) {
sum = new Long(a.longValue() + b.longValue());
} else {
throw new NumberClassNotFoundException(a.getClass() + " and "
+ b.getClass() + " not currently supported.");
}
return sum;
}
/**
* multiplies two objects that are instances of one of the child classes of
* java.lang.Number
*
* @param a the first number
* @param b the second number
* @return the product: a*b
* @throws NumberClassNotFoundException if number class not supported
*/
public Number multiplyNumbers(Number a, Number b)
throws NumberClassNotFoundException {
Number product = null;
if (a instanceof Double && b instanceof Double) {
product = new Double(roundDouble(a.doubleValue() * b.doubleValue()));
} else if (a instanceof Integer && b instanceof Integer) {
product = new Integer(a.intValue() * b.intValue());
} else if (a instanceof Float && b instanceof Float) {
product = new Float(roundFloat(a.floatValue() * b.floatValue()));
} else if (a instanceof Long && b instanceof Long) {
product = new Long(a.longValue() * b.longValue());
} else {
throw new NumberClassNotFoundException(a.getClass() + " and "
+ b.getClass() + " not currently supported.");
}
return product;
}
/**
* tests if the first argument is greater than the second among two objects
* that are instances of one of the child classes of java.lang.Number
*
* @param a the first number
* @param b the second number
* @return true if a is less than b
* @throws NumberClassNotFoundException if number class not supported
*/
public boolean lessThan(Number a, Number b)
throws NumberClassNotFoundException {
boolean greater = false;
if (a instanceof Double && b instanceof Double) {
if (a.doubleValue() < b.doubleValue())
greater = true;
} else if (a instanceof Integer && b instanceof Integer) {
if (a.intValue() < b.intValue())
greater = true;
} else if (a instanceof Float && b instanceof Float) {
if (a.floatValue() < b.floatValue())
greater = true;
} else if (a instanceof Long && b instanceof Long) {
if (a.longValue() < b.longValue())
greater = true;
} else {
throw new NumberClassNotFoundException(a.getClass() + " and "
+ b.getClass() + " not currently supported.");
}
return greater;
}
/**
* tests for equality among two objects that are instances of one of the
* child classes of java.lang.Number
*
* @param a the first number
* @param b the second number
* @return true if the two values are equal
* @throws NumberClassNotFoundException if number class not supported
*/
public boolean equals(Number a, Number b)
throws NumberClassNotFoundException {
boolean equals = false;
if (a instanceof Double && b instanceof Double) {
if (Math.abs(a.doubleValue() - b.doubleValue()) < epsilon)
equals = true;
} else if (a instanceof Integer && b instanceof Integer) {
if (a.intValue() == b.intValue())
equals = true;
} else if (a instanceof Float && b instanceof Float) {
if (Math.abs(a.floatValue() - b.floatValue()) < epsilon)
equals = true;
} else if (a instanceof Long && b instanceof Long) {
if (a.longValue() == b.longValue())
equals = true;
} else {
throw new NumberClassNotFoundException(a.getClass() + " and "
+ b.getClass() + " not currently supported.");
}
return equals;
}
/**
* A helper method to figure out what number format should be used to
* display the numbers value in a formatted text box.
*
* @return the number format
* @throws NumberClassNotFoundException if number class not supported
*/
public NumberFormat getNumberFormat() throws NumberClassNotFoundException {
NumberFormat numberFormat = null;
Number value = getValue();
if (value instanceof Double) {
numberFormat = NumberFormat.getInstance();
numberFormat.setMaximumFractionDigits(7);
} else if (value instanceof Integer) {
numberFormat = NumberFormat.getIntegerInstance();
} else if (value instanceof Float) {
numberFormat = NumberFormat.getInstance();
numberFormat.setMaximumFractionDigits(7);
} else if (value instanceof Long) {
numberFormat = NumberFormat.getIntegerInstance();
} else {
throw new NumberClassNotFoundException(value.getClass()
+ " not currently supported.");
}
return numberFormat;
}
}