All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.javagimmicks.swing.model.ListTableModel Maven / Gradle / Ivy

package net.sf.javagimmicks.swing.model;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

import net.sf.javagimmicks.beans.BeanUtils;

public class ListTableModel extends AbstractList implements TableModel
{
    private final Class _rowType;
    private final List> _columnTypes;
    private final List _rows;

    private final List _indexToProperty;
    private final Map _propertyToIndex;

    private List _columnNames;

    private final List _listeners;

    public ListTableModel(Class rowType)
    {
        this(null, rowType, null);
    }

    public ListTableModel(Class rowType, List indexToProperty)
    {
        this(null, rowType, indexToProperty);
    }

    public ListTableModel(List rowdata, Class rowType)
    {
        this(rowdata, rowType, null);
    }

    public ListTableModel(List rowdata, Class rowType, List indexToProperty)
    {
        if(indexToProperty == null || indexToProperty.isEmpty())
        {
            indexToProperty = BeanUtils.extractPropertyNames(rowType);
        }
        
        if(rowType == null || !rowType.isInterface())
        {
            throw new IllegalArgumentException("Row type must be an interface!");
        }

        _rowType = rowType;
        _indexToProperty = indexToProperty;
        _rows = (rowdata != null) ? rowdata : new ArrayList();
        _listeners = new LinkedList();

        _columnTypes = new ArrayList>(_indexToProperty.size());
        for(int i = 0; i < _indexToProperty.size(); ++i)
        {
            try
            {
                _columnTypes.add(getGetter(_rowType, i).getReturnType());
            }
            catch (Exception e)
            {
                throw new IllegalArgumentException("No get-method for property '" +
                    _indexToProperty.get(i) + "' or method not accessible!");
            }
        }

        _propertyToIndex = getPropertyToIndexMap(_indexToProperty);
        
        _columnNames = getPropertyNames();
    }
    
    public Class getRowType()
    {
    	return _rowType;
    }
    
    public void add(int index, E element)
    {
        _rows.add(index, element);
        fireRowAdded(index);
    }

    public boolean addAll(int index, Collection c)
    {
        boolean result = _rows.addAll(index, c);

        fireRowsAdded(index, index + c.size() - 1);

        return result;
    }
    
	public void clear()
	{
		int size = _rows.size();
		
		_rows.clear();
		
		fireRowsRemoved(0, size - 1);
	}

