проблема форматирования поля в JTable - различия между Integer и Double

Обновление

Подтверждено как ошибка на JTable не может отформатировать данный объект как Number, когда columnClass является Double (идентификатор ошибки: 7051636) . Не стесняйтесь голосовать за него, или, если у вас есть альтернативный (лучший) обходной путь, опубликуйте его в качестве комментария к отчету.


Я создаю JTable с пользовательской табличной моделью, построенной как расширение AbstractTableModel. Моя модель должна поддерживать пустую строку для отображения и сортировки. Поэтому я следил за этим сообщением , чтобы реализовать это, и теперь работает довольно хорошо.

У меня все еще есть проблема с форматированным полем в JTable. Допустим, у меня такая модель:

public class MyModel extends AbstractTableModel{

    public Object[] types= {new Integer(0), ""};
    public static final Object EMPTY_ROW = "";

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
         return this.EMPTY_ROW;
    }
    public Class getColumnClass(int c) {
      if (c > this.types.length - 1)
        return null;
      else
        return this.types[c].getClass();

    }
}

Все работает нормально. Но если у меня есть Double вместо Integer:

public class MyModel extends AbstractTableModel{

        public Object[] types= {new Double(0.0), ""};
  .......

, я получу исключение Illegal Argument:

EDIT: новый вывод трассировки стека после предложения @Aaron Digulla

Исключение в потоке «AWT-EventQueue-0» java.lang.IllegalArgumentException: невозможно отформатировать данный объект как число в java.text.DecimalFormat.format (DecimalFormat.java:487) в java.text.Format.format (Format.java:140) в javax.swing.JTable $ DoubleRenderer.setValue (JTable.java:5352) в javax.swing.table.DefaultTableCellRenderer.getTableCellRendererComponent (DefaultTableCellRenderer.java:237) в javax.swing.JTable.prepareRenderer (JTable.java:5720) в javax.swing.plaf.basic.BasicTableUI.paintCell (BasicTableUI.java:2072) в javax.swing.plaf.basic.BasicTableUI.paintCells (BasicTableUI.java:1974) в javax.swing.plaf.basic.BasicTableUI.paint (BasicTableUI.java:1770) в javax.swing.plaf.ComponentUI.update (ComponentUI.java:143) в javax.swing.JComponent.paintComponent (JComponent.java:752) в javax.swing.JComponent.paint (JComponent.java:1029) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JViewport.paint (JViewport.java:747) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JLayeredPane.paint (JLayeredPane.java:567) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paintToOffscreen (JComponent.java:5131) в javax.swing.BufferStrategyPaintManager.paint (BufferStrategyPaintManager.java:278) в javax.swing.RepaintManager.paint (RepaintManager.java:1224) в javax.swing.JComponent.paint (JComponent.java:1015) в java.awt.GraphicsCallback $ PaintCallback.run (GraphicsCallback.java:21) в sun.awt.SunGraphicsCallback.runOneComponent (SunGraphicsCallback.java:60) в sun.awt.SunGraphicsCallback.runComponents (SunGraphicsCallback.java:97) в java.awt.Container.paint (Container.java:1780) в java.awt.Window.paint (Window.java:3375) в javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:796) в javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:713) в javax.swing.RepaintManager.seqPaintDirtyRegions (RepaintManager.java:693) в javax.swing.SystemEventQueueUtilities $ ComponentWorkRequest.run (SystemEventQueueUtilities.java:125) в java.awt.event.InvocationEvent.dispatch (InvocationEvent.java:209) в java.awt.EventQueue.dispatchEvent (EventQueue.java:597) в java.awt.EventDispatchThread.pumpOneEventForFilters (EventDispatchThread.java:269) в java.awt.EventDispatchThread.pumpEventsForFilter (EventDispatchThread.java:184) в java.awt.EventDispatchThread.pumpEventsForHierarchy (EventDispatchThread.java:174) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:169) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:161) at java.awt.EventDispatchThread.run (EventDispatchThread.java:122)

Почему это?

getValueAt всегда возвращает одно и то же значение, чтобы заполнить им все записи таблиц. Это только для отладки:

