edu.ku.brc.specify.ui.db.ResultSetTableModel.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.specify.ui.db.ResultSetTableModel.java

Source

/* Copyright (C) 2015, University of Kansas Center for Research
 * 
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.specify.ui.db;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

import javax.swing.table.DefaultTableModel;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import edu.ku.brc.af.prefs.AppPreferences;
import edu.ku.brc.af.prefs.AppPrefsCache;
import edu.ku.brc.af.ui.db.ERTICaptionInfo;
import edu.ku.brc.af.ui.db.QueryForIdResultsIFace;
import edu.ku.brc.af.ui.forms.DataObjectSettable;
import edu.ku.brc.af.ui.forms.DataObjectSettableFactory;
import edu.ku.brc.af.ui.forms.FormHelper;
import edu.ku.brc.af.ui.forms.formatters.DataObjFieldFormatMgr;
import edu.ku.brc.af.ui.forms.formatters.DataObjSwitchFormatter;
import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterIFace;
import edu.ku.brc.dbsupport.CustomQueryIFace;
import edu.ku.brc.dbsupport.CustomQueryListener;
import edu.ku.brc.dbsupport.JPAQuery;
import edu.ku.brc.dbsupport.RecordSetIFace;
import edu.ku.brc.dbsupport.SQLExecutionListener;
import edu.ku.brc.dbsupport.SQLExecutionProcessor;
import edu.ku.brc.specify.datamodel.RecordSet;
import edu.ku.brc.specify.tasks.ExpressSearchTask;
import edu.ku.brc.specify.tasks.subpane.ESResultsTablePanelIFace;
import edu.ku.brc.ui.CommandAction;
import edu.ku.brc.ui.CommandDispatcher;
import edu.ku.brc.ui.DateWrapper;
import edu.ku.brc.ui.JStatusBar;
import edu.ku.brc.ui.UIRegistry;

/**
 * @code_status Alpha
 *
 * @author rods
 *
 */
@SuppressWarnings("serial")
public class ResultSetTableModel extends DefaultTableModel implements SQLExecutionListener, CustomQueryListener {
    // Static Data Members
    private static final Logger log = Logger.getLogger(ResultSetTableModel.class);

    protected static DateWrapper scrDateFormat = AppPrefsCache.getDateWrapper("ui", "formatting", "scrdateformat");

    protected static int VISIBLE_ROWS = 10; // XXX Got get this from elsewhere

    // Data Members    
    protected ESResultsTablePanelIFace parentERTP;
    protected Vector<Class<?>> classNames = new Vector<Class<?>>();
    protected Vector<String> colNames = new Vector<String>();
    protected int currentRow = 0;
    protected QueryForIdResultsIFace results;
    protected boolean doSequentially;
    protected int numColumns = 0;
    protected Vector<Integer> ids = null; // Must be initialized to null!
    protected Vector<Vector<Object>> cache = new Vector<Vector<Object>>();
    protected List<ERTICaptionInfo> captionInfo = null;
    protected int[] columnIndexMapper = null;

    protected PropertyChangeListener propertyListener = null;

    protected JStatusBar statusBar = UIRegistry.getStatusBar();
    protected boolean doDebug = AppPreferences.getLocalPrefs().getBoolean("esdebug", false);

    // Frame buffer
    protected int startInx = 0;
    protected int endInx = 0;

    protected boolean useColOffset = false;

    /**
     * Construct with a QueryForIdResultsIFace
     * @param parentERTP
     * @param results
     */
    public ResultSetTableModel(final ESResultsTablePanelIFace parentERTP, final QueryForIdResultsIFace results) {
        this(parentERTP, results, false);
    }

    /**
     * @param parentERTP
     * @param results
     * @param doSequentially
     */
    public ResultSetTableModel(final ESResultsTablePanelIFace parentERTP, final QueryForIdResultsIFace results,
            final boolean doSequentially, final boolean doSelfStart) {
        this.parentERTP = parentERTP;
        this.results = results;
        this.doSequentially = doSequentially;

        captionInfo = results.getVisibleCaptionInfo();

        if (!results.showProgress()) {
            statusBar = null;
        }

        initialize();

        if (doSelfStart) {
            startDataAquisition(doSequentially);
        }
    }

