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

Java tutorial

Introduction

Here is the source code for edu.ku.brc.specify.tasks.QueryTask.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.helpers.XMLHelper.getAttr;
import static edu.ku.brc.ui.UIRegistry.getResourceString;

import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.lang.ref.SoftReference;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.SwingUtilities;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Element;

import com.thoughtworks.xstream.XStream;

import edu.ku.brc.af.auth.BasicPermisionPanel;
import edu.ku.brc.af.auth.PermissionEditorIFace;
import edu.ku.brc.af.core.AppContextMgr;
import edu.ku.brc.af.core.AppResourceIFace;
import edu.ku.brc.af.core.ContextMgr;
import edu.ku.brc.af.core.DroppableNavBox;
import edu.ku.brc.af.core.NavBox;
import edu.ku.brc.af.core.NavBoxIFace;
import edu.ku.brc.af.core.NavBoxItemIFace;
import edu.ku.brc.af.core.NavBoxMgr;
import edu.ku.brc.af.core.SubPaneIFace;
import edu.ku.brc.af.core.SubPaneMgr;
import edu.ku.brc.af.core.ToolBarItemDesc;
import edu.ku.brc.af.core.UsageTracker;
import edu.ku.brc.af.core.db.DBFieldInfo;
import edu.ku.brc.af.core.db.DBRelationshipInfo;
import edu.ku.brc.af.core.db.DBTableIdMgr;
import edu.ku.brc.af.core.db.DBTableInfo;
import edu.ku.brc.af.prefs.AppPreferences;
import edu.ku.brc.af.ui.db.ERTICaptionInfo;
import edu.ku.brc.af.ui.db.QueryForIdResultsIFace;
import edu.ku.brc.af.ui.db.ViewBasedDisplayDialog;
import edu.ku.brc.af.ui.forms.FormHelper;
import edu.ku.brc.af.ui.forms.MultiView;
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.helpers.XMLHelper;
import edu.ku.brc.specify.config.SpecifyAppContextMgr;
import edu.ku.brc.specify.conversion.BasicSQLUtils;
import edu.ku.brc.specify.datamodel.Collection;
import edu.ku.brc.specify.datamodel.DataModelObjBase;
import edu.ku.brc.specify.datamodel.RecordSet;
import edu.ku.brc.specify.datamodel.SpExportSchemaMapping;
import edu.ku.brc.specify.datamodel.SpQuery;
import edu.ku.brc.specify.datamodel.SpQueryField;
import edu.ku.brc.specify.datamodel.SpReport;
import edu.ku.brc.specify.datamodel.SpecifyUser;
import edu.ku.brc.specify.datamodel.Taxon;
import edu.ku.brc.specify.datamodel.TaxonTreeDef;
import edu.ku.brc.specify.datamodel.TaxonTreeDefItem;
import edu.ku.brc.specify.datamodel.TreeDefIface;
import edu.ku.brc.specify.datamodel.TreeDefItemIface;
import edu.ku.brc.specify.datamodel.Treeable;
import edu.ku.brc.specify.dbsupport.RecordTypeCodeBuilder;
import edu.ku.brc.specify.tasks.subpane.SQLQueryPane;
import edu.ku.brc.specify.tasks.subpane.qb.ERTICaptionInfoQB;
import edu.ku.brc.specify.tasks.subpane.qb.QBLiveDataSource;
import edu.ku.brc.specify.tasks.subpane.qb.QBQueryForIdResultsHQL;
import edu.ku.brc.specify.tasks.subpane.qb.QBReportInfoPanel;
import edu.ku.brc.specify.tasks.subpane.qb.QueryBldrPane;
import edu.ku.brc.specify.tasks.subpane.qb.QueryFieldPanel;
import edu.ku.brc.specify.tasks.subpane.qb.SearchResultReportServiceInfo;
import edu.ku.brc.specify.tasks.subpane.qb.TableTree;
import edu.ku.brc.specify.tasks.subpane.qb.TreeLevelQRI;
import edu.ku.brc.specify.tools.schemalocale.SchemaLocalizerDlg;
import edu.ku.brc.specify.ui.db.ResultSetTableModel;
import edu.ku.brc.specify.ui.treetables.TreeDefinitionEditor;
import edu.ku.brc.ui.ChooseFromListDlg;
import edu.ku.brc.ui.CommandAction;
import edu.ku.brc.ui.CommandDispatcher;
import edu.ku.brc.ui.CustomDialog;
import edu.ku.brc.ui.DataFlavorTableExt;
import edu.ku.brc.ui.IconManager;
import edu.ku.brc.ui.RolloverCommand;
import edu.ku.brc.ui.ToggleButtonChooserDlg;
import edu.ku.brc.ui.ToggleButtonChooserPanel;
import edu.ku.brc.ui.ToolBarDropDownBtn;
import edu.ku.brc.ui.UIHelper;
import edu.ku.brc.ui.UIRegistry;
import edu.ku.brc.ui.dnd.Trash;
import edu.ku.brc.util.Pair;

/**
 * This task will enable the user to create queries, save them and execute them.
 *
 * @code_status Alpha
 *
 * @author rods
 *
 */
public class QueryTask extends BaseTask {
    private static final Logger log = Logger.getLogger(QueryTask.class);

    // Static Data Members
    public static final String QUERY = "Query";
    public static final String SAVE_QUERY = "Save";
    public static final String REFRESH_QUERIES = "RefreshQueries";
    public static final String QUERY_RESULTS_REPORT = "QueryResultsReport";
    protected static final String XML_PATH_PREF = "Query.XML.Dir";

    public static final DataFlavor QUERY_FLAVOR = new DataFlavor(QueryTask.class, QUERY);

    protected QueryBldrPane queryBldrPane = null;
    protected SoftReference<TableTree> tableTree = null;
    protected SoftReference<Hashtable<String, TableTree>> tableTreeHash = null;
    protected final AtomicBoolean configurationHasChanged = new AtomicBoolean(false);

    protected Vector<ToolBarDropDownBtn> tbList = new Vector<ToolBarDropDownBtn>();
    protected Vector<JComponent> menus = new Vector<JComponent>();
    protected Vector<NavBoxIFace> extendedNavBoxes = new Vector<NavBoxIFace>();
    protected DroppableNavBox navBox = null;
    protected NavBox actionNavBox = null;

    protected Vector<String> favQueries = new Vector<String>();
    protected List<String> freqQueries;
    protected List<String> extraQueries;
    protected List<String> stdQueries = new ArrayList<String>();
    protected int nonFavCount = 0;

    //protected List<DBTableInfo>               tableInfos       = new ArrayList<DBTableInfo>();

    /**
     * Default Constructor
     *
     */
    public QueryTask() {
        this(QUERY, getResourceString(QUERY));
    }

    /**
     * Constructor.
     */
    public QueryTask(final String name, final String title) {
        super(name, title);

        CommandDispatcher.register(name, this);
        CommandDispatcher.register(TreeDefinitionEditor.TREE_DEF_EDITOR, this);
        CommandDispatcher.register(SchemaLocalizerDlg.SCHEMA_LOCALIZER, this);
    }

    /**
     * Ask the user for information needed to fill in the data object. (Could be refactored with WorkBench Task)
     * @param data the data object
     * @return true if OK, false if cancelled
     */
    public static boolean askUserForInfo(final String viewSetName, final String dlgTitle, final SpQuery query) {
        ViewBasedDisplayDialog editorDlg = new ViewBasedDisplayDialog((Frame) UIRegistry.getTopWindow(), "Global",
                viewSetName, null, dlgTitle, getResourceString("OK"), null, // className,
                null, // idFieldName,
                true, // isEdit,
                MultiView.HIDE_SAVE_BTN);

        editorDlg.preCreateUI();
        editorDlg.setData(query);
        editorDlg.getMultiView().preValidate();
        editorDlg.setModal(true);
        editorDlg.setVisible(true);

        if (!editorDlg.isCancelled()) {
            editorDlg.getMultiView().getDataFromUI();
        }
        editorDlg.dispose();

        return !editorDlg.isCancelled();
    }

    /**
     * Creates a new Query Data Object.
     * @param tableInfo the table information
     * @return the query
     */
    protected SpQuery createNewQueryDataObj(final DBTableInfo tableInfo) {
        SpQuery query = new SpQuery();
        query.initialize();
        query.setSpecifyUser(AppContextMgr.getInstance().getClassObject(SpecifyUser.class));
        query.setName(String.format(getResourceString("QB_NEW_QUERY_NAME"), tableInfo.getTitle()));
        query.setNamed(false);
        query.setContextTableId((short) tableInfo.getTableId());
        query.setContextName(tableInfo.getShortClassName());
        return query;
    }

