edu.ku.brc.specify.tasks.InteractionsProcessor.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.specify.tasks.InteractionsProcessor.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.tasks;

import static edu.ku.brc.ui.UIRegistry.getLocalizedMessage;
import static edu.ku.brc.ui.UIRegistry.getResourceString;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import javax.swing.JOptionPane;

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

import edu.ku.brc.af.core.TaskMgr;
import edu.ku.brc.af.core.db.DBTableIdMgr;
import edu.ku.brc.af.core.db.DBTableInfo;
import edu.ku.brc.af.core.expresssearch.QueryAdjusterForDomain;
import edu.ku.brc.af.tasks.BaseTask.ASK_TYPE;
import edu.ku.brc.af.ui.forms.Viewable;
import edu.ku.brc.dbsupport.DataProviderFactory;
import edu.ku.brc.dbsupport.DataProviderSessionIFace;
import edu.ku.brc.dbsupport.RecordSetIFace;
import edu.ku.brc.dbsupport.RecordSetItemIFace;
import edu.ku.brc.helpers.SwingWorker;
import edu.ku.brc.specify.conversion.BasicSQLUtils;
import edu.ku.brc.specify.datamodel.CollectionObject;
import edu.ku.brc.specify.datamodel.InfoRequest;
import edu.ku.brc.specify.datamodel.OneToManyProviderIFace;
import edu.ku.brc.specify.ui.ColObjInfo;
import edu.ku.brc.specify.ui.PrepInfo;
import edu.ku.brc.specify.ui.SelectPrepsDlg;
import edu.ku.brc.ui.JStatusBar;
import edu.ku.brc.ui.UIHelper;
import edu.ku.brc.ui.UIRegistry;

/**
 * @author rod
 *
 * @code_status Alpha
 *
 * Oct 9, 2008
 *
 */
public class InteractionsProcessor<T extends OneToManyProviderIFace> {
    private static final Logger log = Logger.getLogger(InteractionsProcessor.class);
    private static final String LOAN_LOADR = "LoanLoader";

    protected static final int forLoan = 0;
    protected static final int forGift = 1;
    protected static final int forAcc = 2;

    protected InteractionsTask task;
    protected int isFor;
    protected int tableId;
    protected Viewable viewable = null;

    /**
     * 
     */
    public InteractionsProcessor(final InteractionsTask task, final int isFor, final int tableId) {
        this.task = task;
        this.isFor = isFor;
        this.tableId = tableId;
    }