    /**
     * @param parentERTP
     * @param results
     * @param doSequentially
     */
    public ResultSetTableModel(final ESResultsTablePanelIFace parentERTP, final QueryForIdResultsIFace results,
            final boolean doSequentially) {
        this(parentERTP, results, doSequentially, true);
    }

    /**
     * perform initializations which must be performed before startDataAcquisition() is called.
     */
    protected void initialize() {
        //nothing to do here
    }

    public void startDataAcquisition() {
        startDataAquisition(doSequentially);
    }

    /**
     * @param doSequentiallyArg
     */
    protected void startDataAquisition(final boolean doSequentiallyArg) {
        //System.out.println("\n"+results.getTitle()+" " +results.isHQL());
        if (statusBar != null) {
            statusBar.incrementRange(getClass().getSimpleName());
        }

        if (results.isHQL()) {
            List<ERTICaptionInfo> captions = results.getVisibleCaptionInfo();
            numColumns = captions.size();
            for (ERTICaptionInfo caption : captions) {
                Class<?> cls = caption.getColClass();
                classNames.addElement(cls == Boolean.class ? String.class : cls);
                colNames.addElement(caption.getColName());
            }

            JPAQuery jpaQuery = null;
            String sqlStr = results.getSQL(results.getSearchTerm(), ids);
            if (sqlStr != null) {
                jpaQuery = new JPAQuery(sqlStr, this);
                jpaQuery.setCount(results.isCount());
                if (!results.isCount()) {
                    jpaQuery.setMaxResults(results.getMaxTableRows()); //not sure if this ok...
                }
                jpaQuery.setParams(results.getParams());
                if (doSequentiallyArg) {
                    jpaQuery.execute();
                } else {
                    results.setQueryTask(jpaQuery.start());
                }
            }

        } else {
            SQLExecutionProcessor sqlProc = new SQLExecutionProcessor(this,
                    results.getSQL(results.getSearchTerm(), ids));
            if (doSequentiallyArg) {
                sqlProc.execute();

            } else {
                sqlProc.start();
            }
        }
    }

    /* (non-Javadoc)
     * @see javax.swing.table.DefaultTableModel#isCellEditable(int, int)
     */
    @Override
    public boolean isCellEditable(int arg0, int arg1) {
        return false;
    }

    /**
     * Cleans up internal data members.
     */
    public void cleanUp() {
        parentERTP = null;
        results = null;
        propertyListener = null;

        for (Vector<Object> list : cache) {
            list.clear();
        }
        cache.clear();

        if (ids != null) {
            ids.clear();
        }

    }

    /**
     * Returns the number of columns
     * @return Number of columns
     */
    public int getColumnCount() {
        if (captionInfo != null) {
            return captionInfo.size();
        }
        return numColumns;
    }

    /**
     * Returns the Class object for a column
     * @param column the column in question
     * @return the Class of the column
     */
    @Override
    public Class<?> getColumnClass(int column) {
        /*
         * Modified this to fix a bug that (so far) had only shown itself for Timestamp columns.
         * 
         * ResultSetTableModel.getValueAt(row, column) returns a formatted object for the column.
         * 
         * Then in the JTable display code, JTable.getCellRenderer calls getDefaultRenderer(getColumnClass())
         * Which returns a formatter for columnClass and trie to format the already-formatted value
         * from getValueAt(). Which actually works out (except the "centered" property is sometimes overridden
         * for Numbers) for all classes (usually a default formatter Object is eventually returned) except
         * java.sql.Timestamp. 
         * 
         * Everything seems OK now.
         * 
         */
        Class<?> cls = getColumnClass2(column);
        if (cls == java.util.Calendar.class) {
            return String.class; //whatever getValueAt(?, column) returns.
        }
        return cls;
        //return Object.class; //whatever getValueAt(?, column) returns.
    }