@Override
    public Object getValueAt(int rowIndex, int columnIndex) {
         return this.EMPTY_ROW;
    }

Например, если я перейду на:

 @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
         return new Integer(3);
         //or return new Double(3.3);
         //return new String("foobar"); doesn't work
    }

, все будет работать нормально, даже если какое-то поле таблицы является String. Мне это подсказывает, потому что Integer и Double можно преобразовать в String, это не вызовет проблем. В любом случае, я хотел бы понять, почему общий объект, такой как мой EMPTY_ROW, может быть принят как значение объявленного поля Integer, в то время как это не работает с полями Double.

EDIT2:

Если я удалю метод getClass в моей модели таблицы . Оно работает. В любом случае я хотел бы решить эту проблему, не удаляя этот метод, даже если это заставит меня реализовать некоторые специальные методы рендеринга.

EDIT3:

вот SSCCE. При добавлении новых значений в таблицу есть некоторые ошибки, но они не имеют ничего общего с проблемами рендеринга.

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SortOrder;
import javax.swing.RowSorter.SortKey;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;


public class TableExample extends JFrame{
    public static final  Object EMPTY_ROW = "";
    public class EmptyRowComparator> implements Comparator{//extends RuleBasedCollator{

        private TableRowSorter sorter;
    private int column;

        public EmptyRowComparator(TableRowSorter sorter, int col) throws ParseException {
        //  super(arg0);
            this.sorter = sorter;
            this.column = col;
            // TODO Auto-generated constructor stub
        }



         private int getSortOrder() {
             SortOrder order = SortOrder.ASCENDING;
//           List keys = sorter.getSortKeys();
//           sorter.getSortKeys();
//       

             for (SortKey sortKey : sorter.getSortKeys()) {
                 if (sortKey.getColumn() == this.column) {
                     order = sortKey.getSortOrder();
                     break;
                 }
             }
             return order == SortOrder.ASCENDING ? 1 : -1;
         }



        @Override
        public int hashCode() {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public int compare(Object arg0, Object arg1) {
            // TODO Auto-generated method stub
            //System.out.println("Comparing Integer arg0 " + arg0 + " arg1 " + arg1);
            boolean empty1 = arg0 == EMPTY_ROW;
            boolean empty2 = arg1 == EMPTY_ROW;
            if (empty1 && empty2) {
                return 0;
            }
            else if (empty1) {
                return 1 * getSortOrder();
            }
            else if (empty2) {
                return -1 * getSortOrder();
            }
            return ((Comparable) (COLUMN_TYPE)arg0).compareTo((COLUMN_TYPE)arg1);
        //  return 0;
        }

    }

    public class ConcreteTable extends AbstractTableMod{

        //
        private static final long serialVersionUID = 4672561280810649603L;
        private String[] columnNames = {"ID",
                                        "description"};


        Class[] types = {Integer.class, String.class};
        //Object[] types = {Double.class, String.class};
        private int minimumDisplayedRow;


        public ConcreteTable(){
            //System.out.println("DEBUG ARRAY length " + data.length);
            this.minimumDisplayedRow = 10;
            this.datas = new ArrayList>();
            for (int i = 0 ; i < this.minimumDisplayedRow  ; i++){
                this.addEmptyRow();
            }
            for (int i = 0 ; i < 5 ; i++){
                ArrayList row = new ArrayList();
                row.add(new Integer(i));
                row.add(new String("prova " + i));
                this.addRow(row);
            }

        }


        public String getColumnName(int col) {
            System.out.println("getColumnName " + col + " = " + columnNames[col]);
            return columnNames[col];
        }

        @Override
        protected Class[] getTypeArray() {
            // TODO Auto-generated method stub
            return this.types;
        }

