com.db2eshop.gui.component.table.api.GenericTable.java Source code

Java tutorial

Introduction

Here is the source code for com.db2eshop.gui.component.table.api.GenericTable.java

Source

/*
 * Copyright 2012 Denis Neuling, Dennis Wieding, Mateusz Wozniak
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.db2eshop.gui.component.table.api;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import com.db2eshop.err.orm.ConstraintVialotionTranslator;
import com.db2eshop.exception.NotImplementedException;
import com.db2eshop.gui.component.table.listener.TableEntityModelListener;
import com.db2eshop.gui.dialog.ErrorDialog;
import com.db2eshop.model.support.AbstractModel;
import com.db2eshop.util.ClassUtil;
import com.db2eshop.util.DateUtil;
import com.db2eshop.util.EntityUtil;
import com.db2eshop.util.UIForUtil;
import com.db2eshop.util.orm.TableValueEntityResolver;

/**
 * <p>
 * Abstract GenericTable class.
 * </p>
 *
 * @author Denis Neuling (denisneuling@gmail.com)
 * 
 */
@Component
public abstract class GenericTable<T extends AbstractModel<T>> extends JTable
        implements InitializingBean, ApplicationListener<ApplicationEvent> {
    private static final long serialVersionUID = 1180747329897017816L;
    protected Logger log = Logger.getLogger(this.getClass());

    protected Class<T> entityClazz;

    protected TableEntityModelListener<T> tableEntityModelListener;
    protected String[] columnNames;

    protected volatile String tableName = this.getClass().getSimpleName();
    protected volatile boolean ready = false;
    protected volatile boolean stable = true;

    @Autowired
    protected ErrorDialog errorDialog;

    @Autowired
    protected TableValueEntityResolver tableValueEntityResolver;

    @Autowired
    private ConstraintVialotionTranslator constraintVialotionTranslator;

    /**
     * <p>
     * Getter for the field <code>tableName</code>.
     * </p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getTableName() {
        return tableName;
    }

    /**
     * <p>
     * rowChanged.
     * </p>
     *
     * @param row
     *            a int.
     */
    @SuppressWarnings("unchecked")
    public void rowChanged(int row) {
        Object[] values = this.getRowAt(row);
        T updateAble = null;
        if (values[0] == null || !(values[0] instanceof Long)) {
            updateAble = ClassUtil.newInstance(entityClazz);
        } else {
            Long id = (Long) values[0];
            updateAble = (T) tableValueEntityResolver.getDao(entityClazz).findById(id);
        }

        try {
            if (stable) {
                updateAble = mixin(values, updateAble);
            }
            if (ready) {
                onRowChange(updateAble);
            }
        } catch (Throwable throwable) {
            stable = false;
            onError(throwable);

            Long id = (Long) values[0];
            updateAble = (T) tableValueEntityResolver.getDao(entityClazz).findById(id);
            rowChanged(row, updateAble);
            stable = true;
        }
    }

    /**
     * <p>
     * rowChanged.
     * </p>
     *
     * @param row
     *            a int.
     * @param entity
     *            a {@link com.db2eshop.model.support.AbstractModel} object.
     */
    @SuppressWarnings("unchecked")
    public void rowChanged(int row, AbstractModel<?> entity) {
        Object values[] = asTableData((T) entity);
        for (int i = 0; i < columnNames.length; i++) {
            this.getModel().setValueAt(values[i], row, i);
        }
        if (stable) {
            onRowChange((T) entity);
        }
    }

    /**
     * <p>
     * getRowAt.
     * </p>
     *
     * @param row
     *            a int.
     * @return an array of {@link java.lang.Object} objects.
     */
    public Object[] getRowAt(int row) {
        Object[] result = new Object[columnNames.length];

        /* 
         * TODO
         * ugly hack to prevent from IndexOutOfBoundsInception
         */
        if (((DefaultTableModel) getModel()).getRowCount() == row && row != 0) {
            --row;
        }
        for (int column = 0; column < columnNames.length; column++) {
            Object columnValue = this.getModel().getValueAt(row, column);
            result[column] = columnValue;
        }
        return result;
    }

    /**
     * <p>
     * getEntityAtRow.
     * </p>
     *
     * @param row
     *            a int.
     * @return a T object.
     */
    @SuppressWarnings("unchecked")
    public T getEntityAtRow(int row) {
        Object[] values = getRowAt(row);
        T entity = null;
        if (values[0] == null) {
            entity = ClassUtil.newInstance(entityClazz);
        } else {
            Long id = (Long) values[0];
            entity = (T) tableValueEntityResolver.getDao(entityClazz).findById(id);
        }
        return mixin(values, entity);
    }

    /**
     * <p>
     * mixin.
     * </p>
     *
     * @param values
     *            an array of {@link java.lang.Object} objects.
     * @param oldEntity
     *            a {@link com.db2eshop.model.support.AbstractModel} object.
     * @return a T object.
     */
    @SuppressWarnings("unchecked")
    protected T mixin(Object[] values, AbstractModel<T> oldEntity) {
        if (values.length != columnNames.length) {
            throw new RuntimeException("ColumnNames lenght unqual value lenght!");
        }

        if (oldEntity == null) {
            log.warn("Entity is null. Skip mixing in Properties.");
            return (T) oldEntity;
        }

        for (int i = 0; i < values.length; i++) {
            oldEntity = (AbstractModel<T>) tableValueEntityResolver.setValue(columnNames[i], values[i], oldEntity);
        }

        return (T) oldEntity;
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() throws Exception {
        if (!getClass().equals(GenericTable.class)) {
            entityClazz = (Class<T>) UIForUtil.retrieveUIFor(this);
            if (entityClazz != null) {
                tableName = entityClazz.getSimpleName();
                Set<String> metaColumnNamesSet = EntityUtil.getRowMeta(entityClazz).keySet();
                List<String> metaColumnNames = new LinkedList<String>(metaColumnNamesSet);
                Collections.sort(metaColumnNames, new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        if (o1 != null && o1.equalsIgnoreCase("id")) {
                            return -1;
                        }
                        return 0;
                    }
                });
                columnNames = metaColumnNames.toArray(new String[metaColumnNames.size()]);
                this.setModel(EntityUtil.asTableModel(entityClazz));
                TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(
                        (DefaultTableModel) this.getModel());
                Class<?>[] columnTypes = ClassUtil.getTypesOfProperties(columnNames, this.entityClazz);
                for (int i = 0; i < columnTypes.length; i++) {
                    if (java.lang.Number.class.isAssignableFrom(columnTypes[i])) {
                        sorter.setComparator(i, new Comparator<Long>() {
                            @Override
                            public int compare(Long arg0, Long arg1) {
                                if (arg0 == arg1) {
                                    return 0;
                                }
                                if (arg1 > arg0) {
                                    return -1;
                                }
                                return 1;
                            }
                        });
                    }
                }
                this.setRowSorter(sorter);
            }
        }
        tableEntityModelListener = new TableEntityModelListener<T>(this);
        this.getModel().addTableModelListener(tableEntityModelListener);
    }

    /**
     * <p>
     * addRow.
     * </p>
     *
     * @param entity
     *            a T object.
     */
    @SuppressWarnings("unchecked")
    public void addRow(AbstractModel<T> entity) {
        if (ready) {
            onRowAdd((T) entity);
        }
        ((DefaultTableModel) getModel()).addRow(asTableData((T) entity));
    }

    /**
     * <p>
     * removeRow.
     * </p>
     *
     * @param row
     *            a int.
     */
    @SuppressWarnings("unchecked")
    public void removeRow(int row) {
        Object[] values = this.getRowAt(row);
        T removeAble = null;
        if (values[0] == null) {
            removeAble = ClassUtil.newInstance(entityClazz);
        } else {
            Long id = (Long) values[0];
            removeAble = (T) tableValueEntityResolver.getDao(entityClazz).findById(id);
        }

        try {
            onRowRemove(removeAble);
            /*
             * TODO
             * ugly hack to prevent from IndexOutOfBoundsInception
             */
            if (((DefaultTableModel) getModel()).getRowCount() == row && row != 0) {
                --row;
            }
            ((DefaultTableModel) getModel()).removeRow(row);
        } catch (Exception e) {
            onError(e);
        }
    }

    /**
     * <p>
     * removeRow.
     * </p>
     *
     * @param entity
     *            a T object.
     */
    public void removeRow(T entity) {
        throw new NotImplementedException();
    }

    /**
     * <p>
     * asTableData.
     * </p>
     *
     * @param entity
     *            a T object.
     * @return an array of {@link java.lang.Object} objects.
     */
    protected Object[] asTableData(T entity) {
        if (entity == null) {
            throw new RuntimeException("Entity cannot be null");
        }
        if (columnNames == null) {
            throw new RuntimeException("Entity extraction fields cannot be null");
        }

        Object[] data = new Object[columnNames.length];
        int index = 0;
        Class<?> entityClass = entity.getClass();
        for (String field : columnNames) {
            try {
                Object object = ClassUtil.getValueOf(field, entity, entityClass,
                        entityClass.getDeclaredField(field).getType());
                if (object != null && AbstractModel.class.isAssignableFrom(object.getClass())) {
                    if (object instanceof AbstractModel) {
                        object = ((AbstractModel<?>) object).getId();
                    } else if (object instanceof String) {
                        log.error("Target was entity but found String " + object);
                    }
                } else if (object != null && object instanceof Date) {
                    object = DateUtil.asString((Date) object);
                }
                data[index] = object;
            } catch (Exception e) {
                log.error("Field " + field + " could not been found", e);
                data[index] = e.getMessage();
            }
            index++;
        }
        return data;
    }

    /** {@inheritDoc} */
    @Override
    public final void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            onApplicationReady();
            ready = true;
        }
    }

    /** {@inheritDoc} */
    public boolean isCellEditable(int row, int column) {
        return column != 0;
    }

    /**
     * <p>
     * onApplicationReady.
     * </p>
     */
    public abstract void onApplicationReady();

    /**
     * <p>
     * onRowChange.
     * </p>
     *
     * @param entity
     *            a T object.
     */
    public abstract void onRowChange(T entity);

    /**
     * <p>
     * onRowRemove.
     * </p>
     *
     * @param entity
     *            a T object.
     */
    public abstract void onRowRemove(T entity);

    /**
     * <p>
     * onRowAdd.
     * </p>
     *
     * @param entity
     *            a T object.
     */
    public abstract void onRowAdd(T entity);

    /**
     * <p>
     * onError.
     * </p>
     *
     * @param throwable
     *            a {@link java.lang.Throwable} object.
     */
    public abstract void onError(Throwable throwable);

    /**
     * <p>
     * Getter for the field <code>entityClazz</code>.
     * </p>
     *
     * @return a {@link java.lang.Class} object.
     */
    public Class<T> getEntityClazz() {
        return entityClazz;
    }

    /**
     * <p>onConstraintViolation.</p>
     *
     * @param throwable a {@link java.lang.Throwable} object.
     * @return a {@link java.lang.String} object.
     */
    public String onConstraintViolation(Throwable throwable) {
        return constraintVialotionTranslator.translate(throwable);
    }

}