    /**
     * Creates pane and executes a query.
     * @param sqlStr SQL to be executed
     */
    public void createAndExecute(final String sqlStr) {
        UsageTracker.incrUsageCount("QB.EXE");

        SQLQueryPane sqlPane = new SQLQueryPane(name, this, false, false);//true, true);
        addSubPaneToMgr(sqlPane);
        sqlPane.setSQLStr(sqlStr);
        sqlPane.doQuery();
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.core.BaseTask#getStarterPane()
     */
    @Override
    public SubPaneIFace getStarterPane() {
        return starterPane = StartUpTask.createFullImageSplashPanel(title, this);
    }

    //-------------------------------------------------------
    // Taskable
    //-------------------------------------------------------

    /* (non-Javadoc)
     * @see edu.ku.brc.af.tasks.BaseTask#preInitialize()
     */
    @Override
    public void preInitialize() {
        // Create and add the Actions NavBox first so it is at the top at the top
        actionNavBox = new NavBox(getActionNavBoxTitle());
        addNewQCreators();
    }

    /**
     * @return title for the action nav box
     */
    protected String getActionNavBoxTitle() {
        return getResourceString("QB_CREATE_QUERY");
    }

    /**
     * Reads the Query Lists from the database.
     */
    protected void readOrgLists() {
        freqQueries = readResourceForList("QueryFreqList");
        extraQueries = readResourceForList("QueryExtraList");
    }

    protected List<String> filterQueryList(List<String> list, boolean doSecurity, boolean doVisibility) {
        List<String> result = new ArrayList<String>();
        for (String q : list) {
            DBTableInfo tbl = DBTableIdMgr.getInstance().getByShortClassName(q);
            if (tbl != null && (!doVisibility || !tbl.isHidden())
                    && (!doSecurity || tbl.getPermissions().canView())) {
                result.add(q);
            }
        }
        return result;
    }

    /**
     * Reads a single list from the database.
     * @param resName the name of the resource to use to save it.
     * @return the list 
     */
    @SuppressWarnings("unchecked")
    protected Vector<String> readResourceForList(final String resourceName) {
        Vector<String> list = null;
        String xmlStr = null;
        AppResourceIFace appRes = AppContextMgr.getInstance().getResourceFromDir("Personal", resourceName);
        if (appRes != null) {
            xmlStr = appRes.getDataAsString();

        } else {
            // Get the default resource by name and copy it to a new User Area Resource
            AppResourceIFace newAppRes = AppContextMgr.getInstance().copyToDirAppRes("Personal", resourceName);
            if (newAppRes != null) {
                // Save it in the User Area
                AppContextMgr.getInstance().saveResource(newAppRes);
                xmlStr = newAppRes.getDataAsString();
            } else {
                if (resourceName.equals("QueryFreqList") || resourceName.equals("QueryExtraList")) {
                    ((SpecifyAppContextMgr) AppContextMgr.getInstance())
                            .addDiskResourceToAppDir(SpecifyAppContextMgr.DISCPLINEDIR, resourceName);
                    newAppRes = AppContextMgr.getInstance().copyToDirAppRes("Personal", resourceName);
                    if (newAppRes != null) {
                        // Save it in the User Area
                        AppContextMgr.getInstance().saveResource(newAppRes);
                        xmlStr = newAppRes.getDataAsString();
                    } else {
                        xmlStr = "";
                    }
                } else {
                    xmlStr = "";
                }
            }
        }

        if (StringUtils.isNotEmpty(xmlStr)) {
            XStream xstream = new XStream();
            list = (Vector<String>) xstream.fromXML(xmlStr);
        }
        //log.debug(xmlStr);

        if (list == null) {
            list = new Vector<String>();
        }
        return list;
    }

    /**
     * Saves a single list to the database.
     * @param resourceName the name of the resource to use to save it.
     * @param list the list to be saved.
     */
    protected void saveQueryList(final String resourceName, final List<String> list) {
        XStream xstream = new XStream();
        AppResourceIFace uaAppRes = AppContextMgr.getInstance().getResourceFromDir("Personal", resourceName);
        if (uaAppRes != null) {
            uaAppRes.setDataAsString(xstream.toXML(list));
            AppContextMgr.getInstance().saveResource(uaAppRes);

        } else {
            AppContextMgr.getInstance().putResourceAsXML(resourceName, xstream.toXML(list));
        }
    }

    /**
     * Saves the Lists to the database.
     */
    protected void saveQueryListConfiguration() {
        saveQueryList("QueryFreqList", freqQueries);
        saveQueryList("QueryExtraList", extraQueries);
    }

    /**
     * Configure the Query Creators.
     */
    protected void configureCreatorQueries() {
        List<String> filteredFreqs = filterQueryList(freqQueries, AppContextMgr.isSecurityOn(), true);
        List<String> filteredExtras = filterQueryList(extraQueries, AppContextMgr.isSecurityOn(), true);
        QueryCreatorsConfigureDlg dlg = new QueryCreatorsConfigureDlg(QueryTask.this, filteredFreqs, filteredExtras,
                filterQueryList(stdQueries, AppContextMgr.isSecurityOn(), true));
        UIHelper.centerAndShow(dlg);
        if (!dlg.isCancelled()) {
            actionNavBox.clear();

            freqQueries.removeAll(filteredFreqs);
            for (String freq : dlg.getFreqQueries()) {
                freqQueries.add(freq);
            }
            extraQueries.removeAll(filteredExtras);
            for (String extra : dlg.getExtraQueries()) {
                extraQueries.add(extra);
            }

            buildNavBoxes(filterQueryList(freqQueries, AppContextMgr.isSecurityOn(), true),
                    filterQueryList(extraQueries, AppContextMgr.isSecurityOn(), true));

            actionNavBox.validate();
            actionNavBox.doLayout();
            NavBoxMgr.getInstance().validate();
            NavBoxMgr.getInstance().doLayout();
            NavBoxMgr.getInstance().repaint();

            // Persist out to database
            saveQueryListConfiguration();
        }
    }

    /**
     * Confgiure Queries that show in the sidebar.
     */
    protected void configureFavoriteQueries() {
        QueryConfigureDlg dlg = new QueryConfigureDlg(QueryTask.this);
        UIHelper.centerAndShow(dlg);
        if (!dlg.isCancelled()) {
            Vector<SpQuery> favs = dlg.getFavQueries();
            Vector<SpQuery> extras = dlg.getOtherQueries();

            Hashtable<Integer, Integer> hash = new Hashtable<Integer, Integer>();
            for (SpQuery q : favs) {
                int id = q.getId();
                hash.put(id, id);
            }

            for (NavBoxItemIFace nbi : new Vector<NavBoxItemIFace>(navBox.getItems())) {
                RecordSetIFace rs = (RecordSetIFace) nbi.getData();
                if (rs != null) {
                    RecordSetItemIFace rsi = rs.getOnlyItem();

                    Integer idInt = rsi.getRecordId();

                    if (idInt != null) {
                        int id = idInt;
                        if (hash.get(id) == null) {
                            navBox.remove(nbi);
                        }
                    }
                }
            }

            hash.clear();
            for (NavBoxItemIFace nbi : navBox.getItems()) {
                RecordSetIFace rs = (RecordSetIFace) nbi.getData();
                if (rs != null) {
                    RecordSetItemIFace rsi = rs.getOnlyItem();
                    int id = rsi.getRecordId();
                    hash.put(id, id);
                }
            }

            for (SpQuery query : favs) {
                int id = query.getId();
                if (hash.get(id) == null) {
                    RecordSet rs = new RecordSet();
                    rs.initialize();
                    rs.set(query.getName(), SpQuery.getClassTableId(), RecordSet.GLOBAL);
                    rs.addItem(query.getSpQueryId());
                    addToNavBox(rs);
                }
            }
            // Persist out to database
            DataProviderSessionIFace session = null;
            try {
                session = DataProviderFactory.getInstance().createSession();
                session.beginTransaction();
                short order = 0;
                for (SpQuery query : favs) {
                    query.setIsFavorite(true);
                    query.setOrdinal(order++);
                    session.saveOrUpdate(query);
                }
                for (SpQuery query : extras) {
                    query.setIsFavorite(false);
                    query.setOrdinal(null);
                    session.saveOrUpdate(query);
                }
                session.commit();

            } catch (Exception ex) {
                edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
                // XXX Error dialog
                session.rollback();
            } finally {
                if (session != null) {
                    session.close();
                }
            }

            // Now Reorder list
            Hashtable<Integer, NavBoxItemIFace> nbiHash = new Hashtable<Integer, NavBoxItemIFace>();
            for (NavBoxItemIFace nbi : navBox.getItems()) {
                RecordSetIFace rs = (RecordSetIFace) nbi.getData();
                if (rs != null) {
                    RecordSetItemIFace rsi = rs.getOnlyItem();
                    int id = rsi.getRecordId();
                    nbiHash.put(id, nbi);
                }
            }

            navBox.clear();
            Collections.sort(favs, new Comparator<SpQuery>() {
                public int compare(SpQuery q1, SpQuery q2) {
                    return q1.getOrdinal().compareTo(q2.getOrdinal());
                }
            });
            for (SpQuery query : favs) {
                int id = query.getId();
                navBox.add(nbiHash.get(id));
            }

            checkForOtherNavBtn();

            navBox.validate();
            navBox.doLayout();
            NavBoxMgr.getInstance().validate();
            NavBoxMgr.getInstance().doLayout();
            NavBoxMgr.getInstance().repaint();
        }
    }

    /**
     * Checks to see if the Other btn for "Other Queries" should be added.
     */
    protected void checkForOtherNavBtn() {
        // XXX Users will probably want to share queries??
        String sqlStr = "From SpQuery as sq Inner Join sq.specifyUser as user where sq.isFavorite = false AND user.specifyUserId = "
                + AppContextMgr.getInstance().getClassObject(SpecifyUser.class).getSpecifyUserId();
        DataProviderSessionIFace session = null;
        try {
            session = DataProviderFactory.getInstance().createSession();
            List<?> queries = session.getDataList(sqlStr);
            int count = 0;
            for (Iterator<?> iter = queries.iterator(); iter.hasNext();) {
                Object[] obj = (Object[]) iter.next();
                SpQuery query = (SpQuery) obj[0];
                if (!AppContextMgr.isSecurityOn() || DBTableIdMgr.getInstance()
                        .getInfoById(query.getContextTableId()).getPermissions().canView()) {
                    count = 1;
                    break;
                }
            }
            if (count > 0) {
                NavBoxItemIFace nbi = NavBox.createBtnWithTT(getResourceString("QY_OTHER_QUERIES"), name,
                        getResourceString("QY_OTHER_QUERIES_TT"), IconManager.STD_ICON_SIZE, new ActionListener() {
                            public void actionPerformed(ActionEvent e) {
                                showOtherViewsDlg();
                            }
                        });

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

    /*
     * (non-Javadoc)
     * 
     * @see edu.ku.brc.af.tasks.BaseTask#isConfigurable()
     */
    @Override
    public boolean isConfigurable() {
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ku.brc.af.tasks.BaseTask#doConfigure()
     */
    @Override
    public void doConfigure() {
        UsageTracker.incrUsageCount("QB.CONFIG");

        String configCreators = UIRegistry.getResourceString("QY_CONFIGURE_CREATORS");
        String configQueries = UIRegistry.getResourceString("QY_CONFIGURE_QUERIES");

        Vector<String> configNames = new Vector<String>();
        Collections.addAll(configNames, configCreators, configQueries);

        ToggleButtonChooserDlg<String> dlg = new ToggleButtonChooserDlg<String>((Frame) UIRegistry.getTopWindow(),
                "QY_CHOOSE_CONFIG", configNames, ToggleButtonChooserPanel.Type.RadioButton);
        dlg.setUseScrollPane(true);
        dlg.setVisible(true);
        if (!dlg.isCancelled()) {
            if (dlg.getSelectedObject().equals(configCreators)) {
                configureCreatorQueries();
            } else {
                configureFavoriteQueries();
            }
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.tasks.BaseTask#getPopupMenu()
     */
    @Override
    public JPopupMenu getPopupMenu() {
        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem mi = new JMenuItem(UIRegistry.getResourceString("QY_CONFIGURE_CREATORS"));
        popupMenu.add(mi);

        mi.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                configureCreatorQueries();
            }
        });

        mi = new JMenuItem(UIRegistry.getResourceString("QY_CONFIGURE_QUERIES"));
        popupMenu.add(mi);

        mi.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                configureFavoriteQueries();
            }
        });

        mi = new JMenuItem(UIRegistry.getResourceString("QY_IMPORT_QUERIES"));
        popupMenu.add(mi);

        mi.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                importQueries();
            }
        });

        mi = new JMenuItem(UIRegistry.getResourceString("QY_EXPORT_QUERIES"));
        popupMenu.add(mi);

        mi.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                exportQueries();
            }
        });

        return popupMenu;
    }

    /**
     * Builds the NavBtns for the grequently used list and the extra list.
     * @param freqList the frequently used list of names
     * @param extraList the list of extra (hidden) names
     */
    protected void buildNavBoxes(final List<String> freqList, final List<String> extraList) {
        createCreateQueryNavBtns(freqList);

        if (extraList != null && !extraList.isEmpty()) {
            NavBoxItemIFace nbi = NavBox.createBtnWithTT(getResourceString("QY_EXTRA_TABLES"), "MoreTables",
                    getResourceString("QY_EXTRA_TABLES_TT"), IconManager.STD_ICON_SIZE, new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                            showMiscViewsDlg();
                        }
                    });

            actionNavBox.add(nbi);
        }

    }

    /**
     * Show a dialog letting them choose from a list of available misc views.   
     */
    protected void showMiscViewsDlg() {
        String shortClassName = null;
        List<String> extras = filterQueryList(extraQueries, AppContextMgr.isSecurityOn(), true);
        if (extras.size() == 1) {
            shortClassName = extras.get(0);
            DBTableInfo tableInfo = DBTableIdMgr.getInstance().getByShortClassName(shortClassName);
            if (tableInfo != null) {
                createNewQuery(tableInfo);
            }

        } else {
            final Hashtable<String, DBTableInfo> tiHash = new Hashtable<String, DBTableInfo>();
            Vector<String> names = new Vector<String>();

            for (String sName : extras) {
                DBTableInfo tableInfo = DBTableIdMgr.getInstance().getByShortClassName(sName);
                names.add(tableInfo.getTitle());
                tiHash.put(tableInfo.getTitle(), tableInfo);

            }
            ToggleButtonChooserDlg<String> dlg = new ToggleButtonChooserDlg<String>(
                    (Frame) UIRegistry.getTopWindow(), "QY_EXTRA_TABLES", names,
                    ToggleButtonChooserPanel.Type.RadioButton);
            dlg.setUseScrollPane(true);
            dlg.setVisible(true);
            if (!dlg.isCancelled()) {
                String sName = dlg.getSelectedObject();
                DBTableInfo tableInfo = tiHash.get(sName);
                if (tableInfo != null) {
                    createNewQuery(tableInfo);
                }
            }
        }
    }

    /**
     * Show a dialog letting them choose from a list of available misc views.   
     */
    protected void showOtherViewsDlg() {
        List<SpQuery> queryList = new Vector<SpQuery>();
        List<?> rows = null;
        DataProviderSessionIFace session = null;
        try {
            session = DataProviderFactory.getInstance().createSession();
            rows = session.getDataList(
                    "FROM SpQuery as sq Inner Join sq.specifyUser as user where sq.isFavorite = false AND user.specifyUserId = "
                            + AppContextMgr.getInstance().getClassObject(SpecifyUser.class).getSpecifyUserId()
                            + " ORDER BY sq.name");
        } finally {
            if (session != null) {
                session.close();
            }
        }

        if (rows.size() == 1) {
            final Object[] row = (Object[]) rows.iterator().next();
            new EditOtherQueryWorker(((SpQuery) row[0]).getId()).start();

        } else {
            for (Object obj : rows) {
                Object[] row = (Object[]) obj;
                queryList.add((SpQuery) row[0]);
            }
            ToggleButtonChooserDlg<SpQuery> dlg = new ToggleButtonChooserDlg<SpQuery>(
                    (Frame) UIRegistry.getTopWindow(), "QY_OTHER_QUERIES", queryList,
                    ToggleButtonChooserPanel.Type.RadioButton);
            dlg.setUseScrollPane(true);
            dlg.setVisible(true);
            if (!dlg.isCancelled()) {
                new EditOtherQueryWorker(dlg.getSelectedObject().getId()).start();
            }
        }
    }

    /**
     * Creates all the NavBtns from a list of Queries create names.
     * @param list the list of names
     */
    protected void createCreateQueryNavBtns(final List<String> list) {
        for (String shortClassName : list) {
            createQueryCreateNB(shortClassName);
        }
    }

    /**
     * Creates a single btn for a new query
     * @param shortClassName the class name
     */
    protected void createQueryCreateNB(final String shortClassName) {
        final DBTableInfo tableInfo = DBTableIdMgr.getInstance().getByShortClassName(shortClassName);
        actionNavBox.add(NavBox.createBtnWithTT(
                String.format(getResourceString("QB_CREATE_NEWQUERY"), tableInfo.getTitle()), tableInfo.getName(),
                // name,
                getResourceString("QB_CREATE_NEWQUERY_TT"), IconManager.STD_ICON_SIZE, new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        new NewQueryWorker(tableInfo).start();
                    }
                }));
    }

    /**
     * @param tblName
     * @return
     */
    protected boolean showQueryCreator(final String tblName) {
        //DBTableInfo tbl = DBTableIdMgr.getInstance().getByShortClassName(tblName);
        //return /*!tbl.isHidden() && */(!AppContextMgr.isSecurityOn() || tbl.getPermissions().canView());
        return true;
    }

    /**
     * Adds the NavBtns for creating new queries.
     */
    protected void addNewQCreators() {
        readOrgLists();
        buildNavBoxes(filterQueryList(freqQueries, AppContextMgr.isSecurityOn(), true),
                filterQueryList(extraQueries, AppContextMgr.isSecurityOn(), true));

        try {
            Element root = XMLHelper.readDOMFromConfigDir("querybuilder.xml");
            List<?> tableNodes = root.selectNodes("/database/table");
            for (Object obj : tableNodes) {
                String sName = XMLHelper.getAttr((Element) obj, "name", null);
                if (showQueryCreator(sName)) {
                    stdQueries.add(sName);
                }
            }
        } catch (Exception ex) {
            UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            ex.printStackTrace();
        }
    }

    /**
     * @return query type
     */
    protected String getQueryType() {
        return QUERY;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.specify.core.Taskable#initialize()
     */
    @Override
    public void initialize() {
        if (!isInitialized) {
            super.initialize(); // sets isInitialized to false

            navBox = new DroppableNavBox(getQueryNavBoxTitle(), QUERY_FLAVOR, getQueryType(), SAVE_QUERY);
            loadQueries();

            navBoxes.add(actionNavBox);
            navBoxes.add(navBox);

            registerServices();

        }
        isShowDefault = true;
    }

    /**
     * register services at initialization.
     */
    protected void registerServices() {
        ContextMgr.registerService(new ReportServiceInfo());
    }

    /**
     * @return title for the query nav box.
     */
    protected String getQueryNavBoxTitle() {
        return getResourceString("QUERIES");
    }

    /*
     *  (non-Javadoc)
     * @see edu.ku.brc.af.core.Taskable#getToolBarItems()
     */
    @Override
    public List<ToolBarItemDesc> getToolBarItems() {
        toolbarItems = new Vector<ToolBarItemDesc>();

        String label = getResourceString(name);
        String localIconName = name;
        String hint = getResourceString("search_hint");
        ToolBarDropDownBtn btn = createToolbarButton(label, localIconName, hint, menus);
        if (tbList.size() == 0) {
            tbList.add(btn);
        }
        toolbarItems.add(new ToolBarItemDesc(btn));

        return toolbarItems;

    }

    /**
     * Adds a Query to the Left Pane NavBox (Refactor this with Workbench)
     * @param query the Query to be added
     * @return the nav box
     */
    protected NavBoxItemIFace addToNavBox(final RecordSet recordSet) {
        //boolean canDelete = AppContextMgr.isSecurityOn() ? getPermissions().canDelete() : true;
        boolean canDelete = ((QueryTask) ContextMgr.getTaskByClass(QueryTask.class)).isPermitted();
        final RolloverCommand roc = (RolloverCommand) makeDnDNavBtn(navBox, recordSet.getName(), "Query", null,
                canDelete ? new CommandAction(getQueryType(), DELETE_CMD_ACT, recordSet) : null, true, true);
        roc.setToolTip(getResourceString("QY_CLICK2EDIT"));
        roc.setData(recordSet);
        roc.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                new EditQueryWorker(recordSet.getOnlyItem().getRecordId(), (RolloverCommand) e.getSource()).start();
            }
        });
        NavBoxItemIFace nbi = (NavBoxItemIFace) roc;

        DBTableInfo tblInfo = DBTableIdMgr.getInstance().getInfoById(recordSet.getTableId());
        if (tblInfo != null) {
            ImageIcon rsIcon = tblInfo.getIcon(IconManager.STD_ICON_SIZE);
            if (rsIcon != null) {
                nbi.setIcon(rsIcon);
            }
        }

        roc.addDragDataFlavor(new DataFlavorTableExt(getClass(), getQueryType(), recordSet.getTableId()));
        if (canDelete) {
            roc.addDragDataFlavor(Trash.TRASH_FLAVOR);
        }
        return nbi;
    }

    /**
     * @param query
     * @return true if query is associated with a SpExportSchemaMapping
     */
    public static boolean isSchemaExportQuery(SpQuery query) {
        return BasicSQLUtils
                .getCount("select count(*) from spexportschemaitemmapping mapping inner join spqueryfield qf "
                        + " on qf.spqueryfieldid = mapping.spqueryfieldid where qf.spqueryid = "
                        + query.getId()) > 0;
    }

    /**
     * @param query
     * @return true if query should be loaded.
     */
    protected boolean isLoadableQuery(SpQuery query) {
        return !isSchemaExportQuery(query);
    }

    /**
     * @return hql to retrieve queries for loading.
     */
    protected String getQueryLoaderHQL() {
        // XXX Users will probably want to share queries??
        return "From SpQuery as sq Inner Join sq.specifyUser as user where sq.isFavorite = true AND user.specifyUserId = "
                + AppContextMgr.getInstance().getClassObject(SpecifyUser.class).getSpecifyUserId()
                + " ORDER BY ordinal";
    }

    /**
     * @param session
     * @return list of possibly loadable queries.
     */
    protected List<?> getQueriesForLoading(DataProviderSessionIFace session) {
        return session.getDataList(getQueryLoaderHQL());
    }

    /**
     * Loads the Queries from the Database
     */
    protected void loadQueries() {
        DataProviderSessionIFace session = null;
        try {
            session = DataProviderFactory.getInstance().createSession();
            List<?> queries = getQueriesForLoading(session);

            for (Iterator<?> iter = queries.iterator(); iter.hasNext();) {
                Object obj = iter.next();
                SpQuery query = (SpQuery) (obj instanceof Object[] ? ((Object[]) obj)[0] : obj);
                if (!AppContextMgr.isSecurityOn() || DBTableIdMgr.getInstance()
                        .getInfoById(query.getContextTableId()).getPermissions().canView()) {
                    if (isLoadableQuery(query)) {
                        RecordSet rs = new RecordSet();
                        rs.initialize();
                        rs.set(query.getName(), SpQuery.getClassTableId(), RecordSet.GLOBAL);
                        rs.addItem(query.getSpQueryId());
                        addToNavBox(rs);
                    }
                }
            }

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

    /**
     * 
     */
    protected void createNewQuery(final DBTableInfo tableInfo) {
        if (queryBldrPane == null || queryBldrPane.aboutToShutdown()) {
            SpQuery query = createNewQueryDataObj(tableInfo);
            if (query != null) {
                editQuery(query);
            }
        }
    }

    /**
     * @param queryId
     */
    protected boolean editQuery(Integer queryId) {
        UsageTracker.incrUsageCount("QB.EDT");
        DataProviderSessionIFace session = DataProviderFactory.getInstance().createSession();
        try {
            Object dataObj = session.getData(SpQuery.class, "spQueryId", queryId,
                    DataProviderSessionIFace.CompareType.Equals);
            if (dataObj != null) {
                ((SpQuery) dataObj).forceLoad(true);
                SpExportSchemaMapping m = ((SpQuery) dataObj).getMapping();
                if (m != null) {
                    m.forceLoad(false);
                }
                return editQuery((SpQuery) dataObj);
            }
            return false;
        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            ex.printStackTrace();
            return false;
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.core.Taskable#requestContext()
     */
    @Override
    public void requestContext() {
        /* this method is complicated by a fix for bug 6252 (no starter pane created for QueryTask in some situations)
         * 
         * The reason for the problem probably has to do with QueryTask being a singletonPane. It could probably be solved
         * more cleanly/generally, but doing it here avoids possibility of creating problems for other tasks.
         * 
         */
        boolean goodStarterPane = starterPane != null
                && SubPaneMgr.getInstance().indexOfComponent(starterPane.getUIComponent()) > -1;
        boolean goodQueryPane = queryBldrPane != null
                && SubPaneMgr.getInstance().indexOfComponent(queryBldrPane.getUIComponent()) > -1;
        if (goodStarterPane || goodQueryPane) {
            ContextMgr.requestContext(this);
        }

        if (!goodStarterPane) {
            if (!goodQueryPane) {
                super.requestContext();

            } else {
                SubPaneMgr.getInstance().showPane(queryBldrPane);
            }
        } else {
            SubPaneMgr.getInstance().showPane(starterPane);
        }
    }

    /**
     * @param query
     * @return QueryBldrPane for new query
     */
    protected QueryBldrPane getNewQbPane(SpQuery query) throws QueryBuilderContextException {
        return new QueryBldrPane(query.getName(), this, query);
    }

    /**
     * @param query
     * @return true if query is not locked.
     * 
     * Locks query if necessary. 
     * 
     */
    protected boolean checkLock(final SpQuery query) {
        return true;
    }

    /**
     * @param query
     */
    protected boolean editQuery(final SpQuery query) {
        if (checkLock(query)) {
            try {
                QueryBldrPane newPane = getNewQbPane(query);
                if (starterPane != null) {
                    SubPaneMgr.getInstance().replacePane(starterPane, newPane);
                    starterPane = null;
                } else if (queryBldrPane != null) {
                    SubPaneMgr.getInstance().replacePane(queryBldrPane, newPane);
                }
                queryBldrPane = newPane;
                return true;
            } catch (QueryBuilderContextException e) {
                //e.printStackTrace();
                UIRegistry.displayErrorDlgLocalized("QueryTask.QUERY_CONTEXT_ERRMSG");
            }
        }
        return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.ku.brc.specify.core.Taskable#getNavBoxes()
     */
    @Override
    public java.util.List<NavBoxIFace> getNavBoxes() {
        initialize();

        extendedNavBoxes.clear();
        extendedNavBoxes.addAll(navBoxes);

        return extendedNavBoxes;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.tasks.BaseTask#isSingletonPane()
     */
    @Override
    public boolean isSingletonPane() {
        return true;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.core.Taskable#getTaskClass()
     */
    @Override
    public Class<? extends BaseTask> getTaskClass() {
        return this.getClass();
    }

    //-------------------------------------------------------
    // CommandListener Interface
    //-------------------------------------------------------

    /**
     * Save it out to persistent storage.
     * @param query the SpQuery
     */
    protected void persistQuery(final SpQuery query, final SpExportSchemaMapping schemaMapping) {
        // TODO Add StaleObject Code from FormView
        boolean saved = schemaMapping == null ? DataModelObjBase.saveWithError(true, query)
                : DataModelObjBase.saveWithError(true, query, schemaMapping);
        if (saved) {
            FormHelper.updateLastEdittedInfo(query);
        }
    }

    /**
     * Save a record set.
     * @param recordSets the rs to be saved
     */
    public RolloverCommand saveNewQuery(final SpQuery query, final SpExportSchemaMapping schemaMapping,
            final boolean enabled) {
        query.setTimestampCreated(new Timestamp(System.currentTimeMillis()));
        query.setSpecifyUser(AppContextMgr.getInstance().getClassObject(SpecifyUser.class));
        if (query.getIsFavorite() == null) {
            query.setIsFavorite(true);
        }

        persistQuery(query, schemaMapping);

        RecordSet rs = new RecordSet();
        rs.initialize();
        rs.set(query.getName(), SpQuery.getClassTableId(), RecordSet.GLOBAL);
        rs.addItem(query.getSpQueryId());

        RolloverCommand roc = (RolloverCommand) addToNavBox(rs);
        roc.setEnabled(enabled);

        NavBoxMgr.getInstance().addBox(navBox);

        // XXX this is pathetic and needs to be generized
        navBox.invalidate();
        navBox.setSize(navBox.getPreferredSize());
        navBox.doLayout();
        navBox.repaint();
        NavBoxMgr.getInstance().invalidate();
        NavBoxMgr.getInstance().doLayout();
        NavBoxMgr.getInstance().repaint();
        UIRegistry.forceTopFrameRepaint();

        String msg = String.format(getResourceString("WB_SAVED"), new Object[] { query.getName() });
        UIRegistry.getStatusBar().setText(msg);

        return roc;
    }

    /**
     * @param query
     * @param session
     * @throws Exception
     */
    protected void deleteThisQuery(SpQuery query, DataProviderSessionIFace session) throws Exception {
        //assumes caller handles transaction 
        session.delete(query);
    }

    /**
     * @param q
     * @param session
     * @return true if q has no associated reports or user confirms delete
     * @throws Exception
     */
    protected boolean okToDeleteQuery(final SpQuery q, DataProviderSessionIFace session) throws Exception {
        //assumes q is force-loaded
        if (q.getReports().size() > 0) {
            CustomDialog cd = new CustomDialog((Frame) UIRegistry.getTopWindow(),
                    UIRegistry.getResourceString("REP_CONFIRM_DELETE_TITLE"), true, CustomDialog.OKHELP,
                    new QBReportInfoPanel(q, UIRegistry.getResourceString("QB_UNDELETABLE_REPS")));
            cd.setHelpContext("QBUndeletableReps");
            UIHelper.centerAndShow(cd);
            cd.dispose();
            return false;
        }

        int option = JOptionPane.showOptionDialog(UIRegistry.getMostRecentWindow(),
                String.format(UIRegistry.getResourceString("REP_CONFIRM_DELETE"), q.getName()),
                UIRegistry.getResourceString("REP_CONFIRM_DELETE_TITLE"), JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE, null, null, JOptionPane.NO_OPTION);

        return option == JOptionPane.YES_OPTION;
    }

    /**
     * Delete a record set
     * @param rs the recordSet to be deleted
     */
    protected boolean deleteQuery(final RecordSet rs) {
        UsageTracker.incrUsageCount("QB.DEL");
        // delete from database
        DataProviderSessionIFace session = DataProviderFactory.getInstance().createSession();
        boolean transOpen = false;
        SpQuery query = session.get(SpQuery.class, rs.getOnlyItem().getRecordId());
        try {
            query.forceLoad(true);
            if (okToDeleteQuery(query, session)) {
                session.beginTransaction();
                transOpen = true;
                deleteThisQuery(query, session);
                session.commit();
                transOpen = false;
                return true;
            }

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            if (transOpen) {
                session.rollback();
            }
            ex.printStackTrace();
            log.error(ex);
        } finally {
            session.close();
        }
        return false;
    }

    /**
     * Delete the RecordSet from the UI, which really means remove the NavBoxItemIFace.
     * This method first checks to see if the boxItem is not null and uses that, if
     * it is null then it looks the box up by name and used that
     * @param boxItem the box item to be deleted
     * @param recordSets the record set that is "owned" by some UI object that needs to be deleted (used for secondary lookup
     */
    protected void deleteQueryFromUI(final NavBoxItemIFace boxItem, final RecordSet rs) {
        deleteDnDBtn(navBox, boxItem != null ? boxItem : getBoxByTitle(navBox, rs.getName()));
    }

    /**
    * Processes all Commands of type QUERY.
    * @param cmdAction the command to be processed
    */
    @SuppressWarnings("unchecked")
    protected void processQueryCommands(final CommandAction cmdAction) {
        if (cmdAction.isAction(DELETE_CMD_ACT) && cmdAction.getData() instanceof RecordSetIFace) {
            RecordSet recordSet = (RecordSet) cmdAction.getData();
            if (deleteQuery(recordSet)) {
                deleteQueryFromUI(null, recordSet);
            }
            return;
        }

        if (cmdAction.isAction(REFRESH_QUERIES)) {
            navBox.clear();
            loadQueries();
            navBox.invalidate();
            navBox.doLayout();
            navBox.repaint();
            NavBoxMgr.getInstance().invalidate();
            NavBoxMgr.getInstance().doLayout();
            NavBoxMgr.getInstance().repaint();
            UIRegistry.forceTopFrameRepaint();
            return;
        }

        if (cmdAction.isAction(QUERY_RESULTS_REPORT)) {
            SearchResultReportServiceInfo selectedRep = null;
            JTable dataTbl = (JTable) cmdAction.getProperties().get("jtable");
            if (dataTbl != null) {
                ResultSetTableModel rsm = (ResultSetTableModel) dataTbl.getModel();
                if (rsm != null) {
                    QueryForIdResultsIFace results = rsm.getResults();
                    QueryBldrPane qb = results instanceof QBQueryForIdResultsHQL
                            ? ((QBQueryForIdResultsHQL) results).getQueryBuilder()
                            : null;
                    int tableId = ((RecordSet) cmdAction.getData()).getDbTableId();
                    List<SearchResultReportServiceInfo> reps = new Vector<SearchResultReportServiceInfo>(
                            ((ReportsBaseTask) ContextMgr.getTaskByClass(ReportsTask.class)).getReports(tableId,
                                    qb));
                    if (reps.size() == 0) {
                        log.error("no reports for query. Should't have gotten here.");
                    } else if (rsm.isLoadingCells()) {
                        UIRegistry.writeTimedSimpleGlassPaneMsg(
                                UIRegistry.getResourceString("QB_NO_REPORTS_WHILE_LOADING_RESULTS"), 5000, null,
                                null, true);
                    } else {
                        ChooseFromListDlg<SearchResultReportServiceInfo> dlg = new ChooseFromListDlg<SearchResultReportServiceInfo>(
                                (Frame) UIRegistry.getTopWindow(),
                                UIRegistry.getResourceString("REP_CHOOSE_SP_REPORT"), reps);
                        dlg.setVisible(true);
                        if (dlg.isCancelled()) {
                            return;
                        }
                        selectedRep = dlg.getSelectedObject();
                        dlg.dispose();
                    }
                    if (selectedRep == null || selectedRep.getFileName() == null) {
                        return;
                    }

                    Object src;
                    if (selectedRep.isLiveData()) {
                        // XXX - probably a smoother way to handle these generic issues.
                        // (type safety warning)
                        List<? extends ERTICaptionInfo> captions = rsm.getResults().getVisibleCaptionInfo();
                        src = new QBLiveDataSource(rsm, (List<ERTICaptionInfoQB>) captions,
                                selectedRep.getRepeats());
                    } else {
                        int[] selectedRows = rsm.getParentERTP().getSelectedRows();
                        if (selectedRows != null && selectedRows.length == 0) {
                            selectedRows = null;
                        }
                        if (selectedRows == null) {
                            src = (RecordSet) cmdAction.getData();
                        } else {
                            src = rsm.getRecordSet(selectedRows, false);
                        }
                    }
                    final CommandAction cmd = new CommandAction(ReportsBaseTask.REPORTS,
                            ReportsBaseTask.PRINT_REPORT, src);
                    cmd.setProperty("title", rsm.getResults().getTitle());
                    cmd.setProperty("file", selectedRep.getFileName());
                    if (selectedRep.isRequiresNewConnection()) {
                        RecordSet repRS = new RecordSet();
                        repRS.initialize();
                        repRS.set(selectedRep.getReportName(), SpReport.getClassTableId(), RecordSet.GLOBAL);
                        repRS.addItem(selectedRep.getSpReportId());
                        cmd.setProperty("spreport", repRS);
                    }
                    CommandDispatcher.dispatch(cmd);
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.tasks.BaseTask#doProcessAppCommands(edu.ku.brc.ui.CommandAction)
     */
    @Override
    protected void doProcessAppCommands(CommandAction cmdAction) {
        super.doProcessAppCommands(cmdAction);

        if (cmdAction.isAction(APP_RESTART_ACT)) {
            configurationHasChanged.set(true);
            isInitialized = false;
            initialize();
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.specify.ui.CommandListener#doCommand(edu.ku.brc.specify.ui.CommandAction)
     */
    @Override
    public void doCommand(CommandAction cmdAction) {
        super.doCommand(cmdAction);

        if (cmdAction.isType(getQueryType())) {
            processQueryCommands(cmdAction);

        } else if (cmdAction.isType(TreeDefinitionEditor.TREE_DEF_EDITOR)) {
            //all we care to know is that a treeDefintion got changed somehow 
            this.configurationHasChanged.set(true);
        } else if (cmdAction.isType(SchemaLocalizerDlg.SCHEMA_LOCALIZER)) {
            //XXX should check whether changed schema actually is the schema in use? 
            // e.g. If German schema was saved when English is in use then ignore??
            this.configurationHasChanged.set(true);
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    if (SubPaneMgr.getInstance().getCurrentSubPane() == queryBldrPane) {
                        if (queryBldrPane != null) {
                            queryBldrPane.showingPane(true);
                        }
                    }
                }
            });
        }
    }

    //--------------------------------------------------------------
    // Inner Classes
    //--------------------------------------------------------------

    /**
     *
     * @author rods
     *
     */
    class QueryAction implements ActionListener {
        private String queryStr;
        private String viewSetName;
        private String viewName;

        public QueryAction(final String queryStr, final String viewSetName, final String viewName) {
            this.queryStr = queryStr;
            this.viewSetName = viewSetName;
            this.viewName = viewName;
        }

        public QueryAction(final String queryStr) {
            this(queryStr, null, null);
        }

        public QueryAction(final String viewSetName, final String viewName) {
            this(null, viewSetName, viewName);
        }

        public void actionPerformed(ActionEvent e) {
            if (StringUtils.isNotEmpty(queryStr)) {
                createAndExecute(queryStr);

            } else if (StringUtils.isNotEmpty(viewSetName) && StringUtils.isNotEmpty(viewName)) {
                //createSearchForm(viewSetName, viewName);
            }
        }
    }

    /**
     * Constructs tableTree and tableTreeHash members.
     */
    protected void bldTableTrees() {
        if (tableTree == null || tableTree.get() == null || needToRebuildTableTree()) {
            tableTreeHash = null;
            tableTree = new SoftReference<TableTree>(readTables());
        }
        if (tableTreeHash == null || tableTreeHash.get() == null || needToRebuildTableTree()) {
            tableTreeHash = new SoftReference<Hashtable<String, TableTree>>(buildTableTreeHash(tableTree.get()));
        }
        configurationHasChanged.set(false);
    }

    /**
     * @param tree
     * 
     * Clears tree of changes resulting from previous use.
     */
    protected void clearTableTree(final TableTree tree) {
        if (tree.getTableQRI() != null) {
            tree.getTableQRI().setIsInUse(false);
            for (int f = 0; f < tree.getTableQRI().getFields(); f++) {
                tree.getTableQRI().getField(f).setIsInUse(false);
            }
        }
        for (int k = 0; k < tree.getKids(); k++) {
            clearTableTree(tree.getKid(k));
        }
    }

    /**
     * @return tableTree paired with tableTreeHash
     */
    public synchronized Pair<TableTree, Hashtable<String, TableTree>> getTableTrees() {
        if (needToRebuildTableTree()) {
            bldTableTrees();
        } else {
            clearTableTree(tableTree.get());
        }
        return new Pair<TableTree, Hashtable<String, TableTree>>(tableTree.get(), tableTreeHash.get());

    }

    /**
     * @return true if the table tree objects need to be rebuilt.
     */
    public synchronized boolean needToRebuildTableTree() {
        return tableTree == null || tableTree.get() == null || tableTreeHash == null || tableTreeHash.get() == null
                || configurationHasChanged.get();
    }

    /**
     * @return TableTree defined by "querybuilder.xml" schema.
     */
    protected TableTree readTables() {
        TableTree treeRoot = new TableTree("root", "root", "root", null);
        try {
            Element root = XMLHelper.readDOMFromConfigDir("querybuilder.xml");
            List<?> tableNodes = root.selectNodes("/database/table");
            for (Object obj : tableNodes) {
                Element tableElement = (Element) obj;
                processForTables(tableElement, treeRoot);
            }
        } catch (Exception ex) {
            UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            ex.printStackTrace();
        }
        return treeRoot;
    }

    /**
     * @return
     */
    protected Integer getHostTaxonTreeDefId() {
        String hostTaxRelName = AppPreferences.getRemote().get("HostTaxonRelationshipName", null);
        if (hostTaxRelName != null) {
            String sql = "select RightSideCollectionID from collectionreltype where name='" + hostTaxRelName
                    + "' and " + "LeftSideCollectionID="
                    + AppContextMgr.getInstance().getClassObject(Collection.class).getId();
            Integer rightSideCollectionID = BasicSQLUtils.querySingleObj(sql);
            if (rightSideCollectionID != null) {
                sql = "select TaxonTreeDefID from discipline d inner join collection c on c.disciplineid=d.disciplineid "
                        + " where c.collectionid=" + rightSideCollectionID;
                Integer result = BasicSQLUtils.querySingleObj(sql);
                if (result != null) {
                    return result;
                }
            }
        }
        log.warn("using current collection's treedef for host taxononmy.");
        SpecifyAppContextMgr mgr = (SpecifyAppContextMgr) AppContextMgr.getInstance();
        return mgr.getTreeDefForClass(Taxon.class).getTreeDefId();
    }

    @SuppressWarnings("unchecked")
    protected TreeDefIface<?, ?, ?> getTreeDefForTreeLevelQRI(String fieldName, TableTree parentTT,
            DBTableInfo tableInfo) {
        if (tableInfo.getClassObj().equals(Taxon.class) && "hostTaxon".equals(fieldName)) {
            DataProviderSessionIFace session = null;
            try {
                session = DataProviderFactory.getInstance().createSession();
                return session.get(TaxonTreeDef.class, getHostTaxonTreeDefId());
            } finally {
                if (session != null) {
                    session.close();
                }
            }

        } else {
            SpecifyAppContextMgr mgr = (SpecifyAppContextMgr) AppContextMgr.getInstance();
            return mgr.getTreeDefForClass((Class<? extends Treeable<?, ?, ?>>) tableInfo.getClassObj());
        }
    }

    /**
     * @param parent
     * @param parentTT
     * 
     * Recursively constructs tableTree defined by "querybuilder.xml" schema.
     */
    protected void processForTables(final Element parent, final TableTree parentTT) {
        String tableName = XMLHelper.getAttr(parent, "name", null);
        DBTableInfo tableInfo = DBTableIdMgr.getInstance().getByShortClassName(tableName);
        if (!tableInfo.isHidden() && (!AppContextMgr.isSecurityOn() || tableInfo.getPermissions().canView())) {
            String fieldName = XMLHelper.getAttr(parent, "field", null);
            if (StringUtils.isEmpty(fieldName)) {
                fieldName = tableName.substring(0, 1).toLowerCase() + tableName.substring(1);
            }

            String abbrev = XMLHelper.getAttr(parent, "abbrev", null);
            TableTree newTreeNode = parentTT.addKid(new TableTree(tableName, fieldName, abbrev, tableInfo));
            if (Treeable.class.isAssignableFrom(tableInfo.getClassObj())) {
                try {
                    TreeDefIface<?, ?, ?> treeDef = getTreeDefForTreeLevelQRI(fieldName, parentTT, tableInfo);

                    SortedSet<TreeDefItemIface<?, ?, ?>> defItems = new TreeSet<TreeDefItemIface<?, ?, ?>>(
                            new Comparator<TreeDefItemIface<?, ?, ?>>() {
                                public int compare(TreeDefItemIface<?, ?, ?> o1, TreeDefItemIface<?, ?, ?> o2) {
                                    Integer r1 = o1.getRankId();
                                    Integer r2 = o2.getRankId();
                                    return r1.compareTo(r2);
                                }
                            });
                    defItems.addAll(treeDef.getTreeDefItems());
                    for (TreeDefItemIface<?, ?, ?> defItem : defItems) {
                        if (defItem.getRankId() > 0) { //skip root, just because. 
                            try {
                                //newTreeNode.getTableQRI().addField(
                                //        new TreeLevelQRI(newTreeNode.getTableQRI(), null, defItem
                                //                .getRankId()));
                                newTreeNode.getTableQRI().addField(new TreeLevelQRI(newTreeNode.getTableQRI(), null,
                                        defItem.getRankId(), "name", treeDef));
                                if (defItem instanceof TaxonTreeDefItem) {
                                    DBFieldInfo fi = DBTableIdMgr.getInstance().getInfoById(Taxon.getClassTableId())
                                            .getFieldByName("author");
                                    if (fi != null && !fi.isHidden()) {
                                        newTreeNode.getTableQRI()
                                                .addField(new TreeLevelQRI(newTreeNode.getTableQRI(), null,
                                                        defItem.getRankId(), "author", treeDef));
                                    }
                                    fi = DBTableIdMgr.getInstance().getInfoById(Taxon.getClassTableId())
                                            .getFieldByName("groupNumber");
                                    if (fi != null && !fi.isHidden()) {
                                        newTreeNode.getTableQRI()
                                                .addField(new TreeLevelQRI(newTreeNode.getTableQRI(), null,
                                                        defItem.getRankId(), "groupNumber", treeDef));
                                    }
                                }
                            } catch (Exception ex) {
                                // if there is no TreeDefItem for the rank then just skip it.
                                if (ex instanceof TreeLevelQRI.NoTreeDefItemException) {
                                    log.error(ex);
                                }
                                // else something is really messed up
                                else {
                                    UsageTracker.incrHandledUsageCount();
                                    edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class,
                                            ex);
                                    ex.printStackTrace();
                                }
                            }
                        }
                    }
                } catch (Exception ex) {
                    UsageTracker.incrHandledUsageCount();
                    edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
                    ex.printStackTrace();
                }
            }

            for (Object kidObj : parent.selectNodes("table")) {
                Element kidElement = (Element) kidObj;
                processForTables(kidElement, newTreeNode);
            }

            for (Object obj : parent.selectNodes("alias")) {
                Element kidElement = (Element) obj;
                String kidClassName = XMLHelper.getAttr(kidElement, "name", null);
                tableInfo = DBTableIdMgr.getInstance().getByShortClassName(kidClassName);
                if (!tableInfo.isHidden()
                        && (!AppContextMgr.isSecurityOn() || tableInfo.getPermissions().canView())) {
                    tableName = XMLHelper.getAttr(kidElement, "name", null);
                    fieldName = XMLHelper.getAttr(kidElement, "field", null);
                    if (StringUtils.isEmpty(fieldName)) {
                        fieldName = tableName.substring(0, 1).toLowerCase() + tableName.substring(1);
                    }
                    newTreeNode.addKid(new TableTree(kidClassName, fieldName, true));
                }
            }
        }
    }

    /**
     * @return string to select objects for import from xml.
     */
    protected String getTopLevelNodeSelector() {
        return "/queries/query";
    }

    /**
     * @param filePath
     * @param topNode
     * @return list queries defined in file.
     */
    @SuppressWarnings("serial")
    protected static Vector<Pair<SpQuery, Boolean>> getQueriesFromFile(final String filePath,
            final String topNode) {
        Vector<Pair<SpQuery, Boolean>> queries = new Vector<Pair<SpQuery, Boolean>>();
        try {
            Element root = XMLHelper.readFileToDOM4J(new File(filePath));
            for (Object obj : root.selectNodes(topNode)) {
                Element el = (Element) obj;
                boolean fixOps = getAttr(el, "appversion", null) == null;
                SpQuery query = new SpQuery();
                query.initialize();
                query.fromXML(el);
                query.setSpecifyUser(AppContextMgr.getInstance().getClassObject(SpecifyUser.class));
                queries.add(new Pair<SpQuery, Boolean>(query, fixOps) {
                    /* (non-Javadoc)
                     * @see edu.ku.brc.util.Pair#toString()
                     */
                    @Override
                    public String toString() {
                        return getFirst().toString();
                    }
                });
            }
            return queries;
        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * @param importedQueries
     * @param existingQueries
     * 
     * Modifies imported query names if necessary to ensure uniqueness.
     */
    protected static void adjustImportedQueryNames(List<Pair<SpQuery, Boolean>> importedQueries,
            List<String> existingQueries) {
        Set<String> names = new HashSet<String>();
        names.addAll(existingQueries);
        for (Pair<SpQuery, Boolean> query : importedQueries) {
            String origName = query.getFirst().getName();
            int cnt = 0;
            String qName = origName;
            while (names.contains(qName)) {
                cnt++;
                qName = origName + cnt;
            }
            query.getFirst().setName(qName);
            names.add(qName);
        }
    }

    /**
     * @param filePath
     * @return true if successful
     * 
     * imports all queries from file
     */
    protected static boolean importQueries(final String filePath) throws Exception {
        Vector<Pair<SpQuery, Boolean>> queries = getQueriesFromFile(filePath, "/queries/query");
        Vector<String> names = new Vector<String>();
        DataProviderSessionIFace session = null;
        try {
            session = DataProviderFactory.getInstance().createSession();
            List<SpQuery> qs = session.getDataList(SpQuery.class);
            for (SpQuery q : qs) {
                names.add(q.getName());
            }
        } finally {
            if (session != null) {
                session.close();
            }
        }
        adjustImportedQueryNames(queries, names);
        for (Pair<SpQuery, Boolean> q : queries) {
            if (q.getSecond()) {
                fixOperatorStorageForQuery(q.getFirst());
            }
        }
        return DataModelObjBase.saveWithError(true, queries);
    }

    /**
     * 
     */
    protected void importQueries() {
        UsageTracker.incrUsageCount("QB.IMPORT");
        String path = AppPreferences.getLocalPrefs().get(XML_PATH_PREF, null);

        try {
            FileDialog fDlg = new FileDialog(((Frame) UIRegistry.getTopWindow()), "Open", FileDialog.LOAD);
            if (path != null) {
                fDlg.setDirectory(path);
            }
            fDlg.setVisible(true);

            String dirStr = fDlg.getDirectory();
            String fileName = fDlg.getFile();
            if (StringUtils.isEmpty(dirStr) || StringUtils.isEmpty(fileName)) {
                return;
            }
            path = dirStr + fileName;
            AppPreferences.getLocalPrefs().put(XML_PATH_PREF, path);

            Vector<Pair<SpQuery, Boolean>> queries = getQueriesFromFile(path, getTopLevelNodeSelector());
            ToggleButtonChooserDlg<Pair<SpQuery, Boolean>> dlg = new ToggleButtonChooserDlg<Pair<SpQuery, Boolean>>(
                    (Frame) UIRegistry.getMostRecentWindow(), "QY_IMPORT_QUERIES", "QY_SEL_QUERIES_IMP", queries,
                    CustomDialog.OKCANCELHELP, ToggleButtonChooserPanel.Type.Checkbox);

            dlg.setAddSelectAll(true);
            dlg.setUseScrollPane(true);
            dlg.setHelpContext("QBImport");
            UIHelper.centerAndShow(dlg);
            List<Pair<SpQuery, Boolean>> queriesList = dlg.getSelectedObjects();
            if (queriesList == null || queriesList.size() == 0) {
                return;
            }

            Vector<String> names = new Vector<String>();

            //For exportschemamappings, mapping name is assumed to be the same as associated query name
            List<Object> nameObjs = BasicSQLUtils.querySingleCol("select name from spquery order by 1");
            for (Object q : nameObjs) {
                names.add((String) q);
            }
            adjustImportedQueryNames(queries, names);

            if (saveImportedQueries(queriesList)) {
                for (Pair<SpQuery, Boolean> query : queriesList) {
                    RecordSet rs = new RecordSet();
                    rs.initialize();
                    rs.set(query.getFirst().getName(), SpQuery.getClassTableId(), RecordSet.GLOBAL);
                    rs.addItem(query.getFirst().getSpQueryId());
                    addToNavBox(rs);
                }

                navBox.validate();
                navBox.repaint();
            }
        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            ex.printStackTrace();
        }
    }

    /**
     * @param queriesList a list of imported queries.
     * 
     * @return true if the queries are successfully saved to the db.
     */
    protected boolean saveImportedQueries(List<Pair<SpQuery, Boolean>> queriesList) throws Exception {
        for (Pair<SpQuery, Boolean> q : queriesList) {
            if (q.getSecond()) {
                fixOperatorStorageForQuery(q.getFirst());
            }
        }
        List<SpQuery> queries = new ArrayList<SpQuery>();
        for (Pair<SpQuery, Boolean> q : queriesList) {
            queries.add(q.getFirst());
        }
        return DataModelObjBase.saveWithError(true, queries);
    }

    /**
     * @return id for usage tracker
     */
    protected String getExportUsageKey() {
        return "QB.EXPORT";
    }

    /**
     * @return i18n resource id
     */
    protected String getNothingToExporti18nKey() {
        return "QY_NO_QUERIES_TO_EXPORT";
    }

    /**
     * @return resource id for export dialog title
     */
    protected String getExportDlgTitlei18nKey() {
        return "QY_EXPORT_QUERIES";
    }

    /**
     * @return resource id for export dialog message
     */
    protected String getExportDlgMsgi18nKey() {
        return "QY_SEL_QUERIES_EXP";
    }

    /**
     * @return export help context id
     */
    protected String getExportHelpContext() {
        return "QBExport";
    }

    /**
     * @return first line for export section
     */
    protected String getXMLExportFirstLine() {
        return "<queries>\n";
    }

    /**
     * @return last line for export section
     */
    protected String getXMLExportLastLine() {
        return "</queries>";
    }

    /**
     * @param query 
     * @param sb
     * 
     * writes xml for query to sb.
     */
    protected void toXML(final SpQuery query, final StringBuilder sb) {
        query.toXML(sb);
    }

    /**
     * 
     */
    protected void exportQueries() {
        UsageTracker.incrUsageCount(getExportUsageKey());
        Vector<String> list = new Vector<String>();
        for (NavBoxItemIFace nbi : navBox.getItems()) {
            list.add(nbi.getTitle());
        }

        List<String> selectedList = null;
        if (list.size() == 0) {
            UIRegistry.showLocalizedMsg(getNothingToExporti18nKey());
            return;
        }
        if (list.size() == 1) {
            selectedList = list;
        } else {
            ToggleButtonChooserDlg<String> dlg = new ToggleButtonChooserDlg<String>(
                    (Frame) UIRegistry.getMostRecentWindow(), getExportDlgTitlei18nKey(), getExportDlgMsgi18nKey(),
                    list, CustomDialog.OKCANCELHELP, ToggleButtonChooserPanel.Type.Checkbox);
            dlg.setAddSelectAll(true);
            dlg.setUseScrollPane(true);
            dlg.setHelpContext(getExportHelpContext());
            UIHelper.centerAndShow(dlg);
            selectedList = dlg.getSelectedObjects();
            if (dlg.isCancelled() || selectedList.size() == 0) {
                return;
            }
        }

        String path = AppPreferences.getLocalPrefs().get(XML_PATH_PREF, null);

        FileDialog fDlg = new FileDialog(((Frame) UIRegistry.getTopWindow()), UIRegistry.getResourceString("SAVE"),
                FileDialog.SAVE);
        if (path != null) {
            fDlg.setDirectory(path);
        }
        fDlg.setVisible(true);

        String dirStr = fDlg.getDirectory();
        String fileName = fDlg.getFile();
        if (StringUtils.isEmpty(dirStr) || StringUtils.isEmpty(fileName)) {
            return;
        }

        if (StringUtils.isEmpty(FilenameUtils.getExtension(fileName))) {
            fileName += ".xml";
        }
        path = dirStr + fileName;
        AppPreferences.getLocalPrefs().put(XML_PATH_PREF, path);

        Hashtable<String, Boolean> hash = new Hashtable<String, Boolean>();
        for (String qTitle : selectedList) {
            hash.put(qTitle, true);
        }

        Vector<SpQuery> queries = new Vector<SpQuery>();

        // Persist out to database
        DataProviderSessionIFace session = null;
        try {
            session = DataProviderFactory.getInstance().createSession();
            for (NavBoxItemIFace nbi : navBox.getItems()) {
                if (hash.get(nbi.getTitle()) != null) {
                    RecordSetIFace rs = (RecordSetIFace) nbi.getData();
                    if (rs != null) {
                        SpQuery query = session.get(SpQuery.class, rs.getOnlyItem().getRecordId());
                        queries.add(query);
                    }
                }
            }

            StringBuilder sb = new StringBuilder();
            sb.append(getXMLExportFirstLine());
            for (SpQuery q : queries) {
                q.toXML(sb);
            }
            sb.append(getXMLExportLastLine());
            FileUtils.writeStringToFile(new File(path), sb.toString());

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            // XXX Error dialog
            ex.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    /**
     * @return the permissions array
     */
    @Override
    protected boolean[][] getPermsArray() {
        return new boolean[][] { { true, true, true, true }, { true, true, true, true },
                { true, true, false, false }, { true, false, false, false } };
    }

    /**
     * @param treeRoot
     * @return a Hashtable of top-level tables in tableTree.
     */
    protected Hashtable<String, TableTree> buildTableTreeHash(final TableTree treeRoot) {
        Hashtable<String, TableTree> result = new Hashtable<String, TableTree>();
        try {
            for (int t = 0; t < treeRoot.getKids(); t++) {
                TableTree tt = treeRoot.getKid(t);
                result.put(tt.getName(), tt);
                log.debug("Adding[" + tt.getName() + "] to hash");
            }
        } catch (Exception ex) {
            UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, ex);
            ex.printStackTrace();
        }
        return result;
    }

    /**
     * @return true if the current user has permission to use the QueryBuilder
     */
    public boolean isPermitted() {
        return getPermissions().canView();
    }

    /* (non-Javadoc)
    * @see edu.ku.brc.af.tasks.BaseTask#getPermEditorPanel()
    */
    @Override
    public PermissionEditorIFace getPermEditorPanel() {
        return new BasicPermisionPanel("QueryTask.PermTitle", "QueryTask.PermEnable", null, null, null);
    }

    /**
      * @author timbo
      *
      * @code_status Alpha
      *
      *  Launches TableTree build with status message and progress bar then opens a Query
      *  in the finished() method. 
      */
    protected class OpenQueryWorker extends SwingWorker {
        @Override
        public Object construct() {
            UIRegistry.displayStatusBarText(UIRegistry.getResourceString("QB_LOADING"));
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    UIRegistry.getStatusBar().setIndeterminate("QUERYTASK", true);
                }
            });
            getTableTrees();
            UIRegistry.displayStatusBarText("");
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    UIRegistry.getStatusBar().setProgressDone("QUERYTASK");
                }
            });
            return null;
        }
    }

    /**
     * Reload the current query.
     * 
     * This method is not currently used.
     * 
     * With adjusments for saving and navBtn updating and for case of not-yet-saved current query it
     * could be used to automatically refresh query after Localization or TreeDef edits.
     */
    public void reloadQuery() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new EditQueryWorker(queryBldrPane.getQuery().getId(), queryBldrPane.getQueryNavBtn()).start();
            }
        });
    }

    protected class NewQueryWorker extends OpenQueryWorker {
        protected final DBTableInfo tableInfo;

        public NewQueryWorker(final DBTableInfo tableInfo) {
            super();
            this.tableInfo = tableInfo;
        }

        @Override
        public void finished() {
            super.finished();
            createNewQuery(tableInfo);
        }
    }

    protected class EditQueryWorker extends OpenQueryWorker {
        protected final Integer queryId;
        protected final RolloverCommand queryNavBtn;

        public EditQueryWorker(final Integer queryId, final RolloverCommand queryNavBtn) {
            super();
            this.queryId = queryId;
            this.queryNavBtn = queryNavBtn;
        }

        @Override
        public void finished() {
            super.finished();
            if (queryBldrPane == null || queryBldrPane.aboutToShutdown()) {
                if (editQuery(queryId)) {
                    queryNavBtn.setEnabled(false);
                    queryBldrPane.setQueryNavBtn(queryNavBtn);
                }
            }
        }

    }

    protected class EditOtherQueryWorker extends OpenQueryWorker {
        protected final Integer queryId;

        public EditOtherQueryWorker(final Integer queryId) {
            super();
            this.queryId = queryId;
        }

        @Override
        public void finished() {
            super.finished();
            editQuery(queryId);
        }
    }

    /**
     * @param q
     */
    public static void fixOperatorStorageForQuery(SpQuery q) throws Exception {
        for (SpQueryField fld : q.getFields()) {
            try {
                fixOperatorStorageForField(fld);
            } catch (Exception e) {
                throw e;
            }
        }
    }

    /**
     * @param qFld
     * @throws Exception
     */
    public static void fixOperatorStorageForField(SpQueryField qFld) throws Exception {
        String[] idParts = qFld.getStringId().split(",");
        String idFinal = idParts[idParts.length - 1];
        String[] fldParts = idFinal.split("\\.");
        String tableName = fldParts[1];
        String fieldName = fldParts[2];
        DBTableInfo tbl = DBTableIdMgr.getInstance().getInfoByTableName(tableName);
        DBFieldInfo fld = tbl.getFieldByName(fieldName);
        boolean isTreeLevel = fld == null && Treeable.class.isAssignableFrom(tbl.getClassObj());
        boolean isRel = false;
        if (fld == null && !isTreeLevel) {
            String relName = qFld.getFieldName();
            for (DBRelationshipInfo rInfo : tbl.getRelationships()) {
                if (relName.equals(rInfo.getOtherSide())) {
                    isRel = true;
                    break;
                }
            }
        }
        boolean isDatePart = false;
        if (fld == null && !isTreeLevel & !isRel) {
            DBFieldInfo dateFld = tbl.getFieldByName(qFld.getFieldName());
            if (dateFld != null) {
                isDatePart = Calendar.class.isAssignableFrom(dateFld.getDataClass());
            }

        }
        SpQueryField.OperatorType op = SpQueryField.OperatorType.EQUALS;
        if (fld != null || isTreeLevel || isDatePart) {
            boolean isPickList = fld != null && (StringUtils.isNotEmpty(fld.getPickListName())
                    || RecordTypeCodeBuilder.getTypeCode(fld) != null);
            SpQueryField.OperatorType[] ops = QueryFieldPanel.getComparatorList(isTreeLevel, isPickList, fld,
                    fld != null ? fld.getDataClass() : null);
            Byte opStart = qFld.getOperStart();
            if (opStart != null && opStart >= 0 && opStart < ops.length) {
                op = ops[opStart];
            }
            //qFld.setTimestampModified(new Timestamp(System.currentTimeMillis()));
        }
        qFld.setOperStart(op.getOrdinal());
    }

    public static boolean fixOperatorStorageForAllQueries() {
        DataProviderSessionIFace session = DataProviderFactory.getInstance().createSession();
        try {
            List<SpQuery> queries = session.getDataList(SpQuery.class);
            try {
                session.beginTransaction();
                for (SpQuery q : queries) {
                    q.forceLoad();
                    fixOperatorStorageForQuery(q);
                    session.saveOrUpdate(q);
                }
                session.commit();
            } catch (Exception e) {
                edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryTask.class, e);
                e.printStackTrace();
                session.rollback();
                return false;
            }
        } finally {
            session.close();
        }
        return true;
    }

    @SuppressWarnings("serial")
    public class QueryBuilderContextException extends Exception {

    }
}