/* * $Id$ * * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ package org.jdesktop.swingx.table; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.math.BigDecimal; import java.math.BigInteger; import java.text.Format; import java.text.NumberFormat; import java.text.ParseException; import java.util.Date; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Box; import javax.swing.InputVerifier; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.KeyStroke; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; import javax.swing.text.NumberFormatter; import org.jdesktop.swingx.InteractiveTestCase; import org.jdesktop.swingx.text.StrictNumberFormatter; import org.jdesktop.swingx.JXFrame; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.JXTable.NumberEditor; import org.jdesktop.test.CellEditorReport; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static org.junit.Assert.*; /** * TODO add type doc * * @author Jeanette Winzenburg */ @RunWith(JUnit4.class) public class NumberEditorExtTest extends InteractiveTestCase { private static final String TOO_BIG_INTEGER = "11111111111111111111111111"; @SuppressWarnings("unused") private static final Logger LOG = Logger .getLogger(NumberEditorExtTest.class.getName()); public static void main(String[] args) { NumberEditorExtTest test = new NumberEditorExtTest(); try { test.runInteractiveTests(); // test.runInteractiveTests("interactive.*AllTypes.*"); } catch (Exception e) { e.printStackTrace(); } } private static final int INTEGER_COLUMN = 0; private static final int NUMBER_COLUMN = 1; private static final int OBJECT_COLUMN = 2; private static final int COLUMN_COUNT = OBJECT_COLUMN + 1; /** a table with a model which has column class Integer in INTEGER_COLUMN and * column class Number in NUMBER_COLUMN. */ private JXTable table; /** a NumberEditorExt configured with IntegerFormat. */ private NumberEditorExt cellEditor; /** a NumberEditorExt configured with strict IntegerFormat. */ private NumberEditorExt cellEditorStrict; /** * Issue #1293-swingx: NumberEditorExt removes Escape binding for all formatted text fields. */ @Test public void testFormattedTextFieldHasEscapeBinding() { JFormattedTextField field = new JFormattedTextField(new Date()); KeyStroke keyStroke = KeyStroke.getKeyStroke("ESCAPE"); assertNotNull("text field must have ESCAPE binding", field.getInputMap().get(keyStroke )); } /** * Issue #1236-swingx: NumberEditor throws for non-number columns. * * Strict editor delegates to formatter - which defaults to string valueclass. */ @Test public void testStrictIsDefault() { TableCellEditor editor = table.getDefaultEditor(Number.class); assertTrue("sanity: expect NumberEditorExt but was " + editor, editor instanceof NumberEditorExt); assertEquals("default formatter is strict", true, ((NumberEditorExt) editor).hasStrictFormatter()); } /** * Issue #1236-swingx: NumberEditor throws for non-number columns. * * Strict editor delegates to formatter - which defaults to string valueclass. */ @Test public void testEditorStrictObjectColumn() { cellEditorStrict.getTableCellEditorComponent(table, null, false, 0, OBJECT_COLUMN); } /** * Issue #1236-swingx: NumberEditor throws for non-number columns. * * Old default: non-strict editor throws IllegalState if column type * is not a subtype of Number. */ @Test (expected = IllegalStateException.class) public void testEditorObjectColumn() { cellEditor.getTableCellEditorComponent(table, null, false, 0, OBJECT_COLUMN); } //---------------- Sanity testing: StrictNumberFormatter - /** * Issue #1528-swingx: numbereditor doesn't allow negative values. * * Sanity: positive okay * @throws ParseException */ @Test public void testFloatStringToValuePositive() throws ParseException { NumberFormat format = NumberFormat.getInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Float.class); float value = 3.4f; String text = format.format(value); assertEquals("string: " + text, value, formatter.stringToValue(text)); } /** * Issue #1528-swingx: numbereditor doesn't allow negative values. * * Test zero * * @throws ParseException */ @Test public void testFloatStringToValueZero() throws ParseException { NumberFormat format = NumberFormat.getInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Float.class); float value = 0f; String text = format.format(value); assertEquals("string: " + text, value, formatter.stringToValue(text)); } /** * Issue #1528-swingx: numbereditor doesn't allow negative values. * * Test negative * * @throws ParseException */ @Test public void testFloatStringToValueNegative() throws ParseException { NumberFormat format = NumberFormat.getInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Float.class); float value = -3.4f; String text = format.format(value); assertEquals("string: " + text, value, formatter.stringToValue(text)); } @Test (expected = ParseException.class) public void testNumberFormatter() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new NumberFormatter(format); formatter.setMaximum(Integer.MAX_VALUE - 1); formatter.setMinimum(Integer.MIN_VALUE + 1); formatter.stringToValue(TOO_BIG_INTEGER); } @Test (expected = ParseException.class) public void testStrictNumberFormatterMinMax() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setMaximum(Integer.MAX_VALUE); formatter.setMinimum(Integer.MIN_VALUE); formatter.stringToValue(TOO_BIG_INTEGER); } @Test (expected = ParseException.class) public void testStrictNumberFormatterAutoRangeInteger() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Integer.class); formatter.stringToValue(TOO_BIG_INTEGER); } @Test (expected = ParseException.class) public void testStrictNumberFormatterAutoRangeLong() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Long.class); String text = new Long(Long.MAX_VALUE).toString() + "9"; formatter.stringToValue(text); } @Test (expected = ParseException.class) public void testStrictNumberFormatterAutoRangeFloat() throws ParseException { NumberFormat format = NumberFormat.getInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Float.class); String text = "9" + new Float(Float.MAX_VALUE).toString(); formatter.stringToValue(text); } @Test (expected = ParseException.class) public void testStrictNumberFormatterAutoRangeFloatMin() throws ParseException { NumberFormat format = NumberFormat.getInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Float.class); String text = "-9" + new Float(Float.MAX_VALUE).toString(); formatter.stringToValue(text); } @Test (expected = ParseException.class) public void testStrictNumberFormatterAutoRangeByte() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Byte.class); formatter.stringToValue(TOO_BIG_INTEGER); } @Test (expected = ParseException.class) public void testStrictNumberFormatterAutoRangeShort() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Short.class); formatter.stringToValue(TOO_BIG_INTEGER); } @Test (expected = ParseException.class) public void testStrictNumberFormatterSmallExceedInteger() throws ParseException { NumberFormat format = NumberFormat.getIntegerInstance(); NumberFormatter formatter = new StrictNumberFormatter(format); formatter.setValueClass(Integer.class); String text = new Integer(Integer.MAX_VALUE).toString() + "1"; formatter.stringToValue(text); } //---------- end sanity testing: StrictNumberFormatter /** * Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if * Integer (short, byte..) below/above min/max. * * Sanity: checks that stopCellEditing returns false on parsing errors. */ @Test public void testEditorValueParsing() { JFormattedTextField field = (JFormattedTextField) cellEditor .getTableCellEditorComponent(table, 100, false, 0, INTEGER_COLUMN); // add valid digit field.setText(field.getText() + "9"); assertTrue("valid input " + field.getText(), cellEditor.stopCellEditing()); // add invalid character field.setText(field.getText() + "x"); assertFalse("invalid input " + field.getText(), cellEditor.stopCellEditing()); } /** * Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if * Integer (short, byte..) below/above min/max. * * Sanity: checks that stopCellEditing returns false on parsing errors, use strict. */ @Test public void testEditorStrictValueParsing() { JFormattedTextField field = (JFormattedTextField) cellEditorStrict .getTableCellEditorComponent(table, 100, false, 0, INTEGER_COLUMN); // add valid digit field.setText(field.getText() + "9"); assertTrue("valid input " + field.getText(), cellEditorStrict.stopCellEditing()); // add invalid character field.setText(field.getText() + "x"); assertFalse("invalid input " + field.getText(), cellEditorStrict.stopCellEditing()); } /** * Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if * Integer (short, byte..) below/above min/max. * * Check that stopCellEditing returns false if bounds exceeded. */ @Test public void testEditorValueExceedBounds() { JFormattedTextField field = (JFormattedTextField) cellEditor .getTableCellEditorComponent(table, Integer.MAX_VALUE, false, 0, INTEGER_COLUMN); // add valid digit - but exceeds Integer bounds so must not return true field.setText(field.getText() + "9"); assertFalse("valid input but exceeds bounds " + field.getText(), cellEditor.stopCellEditing()); } /** * Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if * Integer (short, byte..) below/above min/max. * * Check that stopCellEditing returns false if bounds exceeded, use strict */ @Test public void testEditorStrictValueExceedBounds() { JFormattedTextField field = (JFormattedTextField) cellEditorStrict .getTableCellEditorComponent(table, Integer.MAX_VALUE, false, 0, INTEGER_COLUMN); // add valid digit - but exceeds Integer bounds so must not return true field.setText(field.getText() + "9"); assertFalse("valid input but exceeds bounds " + field.getText(), cellEditorStrict.stopCellEditing()); } /** * Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if * Integer (short, byte..) below/above min/max. * * Check IllegalStateException as doc'ed */ @Test(expected = IllegalStateException.class) public void testEditorValueIllegalState() { JFormattedTextField field = (JFormattedTextField) cellEditor .getTableCellEditorComponent(table, Integer.MAX_VALUE, false, 0, INTEGER_COLUMN); // add valid digit - but exceeds Integer bounds so must not return true field.setText(field.getText() + "9"); cellEditor.getCellEditorValue(); } /** * Issue ??-swingx: editor with strict number formatter throws on * committing null value. * * happens only if active editor in table, so this test fails */ @Test public void testEditorStrictNullValue() { table.getColumn(INTEGER_COLUMN).setCellEditor(cellEditorStrict); table.editCellAt(0, INTEGER_COLUMN); cellEditorStrict.stopCellEditing(); } /** * Issue ??-swingx: editor with strict number formatter throws on * committing null value. * */ @Test public void testEditorStrictNullValueStandAlone() { cellEditorStrict .getTableCellEditorComponent(table, null, false, 0, INTEGER_COLUMN); cellEditorStrict.getCellEditorValue(); } //----------------- interactive /** * Issue #1293-swingx: NumberEditorExt removes Escape binding for all formatted text fields. * * Here we assure that the fix for the issue doesn't effect the table edit - must * pass-through to allow correct cancel processing (non-strict number editor). */ public void interactiveEscapeNonStrict() { JXTable table = new JXTable(this.table.getModel()); table.setDefaultEditor(Number.class, new NumberEditorExt(false)); JXFrame frame = wrapWithScrollingInFrame(table, "non-strict: escape must cancel edit"); addStatusMessage(frame, "edit number column, press escape, verify editingCanceled"); final CellEditorReport report = new CellEditorReport(); table.getDefaultEditor(Number.class).addCellEditorListener(report); Action verify = new AbstractAction("verify cancelled") { @Override public void actionPerformed(ActionEvent e) { int cancelledCount = report.getCanceledEventCount(); report.clear(); LOG.info("cancelled event count must be > 0, was: " + cancelledCount); } }; addAction(frame, verify); show(frame); } /** * Issue #1293-swingx: NumberEditorExt removes Escape binding for all formatted text fields. * * Here we assure that the fix for the issue doesn't effect the table edit - must * pass-through to allow correct cancel processing (strict number editor). */ public void interactiveEscapeStrict() { JXTable table = new JXTable(this.table.getModel()); JXFrame frame = wrapWithScrollingInFrame(table, "strict: escape must cancel edit"); addStatusMessage(frame, "edit number column, press escape, verify editingCanceled"); final CellEditorReport report = new CellEditorReport(); table.getDefaultEditor(Number.class).addCellEditorListener(report); Action verify = new AbstractAction("verify cancelled") { @Override public void actionPerformed(ActionEvent e) { int cancelledCount = report.getCanceledEventCount(); report.clear(); LOG.info("cancelled event count must be > 0, was: " + cancelledCount); } }; addAction(frame, verify); show(frame); } /** * Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if * Integer (short, byte..) below/above min/max. * * Visualize JFormattedText behaviour. * * Problem is that the formattedTextField.isvalid/isValidEdit only check for * formal validity. So the assumption that at the time of constructor * invokation later the value is valid is incorrect. */ public void interactiveNumberOutOfBounds() { StrictNumberFormatter formatter = new StrictNumberFormatter(NumberFormat.getIntegerInstance()); formatter.setMaximum(Integer.MAX_VALUE); formatter.setMinimum(Integer.MIN_VALUE); final JFormattedTextField field = new JFormattedTextField( formatter) { @Override protected void invalidEdit() { LOG.info("invalid .... " + getText()); } }; final JCheckBox validBox = new JCheckBox("valid"); final JCheckBox editValid = new JCheckBox("edit valid"); DocumentListener listener = new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { LOG.info("insert " + e); changedUpdate(e); } @Override public void removeUpdate(DocumentEvent e) { // TODO Auto-generated method stub LOG.info("remove " + e); changedUpdate(e); } @Override public void changedUpdate(DocumentEvent e) { validBox.setSelected(field.isValid()); editValid.setSelected(field.isEditValid()); try { field.commitEdit(); } catch (ParseException e1) { // TODO Auto-generated catch block // e1.printStackTrace(); } LOG.info("current value " + field.getValue()); } }; field.getDocument().addDocumentListener(listener); field.setText(TOO_BIG_INTEGER); JComponent content = Box.createVerticalBox(); content.add(field); content.add(validBox); content.add(editValid); JXFrame frame = showInFrame(content, "valid states of formatted text"); show(frame, 300, 200); } public void interactiveNumberInputVerifier() { final JFormattedTextField field = new JFormattedTextField(NumberFormat.getIntegerInstance()) { @Override protected void invalidEdit() { LOG.info("got invalid edit"); super.invalidEdit(); } }; field.setText("20"); try { field.commitEdit(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } InputVerifier verifier = new InputVerifier() { @Override public boolean verify(JComponent input) { boolean result = ((Number) field.getValue()).intValue() < 200; if (!result) LOG.info("in verifier"); return result; } }; field.setInputVerifier(verifier); final JFormattedTextField other = new JFormattedTextField(NumberFormat.getIntegerInstance()); JComponent content = Box.createVerticalBox(); content.add(field); content.add(other); showInFrame(content, ""); } /** * Issue ??-swingx: localized NumberEditor using strict. * Default for JXTable. * * Here: all column classes are Number. * */ public void interactiveStrictNumberEditorAllTypesAsNumber() { @SuppressWarnings("unused") final Class<?>[] classes = new Class[] {Byte.class, Short.class, Integer.class, Float.class, Double.class, BigInteger.class, BigDecimal.class, Number.class}; DefaultTableModel model = new DefaultTableModel(new String[] { "Byte", "Short", "Integer", "Float", "Double", "BigInteger", "BigDecimal", "Number"}, 10) { @Override public Class<?> getColumnClass(int columnIndex) { return Number.class; } }; // positive/max model.setValueAt(Byte.MAX_VALUE, 0, 0); model.setValueAt(Short.MAX_VALUE, 0, 1); model.setValueAt(Integer.MAX_VALUE, 0, 2); model.setValueAt(Float.MAX_VALUE, 0, 3); model.setValueAt(Double.MAX_VALUE, 0, 4); model.setValueAt(new BigInteger(TOO_BIG_INTEGER), 0, 5); model.setValueAt(new BigDecimal("44444444444444.666666666666666666"), 0, 6); // negative/min model.setValueAt(Byte.MIN_VALUE, 1, 0); model.setValueAt(Short.MIN_VALUE, 1, 1); model.setValueAt(Integer.MIN_VALUE, 1, 2); model.setValueAt(-Float.MAX_VALUE, 1, 3); model.setValueAt(-Double.MAX_VALUE, 1, 4); // model.setValueAt(new BigInteger(TOO_BIG_INTEGER), 1, 5); model.setValueAt(new BigDecimal("-44444444444444.666666666666666666"), 1, 6); final JXTable table = new JXTable(model); table.setSurrendersFocusOnKeystroke(true); JXFrame frame = showWithScrollingInFrame(table, "Extended NumberEditors: number class"); addStatusMessage(frame, "number class: no error, commit any number"); } /** * Issue ??-swingx: localized NumberEditor not using strict. * * Column class is concrete type. * */ public void interactiveNonStrictNumberEditorAllTypes() { final Class<?>[] classes = new Class[] {Byte.class, Short.class, Integer.class, Float.class, Double.class, BigInteger.class, BigDecimal.class, Number.class}; DefaultTableModel model = new DefaultTableModel(new String[] { "Byte", "Short", "Integer", "Float", "Double", "BigInteger", "BigDecimal", "Number"}, 10) { @Override public Class<?> getColumnClass(int columnIndex) { return classes[columnIndex]; } }; model.setValueAt(Byte.MAX_VALUE, 0, 0); model.setValueAt(Short.MAX_VALUE, 0, 1); model.setValueAt(Integer.MAX_VALUE, 0, 2); model.setValueAt(Float.MAX_VALUE, 0, 3); model.setValueAt(Double.MAX_VALUE, 0, 4); model.setValueAt(new BigInteger(TOO_BIG_INTEGER), 0, 5); model.setValueAt(new BigDecimal("44444444444444.666666666666666666"), 0, 6); // negative/min model.setValueAt(Byte.MIN_VALUE, 1, 0); model.setValueAt(Short.MIN_VALUE, 1, 1); model.setValueAt(Integer.MIN_VALUE, 1, 2); model.setValueAt(-Float.MAX_VALUE, 1, 3); model.setValueAt(-Double.MAX_VALUE, 1, 4); // model.setValueAt(new BigInteger(TOO_BIG_INTEGER), 1, 5); model.setValueAt(new BigDecimal("-44444444444444.666666666666666666"), 1, 6); final JXTable table = new JXTable(model); table.setSurrendersFocusOnKeystroke(true); NumberEditorExt strictEditor = new NumberEditorExt(false); table.setDefaultEditor(Number.class, strictEditor); JXFrame frame = showWithScrollingInFrame(table, "Extended NumberEditors(non-strict): concrete Number classes"); addStatusMessage(frame, "concrete number classes and non-strict formatter: error not shown, fails on commit"); } /** * Issue ??-swingx: localized NumberEditor using strict. * * Column class is concrete type. * */ public void interactiveStrictNumberEditorAllTypes() { final Class<?>[] classes = new Class[] {Byte.class, Short.class, Integer.class, Float.class, Double.class, BigInteger.class, BigDecimal.class, Number.class}; DefaultTableModel model = new DefaultTableModel(new String[] { "Byte", "Short", "Integer", "Float", "Double", "BigInteger", "BigDecimal", "Number"}, 10) { @Override public Class<?> getColumnClass(int columnIndex) { return classes[columnIndex]; } }; model.setValueAt(Byte.MAX_VALUE, 0, 0); model.setValueAt(Short.MAX_VALUE, 0, 1); model.setValueAt(Integer.MAX_VALUE, 0, 2); model.setValueAt(Float.MAX_VALUE, 0, 3); model.setValueAt(Double.MAX_VALUE, 0, 4); model.setValueAt(new BigInteger(TOO_BIG_INTEGER), 0, 5); model.setValueAt(new BigDecimal("44444444444444.666666666666666666"), 0, 6); // negative/min model.setValueAt(Byte.MIN_VALUE, 1, 0); model.setValueAt(Short.MIN_VALUE, 1, 1); model.setValueAt(Integer.MIN_VALUE, 1, 2); model.setValueAt(-Float.MAX_VALUE, 1, 3); model.setValueAt(-Double.MAX_VALUE, 1, 4); // model.setValueAt(new BigInteger(TOO_BIG_INTEGER), 1, 5); model.setValueAt(new BigDecimal("-44444444444444.666666666666666666"), 1, 6); final JXTable table = new JXTable(model); table.setSurrendersFocusOnKeystroke(true); JXFrame frame = showWithScrollingInFrame(table, "Extended NumberEditors(strict): concrete Number classes"); addStatusMessage(frame, "concrete number classes and strict formatter: error shown on typing"); } /** * Issue #393-swingx: localized NumberEditor. * * Column type is Number, editors as noted in header text * * */ public void interactiveNumberEditor() { final int doubleColumns = 3; final int integerColumns = 6; DefaultTableModel model = new DefaultTableModel(new String[] { "Double-core", "Double-extstrict", "Double-extnonstrict", "Integer-core", "Integer-extstrict", "Integer-extnonstrict", "Object" }, 10) { @Override public Class<?> getColumnClass(int columnIndex) { return Number.class; } }; final JXTable table = new JXTable(model); table.setSurrendersFocusOnKeystroke(true); for (int i = 0; i < table.getColumnCount(); i++) { if (i < doubleColumns) { table.setValueAt(Double.MAX_VALUE-1, 0, i); } else { table.setValueAt(Integer.MAX_VALUE-1, 0, i); } } NumberEditor numberEditor = new NumberEditor(); table.getColumn(0).setCellEditor(numberEditor); table.getColumn(doubleColumns).setCellEditor(numberEditor); NumberEditorExt nonStrictEditor = new NumberEditorExt(false); table.getColumn(doubleColumns -1).setCellEditor(nonStrictEditor); table.getColumn(integerColumns -1).setCellEditor(nonStrictEditor); JXFrame frame = showWithScrollingInFrame(table, "Extended NumberEditors: Number.class"); addStatusMessage(frame, "number-class: has no constructor with string, core/nonstrict can't handle"); } /** * Issue #393-swingx: localized NumberEditor. * * Column type is Number, editors as noted in header text * * */ public void interactiveFloatingPointEditor() { final int doubleColumns = 3; final int integerColumns = 6; DefaultTableModel model = new DefaultTableModel(new String[] { "Double-core", "Double-extstrict", "Double-extnonstrict", "Integer-core", "Integer-extstrict", "Integer-extnonstrict", "Object" }, 10) { @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex < doubleColumns) { return Double.class; } if (columnIndex < integerColumns) { return Integer.class; } return Object.class; } }; final JXTable table = new JXTable(model); table.setSurrendersFocusOnKeystroke(true); for (int i = 0; i < table.getColumnCount(); i++) { if (i < doubleColumns) { table.setValueAt(Double.MAX_VALUE, 0, i); } else { table.setValueAt(Integer.MAX_VALUE, 0, i); } } NumberEditor numberEditor = new NumberEditor(); table.getColumn(0).setCellEditor(numberEditor); table.getColumn(doubleColumns).setCellEditor(numberEditor); NumberEditorExt nonStrictEditor = new NumberEditorExt(false); table.getColumn(doubleColumns -1).setCellEditor(nonStrictEditor); table.getColumn(integerColumns -1).setCellEditor(nonStrictEditor); showWithScrollingInFrame(table, "Extended NumberEditors: concrete classes Double/Integer"); } /** * Issue #??-swingx: default number editor shows 3 digits only. * * Compare with plain JFromattedTextField and default NumberFormat - same. * To see, type a number with fractional digits > 3 in the first text field * and press commit or transfer focus away. */ public void interactiveFloatingPointEditorDigits(){ final int doubleColumns = 3; DefaultTableModel model = new DefaultTableModel( new String[] {"Double-defaultstrict", "Double-customMaxDigits-nonstrict", "Double-customMaxDigitsstrict"}, 10) { @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex < doubleColumns) { return Double.class; } return Integer.class; } }; final JXTable table = new JXTable(model); table.setSurrendersFocusOnKeystroke(true); for (int i = 0; i < doubleColumns; i++) { table.setValueAt(10.123456789, 0, i); table.setValueAt(10, 1, i); } NumberFormat moreFractionalDigits = NumberFormat.getInstance(); moreFractionalDigits.setMaximumFractionDigits(20); NumberEditorExt numberEditor = new NumberEditorExt(moreFractionalDigits); table.getColumn(1).setCellEditor(numberEditor); table.getColumn(2).setCellEditor(new NumberEditorExt(moreFractionalDigits, true)); JXFrame frame = showWithScrollingInFrame(table, "Extended NumberEditors: Doubles"); Format format = NumberFormat.getInstance(); final JFormattedTextField field = new JFormattedTextField(format); field.setColumns(10); final JFormattedTextField target = new JFormattedTextField(moreFractionalDigits); target.setColumns(10); field.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { target.setValue(field.getValue()); LOG.info("value: " + field.getValue()); } }); FocusAdapter focusAdapter = new FocusAdapter() { @Override public void focusLost(FocusEvent e) { LOG.info("field value: " + field.getValue()); LOG.info("table value: " + table.getValueAt(0, 1)); } }; field.addFocusListener(focusAdapter); table.addFocusListener(focusAdapter); addStatusComponent(frame, field); addStatusComponent(frame, target); } @Before @Override public void setUp() throws Exception { DefaultTableModel model = new DefaultTableModel(5, COLUMN_COUNT) { @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == INTEGER_COLUMN) return Integer.class; if (columnIndex == NUMBER_COLUMN) return Number.class; return super.getColumnClass(columnIndex); } }; table = new JXTable(model); cellEditor = new NumberEditorExt(NumberFormat.getIntegerInstance()); cellEditorStrict = new NumberEditorExt(NumberFormat.getIntegerInstance(), true); } }