package org.atdl4j.ui.swing.widget;
import java.awt.Color;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import javax.swing.AbstractSpinnerModel;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.NumberFormatter;
public class SwingNullableSpinner extends JSpinner {
private static final long serialVersionUID = 2947835451995064559L;
public static BigDecimal MIN_INTEGER_VALUE_AS_BIG_DECIMAL = new BigDecimal( -Integer.MAX_VALUE );
public static BigDecimal MAX_INTEGER_VALUE_AS_BIG_DECIMAL = new BigDecimal( Integer.MAX_VALUE );
public SwingNullableSpinner() {
super();
setModel(new SpinnerNumberModelNull());
setValue(null);
}
protected JComponent createEditor(SpinnerModel model) {
return new NumberEditorNull(this);
}
public class SpinnerNumberModelNull extends AbstractSpinnerModel implements Serializable {
private static final long serialVersionUID = -7274426043990492783L;
private Number stepSize, value;
private Comparable minimum, maximum;
public SpinnerNumberModelNull(Number value, Comparable minimum, Comparable maximum, Number stepSize) {
this.value = value;
this.minimum = minimum;
this.maximum = maximum;
this.stepSize = stepSize;
}
public SpinnerNumberModelNull(int value, int minimum, int maximum, int stepSize) {
this(new Integer(value), new Integer(minimum), new Integer(maximum), new Integer(stepSize));
}
public SpinnerNumberModelNull(double value, double minimum, double maximum, double stepSize) {
this(new BigDecimal(value), new BigDecimal(minimum), new BigDecimal(maximum), new BigDecimal(stepSize));
}
public SpinnerNumberModelNull() {
this(new BigDecimal(0), null, null, new BigDecimal(1.00));
}
public void setMinimum(Comparable minimum) {
if ((minimum == null) ? (this.minimum != null) : !minimum.equals(this.minimum)) {
this.minimum = minimum;
fireStateChanged();
}
}
public Comparable getMinimum() {
return minimum;
}
public void setMaximum(Comparable maximum) {
if ((maximum == null) ? (this.maximum != null) : !maximum.equals(this.maximum)) {
this.maximum = maximum;
fireStateChanged();
}
}
public Comparable getMaximum() {
return maximum;
}
public void setStepSize(Number stepSize) {
if (stepSize == null) {
throw new IllegalArgumentException("null stepSize");
}
if (!stepSize.equals(this.stepSize)) {
this.stepSize = stepSize;
fireStateChanged();
}
}
public Number getStepSize() {
return stepSize;
}
private Number incrValue(int dir) {
Number newValue;
if ((value instanceof Long) || (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte)) {
if (value == null) value = 0;
long v = value.longValue() + (stepSize.longValue() * (long) dir);
if (value instanceof Long) {
newValue = new Long(v);
}
else if (value instanceof Integer) {
newValue = new Integer((int) v);
}
else if (value instanceof Short) {
newValue = new Short((short) v);
}
else {
newValue = new Byte((byte) v);
}
}
else {
if (value instanceof Float) {
if (value == null) value = 0.;
double v = value.doubleValue() + (stepSize.doubleValue() * (double) dir);
newValue = new Float(v);
}
else if (value instanceof Double) {
if (value == null) value = 0.;
double v = value.doubleValue() + (stepSize.doubleValue() * (double) dir);
newValue = new Double(v);
}
else {
if (value == null) value = 0.;
double v = value.doubleValue() + (stepSize.doubleValue() * (double) dir);
newValue = new BigDecimal(v);
}
}
if ((maximum != null) && (maximum.compareTo(newValue) < 0.)) {
return null;
}
if ((minimum != null) && (minimum.compareTo(newValue) > 0.)) {
return null;
}
else {
return newValue;
}
}
public Object getNextValue() {
return incrValue(+1);
}
public Object getPreviousValue() {
return incrValue(-1);
}
public Number getNumber() {
return value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
if ((this.value != null) && (value == null)){
this.value = null;
fireStateChanged();
}
else if ((value != null) && !value.equals(this.value)) {
if (value instanceof Number) {
this.value = (Number)value;
}
fireStateChanged();
}
}
}
public static class NumberEditorNull extends DefaultEditor {
private static final long serialVersionUID = 4927264073154690869L;
private static DecimalFormat getDefaultPattern(Locale locale) {
return (DecimalFormat) NumberFormat.getNumberInstance(locale);
}
public NumberEditorNull(JSpinner spinner) {
this(spinner, getDefaultPattern(spinner.getLocale()));
}
public NumberEditorNull(JSpinner spinner, String decimalFormatPattern) {
// this(spinner, new DecimalFormat(decimalFormatPattern));
this(spinner, new DecimalFormat("0.######"));
}
private NumberEditorNull(JSpinner spinner, DecimalFormat format) {
super(spinner);
if (!(spinner.getModel() instanceof SpinnerNumberModelNull)) {
return;
}
SpinnerNumberModelNull model = (SpinnerNumberModelNull) spinner.getModel();
NumberFormatter formatter = new NumberEditorFormatterNull(model, format);
DefaultFormatterFactory factory = new DefaultFormatterFactory(formatter);
JFormattedTextField ftf = getTextField();
ftf.setEditable(true);
ftf.setFormatterFactory(factory);
ftf.setHorizontalAlignment(JTextField.RIGHT);
try {
String maxString = formatter.valueToString(model.getMinimum());
String minString = formatter.valueToString(model.getMaximum());
ftf.setColumns(Math.max(maxString.length(), minString.length()));
}
catch (ParseException e) {
// TBD should throw a chained error here
}
}
public DecimalFormat getFormat() {
return (DecimalFormat) ((NumberFormatter) (getTextField().getFormatter())).getFormat();
}
public SpinnerNumberModel getModel() {
return (SpinnerNumberModel) (getSpinner().getModel());
}
}
private static class NumberEditorFormatterNull extends NumberFormatter {
private static final long serialVersionUID = 4731911867350591824L;
private final SpinnerNumberModelNull model;
NumberEditorFormatterNull(SpinnerNumberModelNull model, NumberFormat format) {
super(format);
this.model = model;
setValueClass(model.getValue().getClass());
setCommitsOnValidEdit(true);
}
public void setMinimum(Comparable min) {
model.setMinimum(min);
}
public Comparable getMinimum() {
return model.getMinimum();
}
public void setMaximum(Comparable max) {
model.setMaximum(max);
}
public Comparable getMaximum() {
return model.getMaximum();
}
/* (non-Javadoc)
* @see javax.swing.text.InternationalFormatter#valueToString(java.lang.Object)
*/
@Override
public String valueToString(Object value) throws ParseException {
if (value == null) {
getFormattedTextField().setBackground(Color.white);
return "";
}
String retVal = null;
try {
retVal = super.valueToString(value);
getFormattedTextField().setBackground(Color.white);
}
catch(ParseException ex){
getFormattedTextField().setBackground(Color.red);
throw ex;
}
return retVal;
}
/* (non-Javadoc)
* @see javax.swing.text.InternationalFormatter#stringToValue(java.lang.String)
*/
@Override
public Object stringToValue(String text) throws ParseException {
if ("".equals(text)) {
getFormattedTextField().setBackground(Color.white);
return null;
}
Object retVal = null;
try {
retVal = super.stringToValue(text);
try {
Double.parseDouble(text);
if (!text.contains("f") && !text.contains("F") && !text.contains("d") && !text.contains("D")) {
getFormattedTextField().setBackground(Color.white);
}
else {
throw new NumberFormatException();
}
}
catch(NumberFormatException exe){
getFormattedTextField().setBackground(Color.red);
}
}
catch(ParseException ex){
getFormattedTextField().setBackground(Color.red);
throw ex;
}
return retVal;
}
}
}