    protected Class<?> getColumnClass2(int column) {
        if (captionInfo != null) {
            if (captionInfo.get(column).getColClass() == null) {
                return String.class;
            }
            //log.debug(captionInfo.get(column).getColClass().getName());
            return captionInfo.get(column).getColClass();
        }

        if (classNames.size() > 0) {
            Class<?> classObj = classNames.elementAt(column);

            if (classObj == Calendar.class || classObj == java.sql.Date.class || classObj == Date.class
                    || classObj == Timestamp.class) {
                return String.class;
            }
            return classObj;
        }
        return String.class;
    }

    /**
     * Get the column name
     * @param column the column of the cell to be gotten
     */
    @Override
    public String getColumnName(int column) {
        if (captionInfo != null) {
            return captionInfo.get(column).getColLabel();
        }

        if (column > -1 && column < colNames.size()) {
            return colNames.get(column);
        }

        return "N/A";
    }

    /**
     * Gets the 'raw' value of the row col.
     * @param rowArg the row of the cell to be gotten
     * @param colArg the column of the cell to be gotten
     */
    public Object getCacheValueAt(final int row, final int column) {
        if (row > -1 && row < cache.size()) {
            Vector<Object> rowArray = cache.get(row);
            if (column > -1 && column < rowArray.size()) {
                return rowArray.get(column);
            }
        }
        return null;
    }

    /**
     * Gets the value of the row col.
     * @param rowArg the row of the cell to be gotten
     * @param colArg the column of the cell to be gotten
     */
    public Object getValueAt(final int row, final int column) {
        if (row > -1 && row < cache.size()) {
            Vector<Object> rowArray = cache.get(row);
            if (column > -1 && column < rowArray.size()) {
                Object obj = rowArray.get(column);

                Class<?> dataClassObj = getColumnClass2(column); //see comment for getColumnClass().
                if (obj == null && (dataClassObj == null || dataClassObj == String.class)) {
                    return "";
                }

                if (obj instanceof Calendar) {
                    return scrDateFormat.format((Calendar) obj);

                } else if (obj instanceof Timestamp) {
                    return scrDateFormat.format((Date) obj);
                } else if (obj instanceof java.sql.Date || obj instanceof Date) {
                    return scrDateFormat.format((Date) obj);

                } else if (obj instanceof Boolean) {
                    if (getColumnClass2(column) == Boolean.class) {
                        return obj;
                    }
                    return UIRegistry.getResourceString(obj.toString());
                }

                //System.out.println(row+" "+column+" ["+obj+"] "+getColumnClass(column).getSimpleName() + " " + useColOffset);

                UIFieldFormatterIFace formatter = captionInfo != null
                        ? captionInfo.get(column).getUiFieldFormatter()
                        : null;
                if (formatter != null && formatter.isInBoundFormatter()) {
                    return formatter.formatToUI(obj);
                }

                return obj;
            }
        }

        return "No Data";
    }

    /**
     * Sets a new value into the Model
     * @param aValue the value to be set
     * @param row the row of the cell to be set
     * @param column the column of the cell to be set
     */
    @Override
    public void setValueAt(Object aValue, int row, int column) {
        // empty
    }

    /**
     * Returns the number of rows
     * @return Number of rows
     */
    public int getRowCount() {
        return cache == null ? 0 : cache.size();
    }

    /**
     * @param index
     * @return
     */
    public Integer getRowId(final int index) {
        return ids.get(index);
    }

    /**
     * Removes a row from the model.
     * @param index the index to be removed.
     */
    public void removeRow(final int index) {
        cache.remove(index);
        ids.remove(index);
        fireTableRowsDeleted(index, index);
    }

    /**
     * @return the results
     */
    public QueryForIdResultsIFace getQueryForIdResults() {
        return results;
    }