    /**
     * Asks where the source of the Loan Preps should come from.
     * @return the source enum
     */
    protected ASK_TYPE askSourceOfPreps(final boolean hasInfoReqs, final boolean hasColObjRS,
            final T currPrepProvider) {
        String label;
        if (hasInfoReqs && hasColObjRS) {
            label = getResourceString("NEW_INTER_USE_RS_IR");

        } else if (hasInfoReqs) {
            label = getResourceString("NEW_INTER_USE_IR");
        } else {
            label = getResourceString("NEW_INTER_USE_RS");
        }

        boolean isForAcc = isFor == forAcc;
        Object[] options = new Object[!isForAcc
                || (isForAcc && ((!hasInfoReqs && !hasColObjRS) || currPrepProvider != null)) ? 2 : 3];
        Integer dosOpt = null;
        Integer rsOpt = null;
        Integer noneOpt = null;
        if (!isForAcc || currPrepProvider != null) {
            options[0] = label;
            options[1] = getResourceString("NEW_INTER_ENTER_CATNUM");
            rsOpt = JOptionPane.YES_OPTION;
            dosOpt = JOptionPane.NO_OPTION;
        } else {
            if (options.length == 2) {
                options[0] = getResourceString("NEW_INTER_ENTER_CATNUM");
                options[1] = getResourceString("NEW_INTER_EMPTY");
                dosOpt = JOptionPane.YES_OPTION;
                noneOpt = JOptionPane.NO_OPTION;
            } else {
                options[0] = label;
                options[1] = getResourceString("NEW_INTER_ENTER_CATNUM");
                options[2] = getResourceString("NEW_INTER_EMPTY");
                rsOpt = JOptionPane.YES_OPTION;
                dosOpt = JOptionPane.NO_OPTION;
                noneOpt = JOptionPane.CANCEL_OPTION;
            }
        }

        int userChoice = JOptionPane.showOptionDialog(UIRegistry.getTopWindow(),
                getResourceString("NEW_INTER_CHOOSE_RSOPT"), getResourceString("NEW_INTER_CHOOSE_RSOPT_TITLE"),
                JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
        if (userChoice == dosOpt) {
            return ASK_TYPE.EnterDataObjs;
        } else if (rsOpt != null && userChoice == rsOpt) {
            return ASK_TYPE.ChooseRS;
        } else if (noneOpt != null && userChoice == noneOpt) {
            return ASK_TYPE.None;
        }

        return ASK_TYPE.Cancel;
    }

    /**
     * Creates a new loan/gift.
     */
    public void createOrAdd() {
        this.viewable = null;
        createOrAdd(null, null, null);
    }

    /**
     * Creates a new loan/gift and will set the new data object back into the Viewable.
     * @param viewableArg
     */
    public void createOrAdd(final Viewable viewableArg) {
        this.viewable = viewableArg;

        createOrAdd(null, null, null);
    }

    /**
     * @param recordSetArg
     */
    public void createOrAdd(final RecordSetIFace recordSetArg) {
        this.viewable = null;
        createOrAdd(null, null, recordSetArg);
    }

    /**
     * @param currPrepProvider
     */
    public void createOrAdd(final T currPrepProvider) {
        this.viewable = null;
        createOrAdd(currPrepProvider, null, null);
    }

    /**
     * Creates a new loan from a RecordSet.
     * @param currPrepProvider an existing loan that needs additional Preps
     * @param infoRequest a info request
     * @param recordSetArg the recordset to use to create the loan
     */
    public void createOrAdd(final T currPrepProvider, final InfoRequest infoRequest,
            final RecordSetIFace recordSetArg) {
        RecordSetIFace recordSet = recordSetArg;
        boolean isEmptyAcc = false;
        if (infoRequest == null && recordSet == null) {
            String catNumField = "catalogNumber";

            // Get a List of InfoRequest RecordSets
            Vector<RecordSetIFace> rsList = task.getInfoReqRecordSetsFromSideBar();
            RecordSetTask rsTask = (RecordSetTask) TaskMgr.getTask(RecordSetTask.RECORD_SET);
            List<RecordSetIFace> colObjRSList = rsTask.getRecordSets(CollectionObject.getClassTableId());

            // If the List is empty then
            if (rsList.size() == 0 && colObjRSList.size() == 0 && (isFor != forAcc || currPrepProvider != null)) {
                recordSet = task.askForDataObjRecordSet(CollectionObject.class, catNumField);

            } else {
                ASK_TYPE rv = askSourceOfPreps(rsList.size() > 0, colObjRSList.size() > 0, currPrepProvider);
                if (rv == ASK_TYPE.ChooseRS) {
                    recordSet = RecordSetTask.askForRecordSet(CollectionObject.getClassTableId(), rsList);

                } else if (rv == ASK_TYPE.EnterDataObjs) {
                    recordSet = task.askForDataObjRecordSet(CollectionObject.class, catNumField);

                } else if (rv == ASK_TYPE.None) {
                    recordSet = null;
                    isEmptyAcc = true;
                } else if (rv == ASK_TYPE.Cancel) {
                    if (viewable != null) {
                        viewable.setNewObject(null);
                    }
                    return;
                }
            }
        }

        if (recordSet == null && !isEmptyAcc) {
            return;
        }

        if (isEmptyAcc) {
            PrepLoaderSQL prepLoaderSQL = new PrepLoaderSQL(null, recordSet, infoRequest, isFor);
            prepLoaderSQL.execute();
        } else {
            DBTableIdMgr.getInstance().getInClause(recordSet);

            DBTableInfo tableInfo = DBTableIdMgr.getInstance().getInfoById(recordSet.getDbTableId());

            DataProviderFactory.getInstance().evict(tableInfo.getClassObj()); // XXX Not sure if this is really needed

            DataProviderSessionIFace session = null;
            try {
                session = DataProviderFactory.getInstance().createSession();

                // OK, it COULD be a RecordSet contain one or more InfoRequest, 
                // we will only accept an RS with one InfoRequest
                if (infoRequest == null && recordSet.getDbTableId() == InfoRequest.getClassTableId()) {
                    if (recordSet.getNumItems() == 1) {
                        RecordSetItemIFace item = recordSet.getOnlyItem();
                        if (item != null) {
                            InfoRequest infoReq = session.get(InfoRequest.class, item.getRecordId().intValue());
                            if (infoReq != null) {
                                createOrAdd(null, infoReq, infoReq.getRecordSets().iterator().next());

                            } else {
                                // error about missing info request
                                // Error Dialog
                            }
                        } else {
                            // error about item being null for some unbelievable reason 
                            // Error Dialog
                        }
                    } else {
                        // error about item having more than one or none
                        // Error Dialog
                    }
                    return;
                }

                // OK, here we have a recordset of CollectionObjects
                // First we process all the CollectionObjects in the RecordSet
                // and create a list of Preparations that can be loaned
                String sqlStr = DBTableIdMgr.getInstance().getQueryForTable(recordSet);
                if (StringUtils.isNotBlank(sqlStr)) {

                    //CACA 
                    final JStatusBar statusBar = UIRegistry.getStatusBar();
                    statusBar.setIndeterminate(LOAN_LOADR, true);

                    if (recordSet.getNumItems() > 2) {
                        UIRegistry.writeSimpleGlassPaneMsg(getResourceString("NEW_INTER_LOADING_PREP"), 24);
                    }

                    PrepLoaderSQL prepLoaderSQL = new PrepLoaderSQL(currPrepProvider, recordSet, infoRequest,
                            isFor);
                    prepLoaderSQL.addPropertyChangeListener(new PropertyChangeListener() {
                        public void propertyChange(PropertyChangeEvent evt) {
                            log.debug(evt.getNewValue());
                            if ("progress".equals(evt.getPropertyName())) {
                                statusBar.setValue(LOAN_LOADR, (Integer) evt.getNewValue());
                            }
                        }
                    });
                    prepLoaderSQL.execute();

                } else {
                    log.error("Query String empty for RecordSet tableId[" + recordSet.getDbTableId() + "]");
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(InteractionsProcessor.class, ex);
            }
        }
    }

    /**
     * @param rs
     * @param prepProvider
     */
    protected void cosLoaded(final RecordSetIFace rs, final T prepProvider) {
        //System.out.println("Adding cos to accession...");
        task.addCosToAcc(prepProvider, rs, viewable);
    }

    /**
     * @param coToPrepHash
     * @param prepTypeHash
     * @param prepProvider
     * @param infoRequest
     * @param session
     */
    protected void prepsLoaded(final Hashtable<Integer, ColObjInfo> coToPrepHash,
            final Hashtable<Integer, String> prepTypeHash, final T prepProvider, final InfoRequest infoRequest) {
        if (coToPrepHash.size() == 0 || prepTypeHash.size() == 0) {
            UIRegistry.showLocalizedMsg("NEW_INTER_NO_PREPS_TITLE", "NEW_INTER_NO_PREPS");
            return;
        }

        final DBTableInfo ti = DBTableIdMgr.getInstance().getInfoById(tableId);

        final SelectPrepsDlg loanSelectPrepsDlg = new SelectPrepsDlg(coToPrepHash, prepTypeHash, ti.getTitle());
        loanSelectPrepsDlg.createUI();
        loanSelectPrepsDlg.setModal(true);

        UIHelper.centerAndShow(loanSelectPrepsDlg);

        if (loanSelectPrepsDlg.isCancelled()) {
            if (viewable != null) {
                viewable.setNewObject(null);
            }
            return;
        }

        final Hashtable<Integer, Integer> prepsHash = loanSelectPrepsDlg.getSelection();
        if (prepsHash.size() > 0) {
            final SwingWorker worker = new SwingWorker() {
                @Override
                public Object construct() {
                    JStatusBar statusBar = UIRegistry.getStatusBar();
                    statusBar.setIndeterminate("INTERACTIONS", true);
                    statusBar.setText(getLocalizedMessage("CREATING_INTERACTION", ti.getTitle()));

                    if (isFor == forLoan) {
                        task.addPrepsToLoan(prepProvider, infoRequest, prepsHash, viewable);
                    } else {
                        task.addPrepsToGift(prepProvider, infoRequest, prepsHash, viewable);
                    }
                    return null;
                }

                //Runs on the event-dispatching thread.
                @Override
                public void finished() {
                    JStatusBar statusBar = UIRegistry.getStatusBar();
                    statusBar.setProgressDone("INTERACTIONS");
                    statusBar.setText("");
                }
            };
            worker.start();
        }
    }

    /**
     * Creates a new loan from a InfoRequest.
     * @param infoRequest the infoRequest to use to create the loan
     */
    public void createFromInfoRequest(final InfoRequest infoRequest) {
        RecordSetIFace rs = null;
        DataProviderSessionIFace session = DataProviderFactory.getInstance().createSession();
        try {
            session.attach(infoRequest);
            rs = infoRequest.getRecordSets().iterator().next();

        } catch (Exception ex) {
            ex.printStackTrace();
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(InteractionsProcessor.class, ex);
            // Error Dialog

        } finally {
            if (session != null) {
                session.close();
            }
        }

        if (rs != null) {
            createOrAdd(null, infoRequest, rs);
        }
    }

    //--------------------------------------------------------------
    // Background loader class for loading a large number of loan preparations
    //--------------------------------------------------------------
    class PrepLoaderSQL extends javax.swing.SwingWorker<Integer, Integer> {
        private final String PROGRESS = "progress";

        private RecordSetIFace recordSet;
        private T prepsProvider;
        private InfoRequest infoRequest;
        private int isFor;

        private Hashtable<Integer, String> prepTypeHash = new Hashtable<Integer, String>();
        private Hashtable<Integer, ColObjInfo> coToPrepHash = new Hashtable<Integer, ColObjInfo>();

        /**
         * @param prepsProvider
         * @param recordSet
         * @param infoRequest
         */
        public PrepLoaderSQL(final T prepsProvider, final RecordSetIFace recordSet, final InfoRequest infoRequest,
                final int isFor) {
            this.recordSet = recordSet;
            this.prepsProvider = prepsProvider;
            this.infoRequest = infoRequest;
            this.isFor = isFor;
        }

        /**
         * @param val
         * @return
         */
        private Integer getInt(final Object val) {
            return val == null ? 0 : (Integer) val;
        }

        /**
         * @return a List of rows that have the CollectionObject info from the rcordset
         */
        protected Vector<Object[]> getColObjsFromRecordSet() {
            String sql = "SELECT co.CollectionObjectID, co.CatalogNumber, tx.FullName FROM determination as dt INNER JOIN collectionobject as co ON dt.CollectionObjectID = co.CollectionObjectID "
                    + "INNER JOIN taxon as tx ON dt.TaxonID = tx.TaxonID WHERE isCurrent <> 0 AND dt.CollectionMemberID = COLMEMID "
                    + "AND co.CollectionObjectID " + DBTableIdMgr.getInstance().getInClause(recordSet);
            sql = QueryAdjusterForDomain.getInstance().adjustSQL(sql);
            log.debug(sql);

            Vector<Object[]> fullItems = BasicSQLUtils.query(sql);
            if (fullItems.size() != recordSet.getNumItems()) {
                sql = "SELECT CollectionObjectID, CatalogNumber FROM collectionobject WHERE CollectionMemberID = COLMEMID "
                        + "AND CollectionObjectID " + DBTableIdMgr.getInstance().getInClause(recordSet);
                Vector<Object[]> partialItems = BasicSQLUtils
                        .query(QueryAdjusterForDomain.getInstance().adjustSQL(sql));
                partialItems.addAll(fullItems);
                return partialItems;
            }
            return fullItems;
        }

        /**
         * @return
         */
        protected int collectForLoan() {
            int total = 0;
            int count = 0;
            try {
                Vector<Object[]> coIdRows = getColObjsFromRecordSet();
                total = coIdRows.size() * 2;
                if (coIdRows.size() != 0) {
                    UIRegistry.getStatusBar().setProgressRange(LOAN_LOADR, 0, Math.min(count, total));

                    // Get Preps with Gifts
                    StringBuilder sb = new StringBuilder();
                    sb.append("SELECT co.CollectionObjectID, p.PreparationID, gp.Quantity "
                            + "FROM preparation AS p INNER JOIN collectionobject AS co ON p.CollectionObjectID = co.CollectionObjectID "
                            + "INNER JOIN giftpreparation AS gp ON p.PreparationID = gp.PreparationID "
                            + "WHERE co.CollectionMemberID = COLMEMID AND co.CollectionObjectID in (");
                    for (Object[] row : coIdRows) {
                        count++;
                        if ((count % 10) == 0)
                            firePropertyChange(PROGRESS, 0, count);

                        Integer coId = (Integer) row[0];
                        sb.append(coId);
                        sb.append(',');

                        if (row[1] != null) {
                            coToPrepHash.put(coId, new ColObjInfo(coId, row[1].toString(),
                                    row.length == 3 ? row[2].toString() : null));
                        }
                    }
                    sb.setLength(sb.length() - 1); // chomp last comma
                    sb.append(')');

                    // Get a hash contain a mapping from PrepId to Gift Quantity
                    Hashtable<Integer, Integer> prepIdToGiftQnt = new Hashtable<Integer, Integer>();

                    String sql = QueryAdjusterForDomain.getInstance().adjustSQL(sb.toString());
                    log.debug(sql);

                    Vector<Object[]> rows = BasicSQLUtils.query(sql);
                    if (rows.size() > 0) {
                        for (Object[] row : rows) {
                            prepIdToGiftQnt.put((Integer) row[1], (Integer) row[2]);
                        }
                    }

                    // Now get the Preps With Loans
                    sb = new StringBuilder();
                    sb.append("SELECT p.PreparationID, p.CountAmt, lp.Quantity, lp.QuantityResolved, "
                            + "co.CollectionObjectID, pt.PrepTypeID, pt.Name "
                            + "FROM preparation AS p INNER JOIN collectionobject AS co ON p.CollectionObjectID = co.CollectionObjectID "
                            + "INNER JOIN preptype AS pt ON p.PrepTypeID = pt.PrepTypeID "
                            + "LEFT OUTER JOIN loanpreparation AS lp ON p.PreparationID = lp.PreparationID "
                            + "WHERE pt.IsLoanable <> 0 AND co.CollectionObjectID in (");
                    for (Object[] row : coIdRows) {
                        sb.append(row[0]);
                        sb.append(',');
                    }
                    sb.setLength(sb.length() - 1); // chomp last comma
                    sb.append(") ORDER BY co.CatalogNumber ASC");

                    // Get the Preps and Qty
                    sql = QueryAdjusterForDomain.getInstance().adjustSQL(sb.toString());
                    log.debug(sql);

                    rows = BasicSQLUtils.query(sql);
                    if (rows.size() > 0) {
                        for (Object[] row : rows) {
                            count++;
                            if ((count % 10) == 0)
                                firePropertyChange(PROGRESS, 0, Math.min(count, total));

                            int prepId = getInt(row[0]);
                            int pQty = getInt(row[1]);
                            int qty = getInt(row[2]);
                            int qtyRes = getInt(row[3]);
                            int coId = getInt(row[4]);

                            prepTypeHash.put((Integer) row[5], row[6].toString());

                            pQty -= getInt(prepIdToGiftQnt.get(prepId));

                            ColObjInfo colObjInfo = coToPrepHash.get(coId);
                            if (colObjInfo == null) {
                                // error
                            }

                            if (colObjInfo != null) {
                                PrepInfo prepInfo = colObjInfo.get(prepId);
                                if (prepInfo != null) {
                                    prepInfo.add(qty, qtyRes);
                                } else {
                                    colObjInfo.add(new PrepInfo(prepId, (Integer) row[5], pQty, qty, qtyRes));
                                }
                            }
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(InteractionsProcessor.class, ex);

            }
            firePropertyChange(PROGRESS, 0, total);
            UIRegistry.getStatusBar().setIndeterminate(LOAN_LOADR, true);
            return 0;
        }

        /**
         * @return
         */
        protected int collectForAcc() {
            return recordSet == null ? 0 : recordSet.getItems().size();
        }

        /**
         * @return
         */
        protected int collectForGift() {
            int total = 0;
            int count = 0;
            try {
                Vector<Object[]> coIdRows = getColObjsFromRecordSet();
                if (coIdRows.size() != 0) {
                    UIRegistry.getStatusBar().setProgressRange(LOAN_LOADR, 0, total);

                    // Get Preps with Loans
                    StringBuilder sb = new StringBuilder();
                    sb.append("SELECT p.PreparationID, lp.Quantity, lp.QuantityResolved "
                            + "FROM preparation AS p INNER JOIN collectionobject AS co ON p.CollectionObjectID = co.CollectionObjectID "
                            + "INNER JOIN loanpreparation AS lp ON p.PreparationID = lp.PreparationID "
                            + "WHERE co.CollectionMemberID = COLMEMID AND co.CollectionObjectID in (");
                    for (Object[] row : coIdRows) {
                        count++;
                        if ((count % 10) == 0)
                            firePropertyChange(PROGRESS, 0, Math.min(count, total));

                        Integer coId = (Integer) row[0];
                        sb.append(coId);
                        sb.append(',');

                        if (row[1] != null) {
                            coToPrepHash.put(coId, new ColObjInfo(coId, row[1].toString(),
                                    row.length == 3 ? row[2].toString() : null));
                        }
                    }
                    sb.setLength(sb.length() - 1); // chomp last comma
                    sb.append(')');

                    // Get a hash contain a mapping from PrepId to Gift Quantity
                    Hashtable<Integer, Integer> prepIdToLoanQnt = new Hashtable<Integer, Integer>();

                    String sql = QueryAdjusterForDomain.getInstance().adjustSQL(sb.toString());
                    log.debug(sql);

                    Vector<Object[]> rows = BasicSQLUtils.query(sql);
                    if (rows.size() > 0) {
                        for (Object[] row : rows) {
                            int qty = getInt(row[1]);
                            int qtyRes = getInt(row[2]);
                            prepIdToLoanQnt.put((Integer) row[0], qty - qtyRes);
                        }
                    }

                    // Now get the Preps With Gift
                    sb = new StringBuilder();
                    sb.append("SELECT p.PreparationID, p.CountAmt, gp.Quantity, "
                            + "co.CollectionObjectID, pt.PrepTypeID, pt.Name "
                            + "FROM preparation AS p INNER JOIN collectionobject AS co ON p.CollectionObjectID = co.CollectionObjectID "
                            + "INNER JOIN preptype AS pt ON p.PrepTypeID = pt.PrepTypeID "
                            + "LEFT OUTER JOIN giftpreparation AS gp ON p.PreparationID = gp.PreparationID "
                            + "WHERE pt.IsLoanable <> 0 AND co.CollectionObjectID in (");
                    for (Object[] row : coIdRows) {
                        sb.append(row[0]);
                        sb.append(',');
                    }
                    sb.setLength(sb.length() - 1); // chomp last comma
                    sb.append(") ORDER BY co.CatalogNumber ASC");

                    // Get the Preps and Qty
                    sql = QueryAdjusterForDomain.getInstance().adjustSQL(sb.toString());
                    log.debug(sql);

                    rows = BasicSQLUtils.query(sql);
                    if (rows.size() > 0) {
                        for (Object[] row : rows) {
                            int prepId = getInt(row[0]);

                            count++;
                            if ((count % 10) == 0)
                                firePropertyChange(PROGRESS, 0, Math.min(count, total));

                            int pQty = getInt(row[1]);
                            int qty = getInt(row[2]);
                            int coId = getInt(row[3]);

                            prepTypeHash.put((Integer) row[4], row[5].toString());

                            pQty -= getInt(prepIdToLoanQnt.get(prepId));

                            ColObjInfo colObjInfo = coToPrepHash.get(coId);
                            if (colObjInfo == null) {
                                // error
                            }

                            if (colObjInfo != null) {
                                PrepInfo prepInfo = colObjInfo.get(prepId);
                                if (prepInfo != null) {
                                    prepInfo.add(qty, qty);
                                } else {
                                    colObjInfo.add(new PrepInfo(prepId, (Integer) row[4], pQty, qty, 0));
                                }
                            }
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(InteractionsProcessor.class, ex);

            }
            firePropertyChange(PROGRESS, 0, total);
            UIRegistry.getStatusBar().setIndeterminate(LOAN_LOADR, true);
            return 0;
        }

        /* (non-Javadoc)
         * @see javax.swing.SwingWorker#doInBackground()
         */
        @Override
        protected Integer doInBackground() throws Exception {
            coToPrepHash = new Hashtable<Integer, ColObjInfo>();

            return isFor == forLoan ? collectForLoan() : (isFor == forGift ? collectForGift() : collectForAcc());
        }

        /* (non-Javadoc)
         * @see javax.swing.SwingWorker#done()
         */
        @Override
        protected void done() {
            super.done();

            UIRegistry.getStatusBar().setProgressDone(LOAN_LOADR);

            if (recordSet != null && recordSet.getNumItems() > 2) {
                UIRegistry.clearSimpleGlassPaneMsg();
            }

            if (isFor == forAcc) {
                cosLoaded(recordSet, prepsProvider);
            } else {
                prepsLoaded(coToPrepHash, prepTypeHash, prepsProvider, infoRequest);
            }
        }

    }

}