/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.model;
import javolution37.javolution.xml.XmlElement;
import javolution37.javolution.xml.XmlFormat;
import com.jstatcom.util.UString;
/**
* A number representation that conforms to the JSCData
* interface. Numbers are stored as Double objects.
*
* Whenever the value of this data object changes, registered JSCDataListeners
* are notified via a JSCDataEvent. If not otherwise stated, the
* event objects always contain the values before the change and after
* the change.
*
* This class is thread-safe. All access to mutable data is synchronized.
* All events are fired outside the synchronized blocks.
*
* @author Markus Kraetzig
*/
public final class JSCNumber extends AbstractJSCData {
private final String name;
private Double value = null;
private JSCDataEvent clearEvent = null;
private JSCDataEvent notEmptyEvent = null;
/**
* XML format field for (de)serialization.
*/
public static final XmlFormat JSCNumber_XML = new XmlFormat(
JSCNumber.class) {
public void format(JSCNumber data, XmlElement xml) {
xml.setAttribute("name", data.name);
synchronized (data) {
xml.setAttribute("value", data.value + "");
}
}
public JSCNumber parse(XmlElement xml) {
String nam = xml.getAttribute("name") + "";
String numberString = xml.getAttribute("value") + "";
Double newNumber = null;
if (!numberString.equalsIgnoreCase("null"))
newNumber = UString.parseToNumber(numberString);
JSCNumber num = new JSCNumber(nam, newNumber);
return num;
}
};
/**
* Creates an empty JSCNumber.
*
* @param name the identifier of that object
* @throws IllegalArgumentException if name is not valid
* or if (name == null)
*
* @see com.jstatcom.model.JSCConstants#isValidName
*/
public JSCNumber(String name) {
this(name, null);
}
/**
* Creates a JSCNumber from value.
*
* @param name the identifier of that object
* @param value the double to be used as value
* @throws IllegalArgumentException if name is not valid
* or if (name == null)
*
* @see com.jstatcom.model.JSCConstants#isValidName
*/
public JSCNumber(String name, double value) {
this(name, new Double(value));
}
/**
* Creates a JSCNumber from value.
*
* @param name the identifier of that object
* @param value the Double to be used as value
* @throws IllegalArgumentException if name is not valid
* or if (name == null)
*
* @see com.jstatcom.model.JSCConstants#isValidName
*/
public JSCNumber(String name, Double value) {
super();
if (name == null)
throw new IllegalArgumentException("Argument was null.");
name = JSCConstants.checkNameThrowEx(name);
this.name = name;
this.value = value;
}
/**
* Clears this data object. Fires:
*
* JSCDataEventTypes.EMPTY_STATE event
* JSCDataEventTypes.VALUE_CHANGED event
*
* if it was not empty before. After calling this
* method isEmpty returns true.
*/
public void clear() {
Double oldValue;
synchronized (this) {
if (value == null) // Nothing changes.
return;
oldValue = value;
value = null;
}
// Dispatch event outside synchronized block.
if (clearEvent == null)
clearEvent = JSCDataEvent.valueOfEmptyState(this, true);
if (eventSupport == null)
return;
getEventSupport().dispatchEvent(clearEvent);
getEventSupport().dispatchEvent(
JSCDataEvent.valueOfChanged(this, oldValue, null));
}
/**
* @see com.jstatcom.model.JSCData
*/
public synchronized JSCNumber copy() {
return new JSCNumber(name, value);
}
/**
* Gets the stored number as a double.
*
* @return the value as double
* @throws IllegalStateException if object is empty
*/
public synchronized double doubleVal() {
if (value == null)
throw new IllegalStateException("JSCData is empty: " + toString()
+ ".");
return value.doubleValue();
}
/**
* Gets the stored Double value.
*
* @return a Double or null if empty
*/
public synchronized Double getNumber() {
return value;
}
/**
* Gets the stored number as an int.
* The following special int values are returned:
*
* - 0 if
value == Double.NaN
* Integer.MAX_VALUE if value == Double.POSITIVE_INFINITY
* Integer.MIN_VALUE if value == Double.NEGATIVE_INFINITY
*
* Note that during this operation information is potentially lost.
* Only the integer part is preserved, which is different from rounding.
*
* @return the result of getNumber().intValue()
* @throws IllegalStateException if object is empty
*/
public synchronized int intVal() {
if (value == null)
throw new IllegalStateException("JSCData is empty: " + toString()
+ ".");
return value.intValue();
}
/**
* Gets whether this is empty. This method returns true,
* if value returns null.
*
* @return true if empty
*/
public synchronized boolean isEmpty() {
return value == null;
}
/**
* @see com.jstatcom.model.JSCData
*/
public synchronized boolean isEqual(JSCData o) {
if (o == this)
return true;
if (!(o instanceof JSCNumber))
return false;
JSCNumber otherNumber = (JSCNumber) o;
boolean ret = true;
synchronized (this) {
synchronized (o) {
if (value != null)
ret = value.equals(otherNumber.getNumber());
else
ret = otherNumber.getNumber() == null;
}
}
return ret;
}
/**
* Gets whether the value equals Double.NaN.
*
* @return true if this represents a missing value
* @throws IllegalStateException if object is empty
*/
public synchronized boolean isNaN() {
if (value == null)
throw new IllegalStateException("JSCData is empty: " + toString()
+ ".");
return Double.isNaN(value);
}
/**
* @see com.jstatcom.model.JSCData
*/
public String name() {
return name;
}
/**
* Sets the value of this to newValue and fires
* a JSCDataEvent of type:
*
* JSCDataEventTypes.VALUE_CHANGED if !newValue.equals(getNumber())
* JSCDataEventTypes.EMPTY_STATE if newValue != null and this was empty before
* JSCDataEventTypes.EMPTY_STATE if newValue == null and this was not empty before
*
*
* @param newValue the new value
*/
public void setNumber(Double newValue) {
if (newValue == null) {
clear();
return;
}
boolean wasEmpty = false;
Double oldValue = null;
synchronized (this) {
// Nothing changed.
if (newValue.equals(value))
return;
wasEmpty = isEmpty();
// No copy needed, because Double is immutable.
oldValue = value;
value = newValue;
}
// Dispatch event outside synchronized block.
if (eventSupport == null)
return;
getEventSupport().dispatchEvent(
JSCDataEvent.valueOfChanged(this, oldValue, newValue));
if (wasEmpty) {
if (notEmptyEvent == null)
notEmptyEvent = JSCDataEvent.valueOfEmptyState(this, false);
getEventSupport().dispatchEvent(notEmptyEvent);
}
}
/**
* Sets the value of this to newDouble. Fires
* a JSCDataEvent of type:
*
* JSCDataEventTypes.VALUE_CHANGED if the new value is not equal to the previous
* JSCDataEventTypes.EMPTY_STATE if this was empty before
*
*
* @param newDouble the new double
*/
public void setVal(double newDouble) {
setNumber(new Double(newDouble));
}
/**
* Gets the type of this JSCData object.
*
* @return JSCTypes.NUMBER
*/
public JSCTypes type() {
return JSCTypes.NUMBER;
}
/**
* Gets the underlying value, which is stored as a Double.
*
* @return the value object or null if this is empty
*/
public synchronized Object value() {
return value;
}
}
Ads help maintain this website.