        @Override
        protected ArrayList getKeysColumnIndex() {
            // TODO Auto-generated method stub
            ArrayList keys = new ArrayList();
            keys.add(0);
            return keys;
        }
        public boolean isCellEditable(int row, int col) {
            System.out.println("isCellEditable row " + row + " col " + col);
            if (col == 1){
                System.out.println("TRUE");
                return true;
            }

            return false;
        }
        /*note: generated keys must be in the same order they appear in the table*/
        @Override
        protected Object getGeneratedKeys(int col) {
            // TODO Auto-generated method stub
            if (col != 0 )
                return null;
            return new Integer(this.rowNumber);
        }
        @Override
        protected int getMinimumDisplayedRow() {
            // TODO Auto-generated method stub
            return this.minimumDisplayedRow;
        }


    }

    public abstract class AbstractTableMod extends AbstractTableModel {


        ArrayList> datas ;
        protected int rowNumber = 0;
        protected abstract Class[] getTypeArray();
        protected abstract ArrayList getKeysColumnIndex();
        protected abstract Object getGeneratedKeys(int col);
        protected abstract int getMinimumDisplayedRow();

        public int getRowCount(){
            return this.datas.size() ;
        }
        @Override
        public int getColumnCount() {
            return this.getTypeArray().length;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {

            if (rowIndex >= this.rowNumber ){
                return EMPTY_ROW;
            }

            try{

                ArrayList row = this.datas.get(rowIndex);
                if (row == null)
                    return null;
                Object obj = row.get(columnIndex);
                return obj;
            }catch(IndexOutOfBoundsException e){
                return null;
            }


        }
        public void setValueAt(Object value, int row, int col) {

            //System.out.println("setValueAt object : " + value.getClass().getName());
            Class targetColClass = this.getColumnClass(col);
            if (!targetColClass.isInstance(value))
                return;
            if (value instanceof String){
                String stringVal = (String)value;
                if (stringVal.compareTo("") == 0)
                    return;
            }
            if (row >= this.rowNumber){
                ArrayList newRow = new ArrayList();
                ArrayList keysIndexList = this.getKeysColumnIndex();

                for (int i = 0 ; i < this.getColumnCount(); i++){
                    if (i == col){
                        newRow.add(value);
                    }else if (keysIndexList.contains(i)){
                        newRow.add(this.getGeneratedKeys(i));
                    }else{
                        newRow.add(EMPTY_ROW);
                    }
                }
                this.addRow(newRow);
            }else{
                this.datas.get(row).set(col, value);
            }
            this.fireTableCellUpdated(row, col);

        }
        public Class getColumnClass(int c) {
            System.out.println("AbstractTable: getColumnClass");
            if (c > this.getTypeArray().length - 1)
                return null;
            else
                return this.getTypeArray()[c];
        }

        public void addEmptyRow(){
            ArrayList emptyRow = new ArrayList();
            for (int i = 0 ; i < this.getTypeArray().length; i++){
                emptyRow.add(EMPTY_ROW);
            }
            this.datas.add(emptyRow);
        }
        public void addRow(ArrayList row){
            Object[] types = this.getTypeArray();
            if (types.length != row.size())
                return;
            for (int i = 0 ; i < row.size() ; i++){
                Class targetColClass = this.getColumnClass(i);
                Object rowItem = row.get(i);
            }
            this.datas.add(this.rowNumber, row);
            this.rowNumber++;
            if (this.rowNumber < this.getMinimumDisplayedRow())
                this.datas.remove(this.datas.size() -1 );
            this.fireTableRowsInserted(this.rowNumber , this.rowNumber  );

        }
    }
    public TableExample(){
        super("JTable example");
        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));



        ConcreteTable model = new ConcreteTable();
        JTable tab = new JTable(model);
        TableRowSorter sorter = new TableRowSorter(model);



        try {

            sorter.setComparator(0, new EmptyRowComparator(sorter,0));
            sorter.setComparator(1, new EmptyRowComparator(sorter,1));

        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        tab.setRowSorter(sorter);
        JScrollPane table = new JScrollPane(tab);

        this.getContentPane().add(table);
        this.setSize(600, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            new TableExample();
    }

}

Если вы попытаетесь изменить

Class[] types = {Integer.class, String.class}; 

с помощью:

Class[] types = {Double.class, String.class};

, вы увидите проблему.

5
задан Andrew Thompson 7 June 2011 в 11:18
поделиться