    /**
     * Returns a RecordSet object from the table
     * @param rows the selected rows
     * @param returnAll indicates whether all the records should be returned if nothing was selected
     * @return Returns a RecordSet object from the table
     */
    public RecordSetIFace getRecordSet(final int[] rows, final boolean returnAll) {
        RecordSet rs = new RecordSet();
        rs.setType(RecordSet.GLOBAL);
        rs.initialize();

        // return if now rows are selected
        if (!returnAll && (rows == null || rows.length == 0)) {
            return rs;
        }

        if (rows == null) {
            for (Integer id : ids) {
                rs.addItem(id);
            }
        } else {
            for (int inx : rows) {
                rs.addItem(ids.get(inx));
            }
        }

        return rs;
    }

    /**
     * Clears all the data from the model
     *
     */
    public void clear() {
        if (ids != null) {
            ids.clear();
        }

        if (cache != null) {
            for (Vector<Object> row : cache) {
                row.clear();
            }
            cache.clear();
        }
        classNames.clear();
        colNames.clear();

        currentRow = 0;
    }

    /**
     * @param propertyListener the propertyListener to set
     */
    public void setPropertyListener(PropertyChangeListener propertyListener) {
        this.propertyListener = propertyListener;
    }

    /**
     * @param setter
     * @param parent
     * @param fieldName
     * @param fieldClass
     * @param resultSet
     * @param colIndex
     * @throws SQLException
     */
    protected void setField(final DataObjectSettable setter, final Object parent, final String fieldName,
            final Class<?> fieldClass, final ResultSet resultSet, final int colIndex) throws SQLException {
        Object fieldDataObj = resultSet.getObject(colIndex + 1);
        //log.debug("fieldName ["+fieldName+"] fieldClass ["+fieldClass.getSimpleName()+"] colIndex [" +  colIndex + "] fieldDataObj [" + fieldDataObj+"]");
        if (fieldDataObj != null) {
            if (fieldClass == String.class) {
                setter.setFieldValue(parent, fieldName, fieldDataObj);

            } else if (fieldClass == Byte.class) {
                setter.setFieldValue(parent, fieldName, resultSet.getByte(colIndex + 1));

            } else if (fieldClass == Short.class) {
                setter.setFieldValue(parent, fieldName, resultSet.getShort(colIndex + 1));

            } else {
                setter.setFieldValue(parent, fieldName, fieldDataObj);
            }
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.dbsupport.SQLExecutionListener#exectionDone(edu.ku.brc.dbsupport.SQLExecutionProcessor, java.sql.ResultSet)
     */
    @Override
    //@SuppressWarnings("null")
    public synchronized void exectionDone(final SQLExecutionProcessor process, final ResultSet resultSet) {
        if (statusBar != null) {
            statusBar.incrementValue(getClass().getSimpleName());
        }

        if (resultSet == null || results == null) {
            log.error("The " + (resultSet == null ? "resultSet" : "results") + " is null.");
            if (propertyListener != null) {
                propertyListener.propertyChange(new PropertyChangeEvent(this, "rowCount", null, 0));
            }
            return;
        }

        List<ERTICaptionInfo> captions = results.getVisibleCaptionInfo();

        // This can do one of two things:
        // 1) Take multiple columns and create an object and use a DataObjectFormatter to format the object.
        // 2) Table multiple objects that were derived from the columns and roll those up into a single column's value.
        //    This happens when you get back rows of info where part of the columns are duplicated because you really
        //    want those value to be put into a single column.
        //
        // Step One - Is to figure out what type of object needs to be created and what the Columns are 
        //            that need to be set into the object so the dataObjFormatter can do its job.
        //
        // Step Two - If the objects are being aggregated then the object created from the columns are added to a List
        //            and then last formatted as an "aggregation"

        try {
            if (resultSet.next()) {
                ResultSetMetaData metaData = resultSet.getMetaData();

                // Composite
                boolean hasCompositeObj = false;
                DataObjSwitchFormatter dataObjFormatter = null;
                UIFieldFormatterIFace formatter = null;
                Object compObj = null;

                // Aggregates
                ERTICaptionInfo aggCaption = null;
                ERTICaptionInfo compositeCaption = null;
                Vector<Object> aggList = null;
                DataObjectSettable aggSetter = null;
                Stack<Object> aggListRecycler = null;

                DataObjectSettable dataSetter = null; // data getter for Aggregate or the Subclass

                // Loop through the caption to figure out what columns will be displayed.
                // Watch for Captions with an Aggregator or Composite 
                numColumns = captions.size();
                for (ERTICaptionInfo caption : captions) {
                    colNames.addElement(caption.getColLabel());

                    int inx = caption.getPosIndex() + 1;
                    //log.debug(metaData.getColumnClassName(inx));
                    Class<?> cls = null;
                    try {
                        cls = Class.forName(metaData.getColumnClassName(inx));
                        if (cls == Calendar.class || cls == java.sql.Date.class || cls == Date.class) {
                            cls = String.class;
                        }
                    } catch (SQLException ex) {
                        cls = String.class;
                    }
                    classNames.addElement(cls);
                    caption.setColClass(cls);

                    if (caption.getAggregatorName() != null) {
                        //log.debug("The Agg is ["+caption.getAggregatorName()+"] "+caption.getColName());

                        // Alright we have an aggregator
                        aggList = new Vector<Object>();
                        aggListRecycler = new Stack<Object>();
                        aggCaption = caption;
                        aggSetter = DataObjectSettableFactory.get(aggCaption.getAggClass().getName(),
                                FormHelper.DATA_OBJ_SETTER);

                        // Now check to see if we are aggregating the this type of object or a child object of this object
                        // For example Collectors use an Agent as part of the aggregation
                        if (aggCaption.getSubClass() != null) {
                            dataSetter = DataObjectSettableFactory.get(aggCaption.getSubClass().getName(),
                                    FormHelper.DATA_OBJ_SETTER);
                        } else {
                            dataSetter = aggSetter;
                        }

                    } else if (caption.getColInfoList() != null) {
                        formatter = caption.getUiFieldFormatter();
                        if (formatter != null) {
                            compositeCaption = caption;
                        } else {
                            // OK, now aggregation but we will be rolling up multiple columns into a single object for formatting
                            // We need to get the formatter to see what the Class is of the object
                            hasCompositeObj = true;
                            aggCaption = caption;
                            dataObjFormatter = caption.getDataObjFormatter();
                            if (dataObjFormatter != null) {
                                if (dataObjFormatter.getDataClass() != null) {
                                    aggSetter = DataObjectSettableFactory.get(
                                            dataObjFormatter.getDataClass().getName(),
                                            "edu.ku.brc.af.ui.forms.DataSetterForObj");
                                } else {
                                    log.error("formatterObj.getDataClass() was null for " + caption.getColName());
                                }
                            } else {
                                log.error("DataObjFormatter was null for " + caption.getColName());
                            }
                        }

                    }
                    //colNames.addElement(metaData.getColumnName(i));
                    //System.out.println("**************** " + caption.getColLabel()+ " "+inx+ " " + caption.getColClass().getSimpleName());
                }

                // aggCaption will be non-null for both a Aggregate AND a Composite
                if (aggCaption != null) {
                    // Here we need to dynamically discover what the column indexes are that we to grab
                    // in order to set them into the created data object
                    for (ERTICaptionInfo.ColInfo colInfo : aggCaption.getColInfoList()) {
                        for (int i = 0; i < metaData.getColumnCount(); i++) {
                            String colName = StringUtils.substringAfterLast(colInfo.getColumnName(), ".");
                            if (colName.equalsIgnoreCase(metaData.getColumnName(i + 1))) {
                                colInfo.setPosition(i);
                                break;
                            }
                        }
                    }

                    // Now check to see if there is an Order Column because the Aggregator 
                    // might need it for sorting the Aggregation
                    String ordColName = aggCaption.getOrderCol();
                    if (StringUtils.isNotEmpty(ordColName)) {
                        String colName = StringUtils.substringAfterLast(ordColName, ".");
                        //log.debug("colName ["+colName+"]");
                        for (int i = 0; i < metaData.getColumnCount(); i++) {
                            //log.debug("["+colName+"]["+metaData.getColumnName(i+1)+"]");
                            if (colName.equalsIgnoreCase(metaData.getColumnName(i + 1))) {
                                aggCaption.setOrderColIndex(i);
                                break;
                            }
                        }
                        if (aggCaption.getOrderColIndex() == -1) {
                            log.error("Agg Order Column Index wasn't found [" + ordColName + "]");
                        }
                    }
                }

                if (ids == null) {
                    ids = new Vector<Integer>();
                } else {
                    ids.clear();
                }

                // Here is the tricky part.
                // When we are doing a Composite we are just taking multiple columns and 
                // essentially replace them with a single value from the DataObjFormatter
                //
                // But when doing an Aggregation we taking several rows and rolling them up into a single value.
                // so this code knows when it is doing an aggregation, so it knows to only add a new row to the display-able
                // results when primary id changes.

                DataObjFieldFormatMgr dataObjMgr = DataObjFieldFormatMgr.getInstance();
                Vector<Object> row = null;
                boolean firstTime = true;
                int prevId = Integer.MAX_VALUE; // really can't assume any value but will choose Max

                int numCols = resultSet.getMetaData().getColumnCount();
                do {
                    int id = resultSet.getInt(1);
                    //log.debug("id: "+id+"  prevId: "+prevId);

                    // Remember aggCaption is used by both a Aggregation and a Composite
                    if (aggCaption != null && !hasCompositeObj) {
                        if (firstTime) {
                            prevId = id;
                            row = new Vector<Object>();
                            firstTime = false;
                            cache.add(row);
                            ids.add(id);

                        } else if (id != prevId) {
                            //log.debug("Agg List len: "+aggList.size());

                            if (row != null && aggList != null) {
                                int aggInx = captions.indexOf(aggCaption);
                                row.remove(aggInx);
                                row.insertElementAt(dataObjMgr.aggregate(aggList, aggCaption.getAggClass()),
                                        aggInx);

                                if (aggListRecycler != null) {
                                    aggListRecycler.addAll(aggList);
                                }
                                aggList.clear();

                                row = new Vector<Object>();
                                cache.add(row);
                                ids.add(id);
                            }
                            prevId = id;

                        } else if (row == null) {
                            row = new Vector<Object>();
                            cache.add(row);
                            ids.add(id);
                        }
                    } else {
                        row = new Vector<Object>();
                        cache.add(row);
                        ids.add(id);
                    }

                    // Now for each Caption column get a value
                    for (ERTICaptionInfo caption : captions) {
                        int posIndex = caption.getPosIndex();
                        if (caption == aggCaption) // Checks to see if we need to take multiple columns and make one column
                        {
                            if (hasCompositeObj) // just doing a Composite
                            {
                                if (aggSetter != null && row != null && dataObjFormatter != null) {
                                    if (compObj == null) {
                                        compObj = aggCaption.getAggClass().newInstance();
                                    }

                                    for (ERTICaptionInfo.ColInfo colInfo : aggCaption.getColInfoList()) {
                                        setField(aggSetter, compObj, colInfo.getFieldName(),
                                                colInfo.getFieldClass(), resultSet, colInfo.getPosition());
                                    }
                                    row.add(DataObjFieldFormatMgr.getInstance().format(compObj,
                                            compObj.getClass()));

                                } else if (formatter != null) {
                                    int len = compositeCaption.getColInfoList().size();
                                    Object[] val = new Object[len];
                                    int i = 0;
                                    for (ERTICaptionInfo.ColInfo colInfo : compositeCaption.getColInfoList()) {
                                        int colInx = colInfo.getPosition() + posIndex + 1;
                                        if (colInx < numCols) {
                                            val[i++] = resultSet.getObject(colInx);
                                        } else {
                                            //val[i++] = resultSet.getObject(posIndex+1);
                                            val[i++] = "(Missing Data)";
                                        }
                                    }
                                    row.add(formatter.formatToUI(val));

                                } else {
                                    log.error("Aggregator is null! [" + aggCaption.getAggregatorName()
                                            + "] or row or aggList");
                                }
                            } else if (aggSetter != null && row != null && aggList != null) // Doing an Aggregation
                            {
                                Object aggObj;
                                if (aggListRecycler.size() == 0) {
                                    aggObj = aggCaption.getAggClass().newInstance();
                                } else {
                                    aggObj = aggListRecycler.pop();
                                }
                                Object aggSubObj = aggCaption.getSubClass() != null
                                        ? aggCaption.getSubClass().newInstance()
                                        : null;
                                aggList.add(aggObj);

                                //@SuppressWarnings("unused")
                                //DataObjAggregator aggregator = DataObjFieldFormatMgr.getInstance().getAggregator(aggCaption.getAggregatorName());
                                //log.debug(" aggCaption.getOrderColIndex() "+ aggCaption.getOrderColIndex());

                                //aggSetter.setFieldValue(aggObj, aggregator.getOrderFieldName(), resultSet.getObject(aggCaption.getOrderColIndex() + 1));

                                Object dataObj;
                                if (aggSubObj != null) {
                                    aggSetter.setFieldValue(aggObj, aggCaption.getSubClassFieldName(), aggSubObj);
                                    dataObj = aggSubObj;
                                } else {
                                    dataObj = aggObj;
                                }

                                for (ERTICaptionInfo.ColInfo colInfo : aggCaption.getColInfoList()) {
                                    setField(dataSetter, dataObj, colInfo.getFieldName(), colInfo.getFieldClass(),
                                            resultSet, colInfo.getPosition());
                                }
                                row.add("PlaceHolder");

                            } else if (aggSetter == null || aggList == null) {
                                log.error("Aggregator is null! [" + aggCaption.getAggregatorName() + "] or aggList["
                                        + aggList + "]");
                            }

                        } else if (row != null) {
                            if (caption.getColName() == null && caption.getColInfoList().size() > 0) {
                                int len = caption.getColInfoList().size();
                                Object[] val = new Object[len];
                                for (int i = 0; i < caption.getColInfoList().size(); i++) {
                                    int inx = posIndex + 1 + i;
                                    val[i] = caption.processValue(resultSet.getObject(inx));
                                }
                                row.add(caption.getUiFieldFormatter().formatToUI(val));
                                //col += caption.getColInfoList().size() - 1;

                            } else {
                                Object obj = caption.processValue(resultSet.getObject(posIndex + 1));
                                row.add(obj);
                            }
                        }
                    }

                } while (resultSet.next());

                // We were always setting the rolled up data when the ID changed
                // but on the last row we need to do it here manually (so to speak)
                if (aggCaption != null && aggList != null && aggList.size() > 0 && row != null) {
                    int aggInx = captions.indexOf(aggCaption);
                    row.remove(aggInx);
                    String colStr;
                    if (StringUtils.isNotEmpty(aggCaption.getAggregatorName())) {
                        colStr = DataObjFieldFormatMgr.getInstance().aggregate(aggList,
                                aggCaption.getAggregatorName());

                    } else {
                        colStr = DataObjFieldFormatMgr.getInstance().aggregate(aggList, aggCaption.getAggClass());
                    }
                    row.insertElementAt(colStr, aggInx);
                    aggList.clear();
                    aggListRecycler.clear();
                }

                fireTableStructureChanged();
                fireTableDataChanged();
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        if (propertyListener != null) {
            propertyListener
                    .propertyChange(new PropertyChangeEvent(this, "rowCount", null, new Integer(cache.size())));
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.dbsupport.SQLExecutionListener#executionError(edu.ku.brc.dbsupport.SQLExecutionProcessor, java.lang.Exception)
     */
    @Override
    public synchronized void executionError(SQLExecutionProcessor process, Exception ex) {
        if (statusBar != null) {
            statusBar.incrementValue(getClass().getSimpleName());
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.dbsupport.CustomQueryListener#exectionDone(edu.ku.brc.dbsupport.CustomQuery)
     */
    @Override
    public void exectionDone(final CustomQueryIFace customQuery) {
        if (statusBar != null) {
            statusBar.incrementValue(getClass().getSimpleName());
        }

        results.queryTaskDone(customQuery);
        List<?> list = customQuery.getDataObjects();

        List<ERTICaptionInfo> captions = results.getVisibleCaptionInfo();

        //log.debug("Results size: "+list.size());

        if (ids == null) {
            ids = new Vector<Integer>();
        } else {
            ids.clear();
        }

        if (!customQuery.isInError() && !customQuery.isCancelled() && list != null && list.size() > 0) {
            /*if (numColumns == 1)
            {
            for (Object rowObj : list)
            {
                Vector<Object> row = new Vector<Object>(list.size());
                row.add(rowObj);
                cache.add(row);
            }
                
            } else*/
            {

                int maxTableRows = results.getMaxTableRows();
                int rowNum = 0;
                for (Object rowObj : list) {
                    if (rowNum == maxTableRows) {
                        break;
                    }
                    if (customQuery.isCancelled()) {
                        break;
                    }
                    //Vector<Object> row = new Vector<Object>(list.size()); //list.size()?????
                    Vector<Object> row = new Vector<Object>(
                            rowObj.getClass().isArray() ? ((Object[]) rowObj).length : 1);
                    if (rowObj != null && rowObj.getClass().isArray()) {
                        Object[] rowCols = (Object[]) rowObj;
                        int capInx = 0;
                        for (int col = 0; col < rowCols.length; col++) {
                            Object colObj = rowCols[col];
                            ERTICaptionInfo capInfo = captions.get(capInx);

                            if (col == 0) {
                                if (colObj instanceof Integer) {
                                    ids.add((Integer) colObj);
                                    if (doDebug)
                                        log.debug("*** 1 Adding id[" + colObj + "]");
                                } else {
                                    //log.error("First Column must be Integer id! ["+colObj+"]");
                                    row.add(capInfo.processValue(colObj));
                                    capInx++;
                                }

                            } else if (capInfo.getColName() == null && capInfo.getColInfoList().size() > 0) {
                                int len = capInfo.getColInfoList().size();
                                Object[] val = new Object[len];
                                for (int i = 0; i < capInfo.getColInfoList().size(); i++) {
                                    val[i] = capInfo.processValue(rowCols[col + i]);
                                }
                                row.add(capInfo.getUiFieldFormatter().formatToUI(val));
                                col += capInfo.getColInfoList().size() - 1;
                                capInx++;

                            } else {
                                Object obj = capInfo.processValue(colObj);
                                row.add(obj);
                                if (doDebug)
                                    log.debug("*** 2 Adding id[" + obj + "]");
                                capInx++;
                            }

                        } // for
                    } else {
                        row.add(rowObj);
                    }
                    cache.add(row);
                    rowNum++;
                }
            }

            results.cacheFilled(cache);

            fireTableDataChanged();
        }

        if (propertyListener != null) {
            propertyListener
                    .propertyChange(new PropertyChangeEvent(this, "rowCount", null, new Integer(cache.size())));
        }

        if (parentERTP != null) {
            CommandAction cmdAction = new CommandAction(ExpressSearchTask.EXPRESSSEARCH, "SearchComplete",
                    customQuery);
            cmdAction.setProperty("QueryForIdResultsIFace", results);
            cmdAction.setProperty("ESResultsTablePanelIFace", parentERTP);
            CommandDispatcher.dispatch(cmdAction);
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.dbsupport.CustomQueryListener#executionError(edu.ku.brc.dbsupport.CustomQuery)
     */
    @Override
    public void executionError(CustomQueryIFace customQuery) {
        UIRegistry.getStatusBar().incrementValue(getClass().getSimpleName());
    }

    /**
     * @return the parentERTP
     */
    public ESResultsTablePanelIFace getParentERTP() {
        return parentERTP;
    }

    /**
     * @return true if results are still being loaded or created.
     */
    public boolean isLoadingCells() {
        return false;
    }

    public QueryForIdResultsIFace getResults() {
        return results;
    }

}