    @SuppressWarnings("unchecked")
    public E get(int index)
    {
        RowInvocationHandler invocationHandler = new RowInvocationHandler(_rows.get(index));
        return (E) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {_rowType}, invocationHandler);
    }

    public E remove(int index)
    {
        E result = _rows.remove(index);

        fireRowRemoved(index);

        return result;
    }

    @SuppressWarnings("unchecked")
    public E set(int index, E element)
    {
        if(Proxy.isProxyClass(element.getClass()))
        {
           InvocationHandler handler = Proxy.getInvocationHandler(element);
           
           if(handler instanceof ListTableModel.RowInvocationHandler)
           {
              element = ((RowInvocationHandler)handler)._row;
           }
        }
       
        E result = _rows.set(index, element);

        fireRowChanged(index);

        return result;
    }

    public int size()
    {
        return _rows.size();
    }

    public boolean isCellEditable(int rowIndex, int columnIndex)
    {
        return true;
    }

    public int getColumnCount()
    {
        return _indexToProperty.size();
    }

    public int getRowCount()
    {
        return _rows.size();
    }

    public Object getValueAt(int rowIndex, int columnIndex)
    {
        try
        {
            E item = _rows.get(rowIndex);
            return getGetter(item.getClass(), columnIndex).invoke(item, new Object[0]);
        }
        catch (Exception e)
        {
            if(e instanceof RuntimeException)
            {
                throw (RuntimeException)e;
            }
            else
            {
                throw new Error("Unexpected exception while invoking internal get method!", e);
            }
        }
    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex)
    {
        try
        {
            E item = _rows.get(rowIndex);
            getSetter(item.getClass(), columnIndex).invoke(item, aValue);
        }
        catch (Exception e)
        {
            if(e instanceof RuntimeException)
            {
                throw (RuntimeException)e;
            }
            else
            {
                throw new Error("Unexpected exception while invoking internal set method!", e);
            }
        }
        fireCellChanged(rowIndex, columnIndex);
    }

    public Class getColumnClass(int columnIndex)
    {
        return BeanUtils.getWrapperType(_columnTypes.get(columnIndex));
    }

    public String getColumnName(int columnIndex)
    {
        return _columnNames.isEmpty() ? null : _columnNames.get(columnIndex);
    }

    public void addTableModelListener(TableModelListener l)
    {
        _listeners.add(l);
    }

    public void removeTableModelListener(TableModelListener l)
    {
        _listeners.remove(l);
    }

    public List getPropertyNames()
    {
        return Collections.unmodifiableList(_indexToProperty);
    }

    public List getColmunNames()
    {
    	return Collections.unmodifiableList(_columnNames);
    }
    
    @SuppressWarnings("unchecked")
    public void setColumnNames(List columnNames)
    {
        if(columnNames == null || columnNames.isEmpty())
        {
            _columnNames = Collections.EMPTY_LIST;
        }
        else if(columnNames.size() != _indexToProperty.size())
        {
            throw new IllegalArgumentException("Wrong number of column names! Expected: " + _indexToProperty.size());
        }
        else
        {
            _columnNames = new ArrayList(columnNames);
        }
    }

    protected void fireRowsAdded(int fromIndex, int toIndex)
    {
        fireTableModelEvent(new TableModelEvent(this, fromIndex, toIndex,
            TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
    }

    protected void fireRowAdded(int rowIndex)
    {
        fireRowsAdded(rowIndex, rowIndex);
    }

    protected void fireRowsRemoved(int fromIndex, int toIndex)
    {
        fireTableModelEvent(new TableModelEvent(this, fromIndex, toIndex,
            TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
    }

    protected void fireRowRemoved(int rowIndex)
    {
        fireTableModelEvent(new TableModelEvent(this, rowIndex, rowIndex,
            TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
    }

    protected void fireCellChanged(int rowIndex, int columnIndex)
    {
        fireTableModelEvent(new TableModelEvent(this, rowIndex, rowIndex,
            columnIndex, TableModelEvent.UPDATE));
    }

    protected void fireRowChanged(int rowIndex)
    {
        fireTableModelEvent(new TableModelEvent(this, rowIndex, rowIndex,
            TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE));
    }

    protected void fireTableModelEvent(TableModelEvent event)
    {
        for (TableModelListener l : _listeners)
        {
            l.tableChanged(event);
        }
    }

    private Method getGetter(Class itemClass, int columnIndex) throws SecurityException,
            NoSuchMethodException
    {
        String methodName = "get" + _indexToProperty.get(columnIndex);
        return itemClass.getMethod(methodName, new Class[0]);
    }

    private Method getSetter(Class itemClass, int columnIndex) throws SecurityException,
            NoSuchMethodException
    {
        String methodName = "set" + _indexToProperty.get(columnIndex);
        return itemClass.getMethod(methodName, _columnTypes.get(columnIndex));
    }

    private static Map getPropertyToIndexMap(
            List indexToProperty)
    {
        Map result = new HashMap();

        for (ListIterator iterator = indexToProperty.listIterator(); iterator.hasNext();)
        {
            result.put(iterator.next(), iterator.previousIndex());
        }

        return result;
    }


    private class RowInvocationHandler implements InvocationHandler
    {
        private final E _row;

        public RowInvocationHandler(final E row)
        {
            _row = row;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {
            method = _row.getClass().getMethod(method.getName(), method.getParameterTypes());
           
            Object result = method.invoke(_row, args);

            String methodName = method.getName();
            if (methodName.startsWith("set") && args.length == 1)
            {
                int columnIndex = getPropertyIndex(methodName.substring(3));
                if (columnIndex >= 0
                    && args[0].getClass().equals(_columnTypes.get(columnIndex)))
                {
                    int rowIndex = _rows.indexOf(_row);
                    if (rowIndex >= 0)
                    {
                        fireCellChanged(rowIndex, columnIndex);
                    }
                }
            }

            return result;
        }

        private int getPropertyIndex(String propertyName)
        {
            Integer result = _propertyToIndex.get(propertyName);

            return result != null ? result : -1;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy