edu.ku.brc.specify.tasks.subpane.qb.QueryFieldPanel.java Source code

Java tutorial

Introduction

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

import static edu.ku.brc.ui.UIHelper.createLabel;
import static edu.ku.brc.ui.UIRegistry.getResourceString;

import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Vector;

import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

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

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import edu.ku.brc.af.core.db.DBFieldInfo;
import edu.ku.brc.af.core.db.DBRelationshipInfo;
import edu.ku.brc.af.core.db.DBRelationshipInfo.RelationshipType;
import edu.ku.brc.af.ui.db.PickListDBAdapterIFace;
import edu.ku.brc.af.ui.forms.FormHelper;
import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterIFace;
import edu.ku.brc.af.ui.forms.validation.DataChangeNotifier;
import edu.ku.brc.af.ui.forms.validation.FormValidator;
import edu.ku.brc.af.ui.forms.validation.UIValidator;
import edu.ku.brc.af.ui.forms.validation.ValCheckBox;
import edu.ku.brc.af.ui.forms.validation.ValComboBox;
import edu.ku.brc.af.ui.forms.validation.ValTextField;
import edu.ku.brc.specify.datamodel.Agent;
import edu.ku.brc.specify.datamodel.CollectionObject;
import edu.ku.brc.specify.datamodel.SpExportSchemaItem;
import edu.ku.brc.specify.datamodel.SpExportSchemaItemMapping;
import edu.ku.brc.specify.datamodel.SpExportSchemaMapping;
import edu.ku.brc.specify.datamodel.SpQueryField;
import edu.ku.brc.specify.datamodel.SpQueryField.OperatorType;
import edu.ku.brc.specify.dbsupport.RecordTypeCodeBuilder;
import edu.ku.brc.specify.ui.CatalogNumberFormatter;
import edu.ku.brc.specify.ui.CatalogNumberUIFieldFormatter;
import edu.ku.brc.specify.ui.db.PickListDBAdapterFactory;
import edu.ku.brc.specify.ui.db.PickListTableAdapter;
import edu.ku.brc.ui.IconManager;
import edu.ku.brc.ui.MultiStateIconButon;
import edu.ku.brc.ui.RolloverCommand;
import edu.ku.brc.ui.UIRegistry;
import edu.ku.brc.util.DateConverter;
import edu.ku.brc.util.Pair;

/**
 * @author rod
 *
 * @code_status Alpha
 *
 * Oct 18, 2007
 *
 */
@SuppressWarnings("serial")
public class QueryFieldPanel extends JPanel implements ActionListener {
    protected static final Logger log = Logger.getLogger(QueryFieldPanel.class);

    //    private static final String operatorSavingFixDateStr = "2012-12-05"; 
    //    private static final Date operatorSavingFixDate = DateFormat.getDateInstance(DateFormat.LONG, Locale.US).parse("2012-12-05");

    protected String noMappingStr = getResourceString("WB_NO_MAPPING");

    protected QueryFieldPanelContainerIFace ownerQuery;
    protected String columnDefStr;
    protected ImageIcon blankIcon = IconManager.getIcon("BlankIcon", IconManager.IconSize.Std24);

    protected boolean hasFocus = false;
    protected Color bgColor = null;
    protected JLabel fieldLabel;
    protected boolean labelQualified = false;
    protected JButton closeBtn;
    protected JComboBox schemaItemCBX;
    protected JLabel iconLabel;
    protected ImageIcon icon;
    protected IconManager.IconSize iconSize = IconManager.IconSize.Std24;
    protected JCheckBox isNotCheckbox;
    protected JComboBox operatorCBX;
    protected JComponent criteria;
    protected MultiStateIconButon sortCheckbox;
    protected JCheckBox isDisplayedCkbx;
    protected JCheckBox isPromptCkbx;
    protected JCheckBox isEnforcedCkbx;

    protected JComponent[] comps;

    protected FieldQRI fieldQRI;
    protected SpQueryField queryField = null;
    protected SpExportSchemaMapping schemaMapping = null;
    protected SpExportSchemaItem schemaItem = null;
    protected String schemaItemName = null;
    protected boolean autoMapped = false;

    protected PickListDBAdapterIFace pickList = null;

    protected FormValidator validator;

    protected QueryFieldPanel thisItem;

    protected String[] labelStrs;
    protected SpQueryField.OperatorType[] comparators;

    protected DateConverter dateConverter = null;

    protected boolean selected = false;

    /**
     * @author timbo
     *
     * @code_status Alpha
     * 
     * Deals with pairs of criteria entries.
     *
     */

    private class CriteriaPair extends JPanel {
        protected JTextField text1;
        protected JTextField text2;
        protected JLabel connectorText;
        protected JPanel rangePanel;
        protected JTextField text;
        protected boolean showingPair = false;

        /**
         * Constructor
         */
        public CriteriaPair(final KeyListener listener) {
            super();
            buildUI(listener);
        }

        /**
         * Creates the UI components.
         */
        protected void buildUI(final KeyListener listener) {
            text1 = createTextField("1");
            text1.addKeyListener(listener);
            text2 = createTextField("2");
            text2.addKeyListener(listener);
            connectorText = new JLabel(" " + getResourceString("AND") + " ");

            rangePanel = new JPanel();
            PanelBuilder pb = new PanelBuilder(new FormLayout("f:p:g, f:p, f:p:g", "f:p"), rangePanel);
            CellConstraints cc = new CellConstraints();
            pb.add(text1, cc.xy(1, 1));
            pb.add(connectorText, cc.xy(2, 1));
            pb.add(text2, cc.xy(3, 1));
            rangePanel.validate();

            setLayout(new CardLayout());
            text = createTextField("3");
            text.addKeyListener(listener);
            add("text", text);
            add("rangePanel", rangePanel);
            validate();
        }

        /* (non-Javadoc)
        * @see javax.swing.JComponent#getPreferredSize()
        */
        @Override
        public Dimension getPreferredSize() {
            Dimension result = super.getPreferredSize();
            result.setSize(Math.min((sortCheckbox.getX() - 4) - (operatorCBX.getX() + operatorCBX.getWidth() + 4),
                    result.getWidth()), result.getHeight());
            return result;
        }

        /**
           * @param entry
           * @param op
           * 
           * Sets the criteria text, modifying the layout if necessary.
           */
        public void setCriteriaText(final String entry, final String entry2, final OperatorType op) {
            if (op != null && op.equals(OperatorType.BETWEEN)) {
                //Currently between operator is not allowed for string fields, so assume no quotes 
                //and no commas but the one separating the two limits.
                //Also assuming (more or less) valid entry. 
                setShowingPair(true);
                String[] entries;
                if (StringUtils.isBlank(entry2)) {
                    entries = entry.split(",");
                } else {
                    entries = new String[2];
                    entries[0] = entry;
                    entries[1] = entry2;
                }
                if (entries.length > 0) {
                    text1.setText(entries[0]);
                    if (entries.length > 1) {
                        text2.setText(entries[1]);
                    } else {
                        text2.setText(null);
                    }
                } else {
                    text1.setText(null);
                }
            } else {
                setShowingPair(false);
                text.setText(entry);
            }

        }

        /**
         * @return a String representation of the entered criteria, parseable by the QueryBuilder.
         */
        public String getCriteriaText() {
            //Assuming isValidPairEntry() is true
            if (showingPair) {
                return text1.getText() + "," + text2.getText();
            }

            return text.getText();
        }

        //        /**
        //         * @return true unless the entered criteria is really messed up.
        //         */
        //        public boolean isValidPairEntry()
        //        {
        //            if (showingPair)
        //            {
        //                return (StringUtils.isBlank(text1.getText()) && StringUtils.isBlank(text2.getText()))
        //                    || (!StringUtils.isBlank(text1.getText()) && !StringUtils.isBlank(text2.getText()));
        //            }
        //            
        //            return true;
        //        }   
        //        
        //        /**
        //         * @return showingPair.
        //         */
        //        public boolean isShowingPair()
        //        {
        //            return showingPair;
        //        }

        /**
         * @param showingPair
         * 
         * Switches the UI from single to double criteria controls based on value of showingPair.
         */
        public void setShowingPair(final boolean showingPair) {
            if (this.showingPair != showingPair) {
                this.showingPair = showingPair;
                if (showingPair) {
                    ((CardLayout) getLayout()).last(this);
                } else {
                    ((CardLayout) getLayout()).first(this);
                }

                //clear old entries, for now.
                text.setText(null);
                text1.setText(null);
                text2.setText(null);

                validate();
            }
        }
    }

    protected PickListDBAdapterIFace buildPickList() {
        if (fieldQRI instanceof RelQRI) {
            PickListDBAdapterIFace pl = PickListDBAdapterFactory.getInstance()
                    .create(fieldQRI.getTableInfo().getName(), false);
            if (pl instanceof PickListTableAdapter) {
                return pl;
            }
            return null;

        }
        if (fieldQRI != null && fieldQRI.getTableInfo() != null && fieldQRI.getFieldInfo() != null) {

            PickListDBAdapterIFace typeCodeList = RecordTypeCodeBuilder.getTypeCode(fieldQRI.getFieldInfo());
            if (typeCodeList != null) {
                return typeCodeList;
            }
            //XXX unfortunately this doesn't work because currently picklist defs are only setup via form view defs
            if (StringUtils.isNotEmpty(fieldQRI.getFieldInfo().getPickListName())) {
                //pickList = ((edu.ku.brc.specify.ui.db.PickListDBAdapterFactory)PickListDBAdapterFactory.getInstance()).create(fieldQRI.getFieldInfo().getPickListName(), false);
                return PickListDBAdapterFactory.getInstance().create(fieldQRI.getFieldInfo().getPickListName(),
                        false);
            }
            //else
            //return RecordTypeCodeBuilder.getTypeCode(fieldQRI.getFieldInfo());
        }
        return null;
    }

    /**
     * @param ownerQuery
     * @param fieldQRI
     * @param columnDefStr
     * @param saveBtn
     * @param queryField
     * @param schemaMapping
     */
    public QueryFieldPanel(final QueryFieldPanelContainerIFace ownerQuery, final FieldQRI fieldQRI,
            final String columnDefStr, final Component saveBtn, final SpQueryField queryField,
            final SpExportSchemaMapping schemaMapping) {
        this(ownerQuery, fieldQRI, IconManager.IconSize.Std24, columnDefStr, saveBtn, queryField, schemaMapping,
                null);
    }

    /**
     * @param ownerQuery
     * @param fieldQRI
     * @param columnDefStr
     * @param saveBtn
     * @param queryField
     */
    public QueryFieldPanel(final QueryFieldPanelContainerIFace ownerQuery, final FieldQRI fieldQRI,
            final String columnDefStr, final Component saveBtn, final SpQueryField queryField) {
        this(ownerQuery, fieldQRI, IconManager.IconSize.Std24, columnDefStr, saveBtn, queryField, null, null);
    }

    /**
     * @param ownerQuery
     * @param fieldQRI
     * @param columnDefStr
     * @param saveBtn
     * @param queryField
     * @param schemaMapping
     * @param schemaItem
     */
    public QueryFieldPanel(final QueryFieldPanelContainerIFace ownerQuery, final FieldQRI fieldQRI,
            final String columnDefStr, final Component saveBtn, final SpQueryField queryField,
            final SpExportSchemaMapping schemaMapping, final SpExportSchemaItem schemaItem) {
        this(ownerQuery, fieldQRI, IconManager.IconSize.Std24, columnDefStr, saveBtn, queryField, schemaMapping,
                schemaItem);
    }

    /**
     * @param ownerQuery
     * @param fieldQRI
     * @param iconSize
     * @param columnDefStr
     * @param saveBtn
     * @param queryField
     * @param schemaMapping
     * @param schemaItem
     */
    public QueryFieldPanel(final QueryFieldPanelContainerIFace ownerQuery, final FieldQRI fieldQRI,
            final IconManager.IconSize iconSize, final String columnDefStr, final Component saveBtn,
            final SpQueryField queryField, final SpExportSchemaMapping schemaMapping,
            final SpExportSchemaItem schemaItem) {
        this.ownerQuery = ownerQuery;
        this.schemaMapping = schemaMapping;
        this.schemaItem = schemaItem;
        boolean isForSchema = this.schemaMapping != null;
        if (this.ownerQuery.isPromptMode()) {
            if (!isForSchema) {
                labelStrs = new String[] { " ", UIRegistry.getResourceString("QB_FIELD"),
                        UIRegistry.getResourceString("QB_NOT"), UIRegistry.getResourceString("QB_OPERATOR"),
                        UIRegistry.getResourceString("QB_CRITERIA"), UIRegistry.getResourceString("QB_SORT"),
                        //UIRegistry.getResourceString("QB_DISPLAY"), getResourceString("QB_PROMPT"), 
                        //" ", " " 
                };
            } else {
                labelStrs = new String[] { UIRegistry.getResourceString("QB_SCHEMAITEM"), " ",
                        UIRegistry.getResourceString("QB_FIELD"), UIRegistry.getResourceString("QB_NOT"),
                        UIRegistry.getResourceString("QB_OPERATOR"), UIRegistry.getResourceString("QB_CRITERIA"),
                        UIRegistry.getResourceString("QB_SORT"), UIRegistry.getResourceString("QB_ALLOW_NULL"),
                        //UIRegistry.getResourceString("QB_DISPLAY"), getResourceString("QB_PROMPT"), 
                        //" ", " " 
                };
            }
        } else {
            if (!isForSchema) {
                labelStrs = new String[] { " ", /*UIRegistry.getResourceString("QB_FIELD")*/" ",
                        UIRegistry.getResourceString("QB_NOT"), UIRegistry.getResourceString("QB_OPERATOR"),
                        UIRegistry.getResourceString("QB_CRITERIA"), UIRegistry.getResourceString("QB_SORT"),
                        UIRegistry.getResourceString("QB_DISPLAY"), getResourceString("QB_PROMPT"),
                        getResourceString("QB_ALWAYS_ENFORCE"), " ", " " };
            } else {
                labelStrs = new String[] { UIRegistry.getResourceString("QB_SCHEMAITEM"), " ",
                        /*UIRegistry.getResourceString("QB_FIELD")*/" ", UIRegistry.getResourceString("QB_NOT"),
                        UIRegistry.getResourceString("QB_OPERATOR"), UIRegistry.getResourceString("QB_CRITERIA"),
                        UIRegistry.getResourceString("QB_SORT"), UIRegistry.getResourceString("QB_DISPLAY"),
                        getResourceString("QB_ALLOW_NULL"), " ", " " };
            }
        }
        this.iconSize = iconSize;
        this.fieldQRI = fieldQRI;
        if (fieldQRI != null && (fieldQRI.getDataClass().equals(Calendar.class)
                || fieldQRI.getDataClass().equals(java.sql.Timestamp.class))) {
            dateConverter = new DateConverter();
        }

        pickList = buildPickList();

        this.columnDefStr = columnDefStr;

        thisItem = this;

        validator = new FormValidator(null);
        if (saveBtn != null) {
            validator.addEnableItem(saveBtn, FormValidator.EnableType.ValidAndChangedItems);
        }
        validator.setEnabled(true);

        boolean createAsHeader = StringUtils.isEmpty(columnDefStr);

        int[] widths = buildControlLayout(iconSize, createAsHeader, saveBtn);
        if (createAsHeader) {
            removeAll();
            buildLabelLayout(widths);
            ownerQuery.setColumnDefStr(this.columnDefStr);
        }

        setQueryField(queryField);
        if (!createAsHeader
                && getFieldInfo() != null /*this means relationships and tree levels won't get qualified*/) {
            setToolTipText(getQualifiedLabel(fieldQRI.getTableTree(), true));
        }
    }

    public void updateQueryField() {
        updateQueryField(queryField);
    }

    /**
     * @param useValues
     * @return the text contained in the criteria control.
     */
    public String getCriteriaText(final boolean useValues) {
        if (criteria instanceof JTextField) {
            return ((JTextField) criteria).getText();
        }
        if (criteria instanceof PickListCriteriaCombo) {
            return ((PickListCriteriaCombo) criteria).getText(useValues);
        }
        if (criteria instanceof CriteriaPair) {
            return ((CriteriaPair) criteria).getCriteriaText();
        }
        throw new RuntimeException("Unrecognized criteria component: " + criteria.getClass());
    }

    public void updateQueryField(final SpQueryField qField) {
        boolean isForSchema = schemaMapping != null;
        if (qField != null && !ownerQuery.isPromptMode() && !ownerQuery.isForSchemaExport()) {
            qField.setIsDisplay(isDisplayedCkbx.isSelected());
            qField.setIsPrompt(isPromptCkbx.isSelected());
            if (isForSchema) {
                qField.setAllowNulls(isEnforcedCkbx.isSelected());
            } else {
                qField.setAlwaysFilter(isEnforcedCkbx.isSelected());
            }
            qField.setIsNot(isNotCheckbox.isSelected());
            if (validator.hasChanged() && qField.getSpQueryFieldId() != null) {
                FormHelper.updateLastEdittedInfo(qField);
            }

            qField.setSortType((byte) sortCheckbox.getState());

            qField.setOperStart(((SpQueryField.OperatorType) operatorCBX.getSelectedItem()).getOrdinal());

            qField.setStartValue(getCriteriaText(false));
            String lbl = this.getLabel();
            if (fieldQRI instanceof RelQRI) {
                lbl = RelQRI.stripDescriptiveStuff(lbl);
            }
            qField.setContextTableIdent(fieldQRI.getTableInfo().getTableId());
            qField.setColumnAliasTitle(lbl, fieldQRI instanceof TreeLevelQRI);
            qField.setIsRelFld(fieldQRI instanceof RelQRI);

            Vector<Integer> idList = new Vector<Integer>();
            TableQRI parent = fieldQRI.getTable();
            while (parent != null) {
                idList.add(parent.getTableInfo().getTableId());
                parent = parent.getTableTree().getParent().getTableQRI();
            }

            String tablesIds = fieldQRI.getTableTree().getPathFromRootAsString();
            log.debug(tablesIds);
            qField.setTableList(tablesIds);
            qField.setStringId(getStringId());

            if (schemaItemCBX != null) {
                SpExportSchemaItemMapping mapping = qField.getMapping();
                if (mapping != null) {
                    schemaItem = (SpExportSchemaItem) schemaItemCBX.getSelectedItem();
                    if (schemaItem.getId() == null) {
                        mapping.setExportSchemaItem(null);
                        String exportName = schemaItemCBX.getEditor().getItem().toString();
                        if (exportName == null
                                || exportName.equals(getResourceString("QueryBldrPane.UnmappedSchemaItemName"))) {
                            mapping.setExportedFieldName(getDefaultExportedFieldName());
                        } else {
                            mapping.setExportedFieldName(exportName);
                        }
                    } else {
                        mapping.setExportSchemaItem(schemaItem);
                    }
                }
            }
        } else {
            log.error("QueryField is null or ownerQuery is prompt only. Unable to update database object.");
        }
    }

    /**
     * @return customized fieldname/column header for schema mapping items that are not mapped to a concept.
     */
    protected String getExportedFieldName() {
        SpExportSchemaItemMapping mi = this.getItemMapping();
        if (mi == null || mi.getExportedFieldName() == null) {
            return getDefaultExportedFieldName();
        } else {
            return mi.getExportedFieldName();
        }
    }

    protected String getDefaultExportedFieldName() {
        if (queryField != null) {
            return queryField.getColumnAlias();
        } else {
            return fieldQRI.getTitle();
        }
    }

    /**
     * @return the queryField
     */
    public SpQueryField getQueryField() {
        return queryField;
    }

    /**
     * @param text the criteria
     * @param text2 the end criteria in case of BETWEEN op
     * @param op the operator
     */
    protected void setCriteriaText(final String text, final String text2, final OperatorType op) {
        if (op != null && op.equals(OperatorType.BETWEEN) && !(criteria instanceof CriteriaPair)) {
            //Probably nothing bad will result but, log situation, in case.
            log.error("operator is 'BETWEEN' but criteria control is not CriteriaPair");
        }
        if (criteria instanceof JTextField) {
            ((JTextField) criteria).setText(text);
        } else if (criteria instanceof PickListCriteriaCombo) {
            ((PickListCriteriaCombo) criteria).setSelections(text);
        } else if (criteria instanceof CriteriaPair) {
            ((CriteriaPair) criteria).setCriteriaText(text, text2, op);
        } else {
            throw new RuntimeException("Unrecognized criteria component: " + criteria.getClass());
        }

    }

    //    private SpQueryField.OperatorType getOperatorType(final SpQueryField fld)
    //    {
    //       Timestamp fldTimeStamp = fld.getTimeStampModified();
    //       
    //    }

    /**
     * @param queryField
     */
    public void setQueryFieldForAutomapping(SpQueryField queryField) {
        justSetTheQueryField(queryField);
    }

    /**
     * @param queryField
     */
    public void justSetTheQueryField(SpQueryField queryField) {
        this.queryField = queryField;
    }

    /**
     * @param queryField the queryField to set
     */
    private void setQueryField(SpQueryField queryField) {
        this.queryField = queryField;
        boolean isForSchema = schemaMapping != null;
        //if (!ownerQuery.isPromptMode()) {
        if (queryField != null) {
            if (queryField.getSpQueryFieldId() != null) {
                isNotCheckbox.setSelected(queryField.getIsNot());
                try {
                    OperatorType o = OperatorType.values()[queryField.getOperStart()];
                    operatorCBX.setSelectedItem(o);
                } catch (IllegalArgumentException ex) {
                    log.error("unable to set operator index for " + queryField.getStringId() + ": " + ex);
                    operatorCBX.setSelectedIndex(0);
                }
                setCriteriaText(queryField.getStartValue(), queryField.getEndValue(),
                        (OperatorType) operatorCBX.getSelectedItem());
                sortCheckbox.setState(queryField.getSortType());
                sortCheckbox.setEnabled(queryField.getIsDisplay());
                if (!ownerQuery.isPromptMode()) {
                    isDisplayedCkbx.setSelected(queryField.getIsDisplay());
                    isPromptCkbx.setSelected(queryField.getIsPrompt() == null ? true : queryField.getIsPrompt());
                    if (isForSchema) {
                        isEnforcedCkbx.setSelected(
                                queryField.getAllowNulls() == null ? false : queryField.getAllowNulls());
                    } else {
                        isEnforcedCkbx.setSelected(
                                queryField.getAlwaysFilter() == null ? true : queryField.getAlwaysFilter());
                    }
                }
                validator.setHasChanged(false);
            } else {
                validator.reset(true); // tells it it is a new data object
                validator.setHasChanged(true);
                this.queryField.setStringId(fieldQRI.getStringId());
            }
            validator.validateForm();
            validator.wasValidated(null);
        }
        //}
    }

    /**
     * @param fqri
     * @param qf
     * 
     * Sets new field and updates UI to display properties for new field.
     */
    public void setField(final FieldQRI fqri, final SpQueryField qf) {
        fieldQRI = fqri;
        if (fieldQRI != null && (fieldQRI.getDataClass().equals(Calendar.class)
                || fieldQRI.getDataClass().equals(java.sql.Timestamp.class))) {
            dateConverter = new DateConverter();
        }

        if (fieldQRI != null) {
            icon = IconManager.getIcon(fieldQRI.getTableInfo().getName(), iconSize);
            setIcon(icon);
        }

        pickList = buildPickList();
        comparators = getComparatorList(fieldQRI);
        String fieldLabelText = fieldQRI != null ? fieldQRI.getTitle() : null;
        if (fieldQRI instanceof RelQRI) {
            DBRelationshipInfo.RelationshipType relType = ((RelQRI) fieldQRI).getRelationshipInfo().getType();
            if (relType.equals(DBRelationshipInfo.RelationshipType.OneToMany)
                    || relType.equals(DBRelationshipInfo.RelationshipType.ManyToMany)) {
                fieldLabelText += " " + UIRegistry.getResourceString("QB_AGGREGATED");
            } else {
                fieldLabelText += " " + UIRegistry.getResourceString("QB_FORMATTED");
            }

        }
        fieldLabel.setText(fieldLabelText);
        boolean isBool = fieldQRI != null && fieldQRI.getDataClass().equals(Boolean.class);
        boolean isRel = fieldQRI != null && fieldQRI instanceof RelQRI;
        boolean isTreeLevel = fieldQRI instanceof TreeLevelQRI;

        operatorCBX.setModel(new DefaultComboBoxModel(comparators));
        //XXX need to set up criteria to support 'between' if necessary

        for (int c = 1; c < comps.length; c++) {
            if (comps[c] != null) {
                comps[c].setVisible(fieldQRI != null);
            }
        }

        isNotCheckbox.setVisible(fieldQRI != null && !isRel);
        operatorCBX.setVisible(fieldQRI != null && !isRel);
        criteria.setVisible(fieldQRI != null && !isRel && !isBool);
        if (schemaMapping != null) {
            this.sortCheckbox.setVisible(!(isTreeLevel || isRel));
        } else {
            if (!isRel) {
                this.sortCheckbox.setVisible(fieldQRI != null);
            } else {
                this.sortCheckbox.setVisible(
                        ((RelQRI) fieldQRI).getRelationshipInfo().getType() != RelationshipType.OneToMany);
            }
        }

        if (schemaMapping != null) {
            isPromptCkbx.setVisible(false);
        } else if (!ownerQuery.isPromptMode()) {
            isDisplayedCkbx.setVisible(fieldQRI != null && !isRel);
            isPromptCkbx.setVisible(fieldQRI != null && !isRel);
            isEnforcedCkbx.setVisible(fieldQRI != null && !isRel);
        }
        setQueryField(qf);
    }

    /**
     * 
     */
    public void resetValidator() {
        validator.reset(true);
    }

    /**
     * @param field
     * @return list of comparators appropriate for field.
     */
    protected SpQueryField.OperatorType[] getComparatorList(final FieldQRI field) {
        if (field == null) {
            return new SpQueryField.OperatorType[] {};
        }
        return getComparatorList(field instanceof TreeLevelQRI, pickList != null, field.getFieldInfo(),
                field.getDataClass());
    }

    /**
     * @return the format name.
     */
    protected String getFormatName() {
        return getQueryField() != null
                ? (StringUtils.isEmpty(getQueryField().getFormatName()) ? null : getQueryField().getFormatName())
                : null;
    }

    /**
     * @param field
     * @return list of comparators appropriate for field.
     */
    public static SpQueryField.OperatorType[] getComparatorList(boolean isTreeLevel, boolean isPickList,
            DBFieldInfo fieldInfo, Class<?> dataClass) {
        if (isPickList) {
            return new SpQueryField.OperatorType[] { SpQueryField.OperatorType.EQUALS, SpQueryField.OperatorType.IN,
                    SpQueryField.OperatorType.EMPTY };
        }
        //CatalogNumber needs special treatment - works better as a number.
        //And other fields? Not sure how to tell. Maybe the formatter?????
        if (fieldInfo != null && fieldInfo.getName().equalsIgnoreCase("catalognumber")
                && fieldInfo.getTableInfo().getClassObj().equals(CollectionObject.class)) {
            if (fieldInfo.getFormatter() != null && fieldInfo.getFormatter().isNumeric()) {
                return getComparatorListForClass(Number.class);
            }
            OperatorType[] stringCmps = getComparatorListForClass(String.class);
            OperatorType[] result = new OperatorType[stringCmps.length + 2];
            int c = 0;
            for (OperatorType ot : stringCmps) {
                result[c++] = ot;
            }
            result[c++] = SpQueryField.OperatorType.GREATERTHAN;
            result[c++] = SpQueryField.OperatorType.LESSTHAN;
            //result[c++] = SpQueryField.OperatorType.BETWEEN;
            return result;
        }
        //else
        return getComparatorListForClass(dataClass);
    }

    /**
     * @param classObj
     * @return
     */
    public static SpQueryField.OperatorType[] getComparatorListForClass(final Class<?> classObj) {
        if (classObj != null) {
            if (classObj.equals(String.class)) {
                return new SpQueryField.OperatorType[] { SpQueryField.OperatorType.CONTAINS,
                        SpQueryField.OperatorType.LIKE, SpQueryField.OperatorType.EQUALS,
                        SpQueryField.OperatorType.IN, SpQueryField.OperatorType.BETWEEN,
                        SpQueryField.OperatorType.EMPTY };
            }
            if (classObj.equals(Boolean.class)) {
                return new SpQueryField.OperatorType[] { SpQueryField.OperatorType.DONTCARE,
                        SpQueryField.OperatorType.TRUE, SpQueryField.OperatorType.FALSE,
                        SpQueryField.OperatorType.TRUEORNULL, SpQueryField.OperatorType.FALSEORNULL,
                        SpQueryField.OperatorType.EMPTY };
            }
            if (classObj.equals(java.sql.Timestamp.class)) {
                return new SpQueryField.OperatorType[] { SpQueryField.OperatorType.EQUALS,
                        SpQueryField.OperatorType.GREATERTHAN, SpQueryField.OperatorType.LESSTHAN,
                        SpQueryField.OperatorType.BETWEEN, SpQueryField.OperatorType.EMPTY };
            }
        }
        return new SpQueryField.OperatorType[] { SpQueryField.OperatorType.EQUALS,
                SpQueryField.OperatorType.GREATERTHAN, SpQueryField.OperatorType.LESSTHAN,
                SpQueryField.OperatorType.GREATERTHANEQUALS, SpQueryField.OperatorType.LESSTHANEQUALS,
                SpQueryField.OperatorType.BETWEEN, SpQueryField.OperatorType.IN, SpQueryField.OperatorType.EMPTY };
    }

    /**
     * @return
    */
    public SortElement getOrderSpec(int pos) {
        Byte sortType;
        if (ownerQuery.isPromptMode()) {
            sortType = (byte) sortCheckbox.getState();
        } else {
            sortType = queryField.getSortType();
        }
        if (sortType.equals(SpQueryField.SORT_NONE)) {
            return null;
        }

        int direction = sortType.equals(SpQueryField.SORT_ASC) ? SortElement.ASCENDING : SortElement.DESCENDING;
        return new SortElement(pos, direction);
    }

    /**
     * @return true if a condition has been specified for the field.
     */
    protected boolean hasCriteria() {
        if (fieldQRI.getDataClass().equals(Boolean.class)) {
            return !operatorCBX.getSelectedItem().equals(SpQueryField.OperatorType.DONTCARE);
        }
        if (operatorCBX.getSelectedItem().equals(SpQueryField.OperatorType.EMPTY)) {
            return true;
        }
        return StringUtils.isNotEmpty(getCriteriaText(false).trim());
    }

    /**
      * @param criteriaEntry - String of comma-delimited entries
      * @return Array of formatted criteria
      * @throws ParseException
      */
    protected Object[] parseCriteria(final String origCriteriaEntry) throws ParseException {
        String[] raw;
        UIFieldFormatterIFace formatter = fieldQRI.getFormatter();
        boolean isNumericCatNum = (formatter instanceof CatalogNumberUIFieldFormatter
                && ((CatalogNumberUIFieldFormatter) formatter).isNumeric());
        String criteriaEntry = origCriteriaEntry;
        if (isNumericCatNum) {
            criteriaEntry = CatalogNumberFormatter.preParseNumericCatalogNumbersWithSeries(origCriteriaEntry,
                    formatter);
        }
        if (operatorCBX.getSelectedItem() == SpQueryField.OperatorType.BETWEEN
                || operatorCBX.getSelectedItem() == SpQueryField.OperatorType.IN
                || formatter instanceof CatalogNumberUIFieldFormatter) //',' in numeric catnums cause stack traces, and they are invalid in string catnums so don't allow them)
        {
            raw = criteriaEntry.split(",");
        } else {
            raw = new String[1];
            raw[0] = criteriaEntry;
        }

        if (operatorCBX.getSelectedItem() == SpQueryField.OperatorType.BETWEEN) {
            if (raw.length != 2) {
                throw new ParseException(getLabel() + " - " + UIRegistry.getResourceString("QB_INVALID_CRITERIA"),
                        -1);
            }
        } else if (operatorCBX.getSelectedItem() != SpQueryField.OperatorType.IN) {
            if (raw.length != 1) {
                throw new ParseException(getLabel() + " - " + UIRegistry.getResourceString("QB_INVALID_CRITERIA"),
                        -1);
            }
        }

        Object[] result = new Object[raw.length];
        for (int e = 0; e < raw.length; e++) {
            try {
                if (isNumericCatNum) {
                    Pair<String, String> series = getSerStartEnd(raw[e].trim());
                    formatter.formatFromUI(series.getFirst());
                    formatter.formatFromUI(series.getSecond());
                    if (!series.getSecond().equals(raw[e].trim())) {
                        result[e] = series;
                    } else {
                        result[e] = series.getFirst();
                    }
                } else {
                    result[e] = formatter != null ? formatter.formatFromUI(raw[e].trim()) : raw[e].trim();
                }
            } catch (Exception ex) {
                throw new ParseException(getLabel() + " - "
                        + String.format(UIRegistry.getResourceString("QB_PARSE_ERROR"), ex.getLocalizedMessage()),
                        -1);
            }
        }
        return result;
    }

    /**
     * @param ser
     * @return
     * @throws Exception
     */
    protected Pair<String, String> getSerStartEnd(String ser) throws Exception {
        String start = null;
        String end = null;
        if (!ser.contains("-")) {
            start = ser;
            end = ser;
        } else if (ser.endsWith("-")) {
            throw new Exception("Bad series format.");
        } else {
            String[] parts = ser.split("-");
            if (parts.length != 2) {
                throw new Exception("Bad series format.");
            }
            start = parts[0];
            if (parts[1].length() >= parts[0].length()) {
                end = parts[1];
            } else {
                end = start.substring(0, start.length() - parts[1].length()) + parts[1];
            }
        }
        return new Pair<String, String>(start, end);
    }

    /**
     * @param escapee
     * @param escaper
     * WRONG:@return escapee with occurrences of escapee preceded by '\'. Pike's Peak -> Pike\'s Peak
     * @return escapee with occurrences of escapee doubled. Pike's Peak -> Pike''s Peak
     * 
     * This actually only works for "'". 
     * Hibernate (but not MySQL) complains when % and \' are both contained in a criteriummmm
     * 
     * Too bad if the escaper is already escaped.
     */
    protected Object escape(final Object escapee, final char escaper) {
        //XXX may be MySQL -specific?
        if (escaper == ' ') {
            return escapee;
        }
        if (!(escapee instanceof String)) {
            throw new RuntimeException("Escapee is not a String!");
        }
        String escapeeStr = (String) escapee;
        StringBuilder result = new StringBuilder();
        for (int c = 0; c < escapeeStr.length(); c++) {
            if (escapeeStr.charAt(c) == escaper) {
                result.append(escaper);
            }
            result.append(escapeeStr.charAt(c));
        }
        return result.toString();
    }

    /**
     * @param criteriaObjs
     * @param operatorStr
     * @param quote - if true then items will be surrounded with single quotes.
     * @return comma-delimited list of items in criteriaObjs.
     */
    @SuppressWarnings("unchecked")
    protected String concatCriteria(final Object[] criteriaObjs, final String operatorStr, final boolean quote,
            final TableAbbreviator ta) {
        //XXX '%' as wildcard may be db vendor -specific??

        char quoteStr = quote ? '\'' : ' ';
        String result = quoteStr + escape(criteriaObjs[0], quoteStr).toString() + quoteStr;
        if (SpQueryField.OperatorType.getOrdForOp(operatorStr) == SpQueryField.OperatorType.LIKE.getOrdinal()
                || SpQueryField.OperatorType.getOrdForOp(operatorStr) == SpQueryField.OperatorType.CONTAINS
                        .getOrdinal()) {
            //for Specify 5 compatibility...?
            //replaced unescaped '*' with '%'
            if (result.contains("*")) {
                //grrrrrrrr
                StringBuilder newResult = new StringBuilder();
                int skip = -1;
                for (int s = 0; s < result.length(); s++) {

                    if (skip != s && result.charAt(s) == '\\') {
                        skip = s + 1;
                    }
                    if (skip != s && result.charAt(s) == '*') {
                        newResult.append('%');
                    } else {
                        newResult.append(result.charAt(s));
                    }
                    if (skip == s) {
                        skip = -1;
                    }
                }
                result = newResult.toString();
            }

            boolean unEscapedWildcard = false;
            boolean skip = false;
            int s = 0;
            while (!unEscapedWildcard && s < result.length()) {
                if (skip) {
                    skip = false;
                } else if (result.charAt(s) == '\\') {
                    skip = true;
                } else if (result.charAt(s) == '%') {
                    unEscapedWildcard = true;
                }
                s++;
            }

            if (SpQueryField.OperatorType.getOrdForOp(operatorStr) == SpQueryField.OperatorType.CONTAINS
                    .getOrdinal()) {
                //if user didn't purposely include a wildcard then add them
                result = quoteStr + "%" + result.substring(1, result.length() - 1) + "%" + quoteStr;
            }

        } else if (SpQueryField.OperatorType.getOrdForOp(operatorStr) == SpQueryField.OperatorType.BETWEEN
                .getOrdinal()) {
            result += " and " + quoteStr + escape(criteriaObjs[1], quoteStr) + quoteStr;
        } else if (SpQueryField.OperatorType.getOrdForOp(operatorStr) == SpQueryField.OperatorType.IN
                .getOrdinal()) {
            result = "";
            List<Pair<String, String>> sers = new ArrayList<Pair<String, String>>();
            for (int p = 0; p < criteriaObjs.length; p++) {
                if (criteriaObjs[p] instanceof String) {
                    if (!"".equals(result)) {
                        result += ",";
                    }
                    result += "" + quoteStr + escape(criteriaObjs[p], quoteStr) + quoteStr;
                } else {
                    Pair<String, String> ser = (Pair<String, String>) criteriaObjs[p];
                    ser.setFirst("" + quoteStr + escape(ser.getFirst(), quoteStr) + quoteStr);
                    ser.setSecond("" + quoteStr + escape(ser.getSecond(), quoteStr) + quoteStr);
                    sers.add(ser);
                }
            }
            if (!"".equals(result) && !(fieldQRI instanceof TreeLevelQRI)) {
                result = fieldQRI.getSQLFldSpec(ta, true, schemaItem != null, getFormatName()) + " " + operatorStr
                        + "(" + result + ")";
            }
            for (Pair<String, String> ser : sers) {
                if (!"".equals(result)) {
                    result += " OR ";
                }
                result += fieldQRI.getSQLFldSpec(ta, true, schemaItem != null, getFormatName()) + " BETWEEN "
                        + ser.getFirst() + " AND " + ser.getSecond();
            }
            result = "(" + result + ")";
        }
        return result;
    }

    /**
     * @return
     */
    public boolean isNegated() {
        return isNotCheckbox != null && isNotCheckbox.isSelected();
    }

    /**
     * @return true if criteria entries should be handled as numeric cat nums for hql/sql
     * 
     * NOTE: "where catalogNumber = 1000" works in hql even though catalogNumber is a string field.
     * This MAY be because MySQL will automatically convert string/numeric types when necessary.
     * If we switch to another sql dbms, catalogNumbers may have to be treated as strings.
     * 
     */
    protected boolean isNumericCatalogNumber() {
        UIFieldFormatterIFace formatter = fieldQRI.getFormatter();
        return formatter instanceof CatalogNumberUIFieldFormatter
                && ((CatalogNumberUIFieldFormatter) formatter).isNumeric();
    }

    /**
     * @return true is the the 'Empty' operator criteria operator is selected
     */
    public boolean isEmptyCriterion() {
        return operatorCBX.getSelectedItem().equals(SpQueryField.OperatorType.EMPTY);
    }

    /**
     * @param op
     * @return
     */
    protected String getOperatorQLText() {
        return SpQueryField.OperatorType
                .getOp(((SpQueryField.OperatorType) operatorCBX.getSelectedItem()).getOrdinal());
    }

    /**
     * @return
     */
    @SuppressWarnings("unchecked")
    public String getCriteriaFormula(final TableAbbreviator ta, final List<Pair<String, Object>> paramList)
            throws ParseException {
        if (operatorCBX.getSelectedItem().equals(SpQueryField.OperatorType.EMPTY)) {
            boolean isNot = isNotCheckbox.isSelected();
            String nullCond = fieldQRI.getNullCondition(ta, schemaItem != null, isNot, getFormatName());
            if (fieldQRI.getDataClass().equals(String.class)) {
                String fieldSpec = fieldQRI.getSQLFldSpec(ta, true, schemaItem != null, getFormatName());
                return "(" + nullCond + (isNot ? " and " : " or ") + fieldSpec + (isNot ? " <> " : " = ") + "'')";
            }
            return nullCond;
        }

        if (hasCriteria()) {
            boolean addNullConjunction = false;
            boolean nullPick = criteria instanceof PickListCriteriaCombo
                    && ((PickListCriteriaCombo) criteria).nullItemIsPicked();
            Object[] criteriaStrs = parseCriteria(getCriteriaText(true).trim());
            String criteriaFormula = "";
            //String operStr = operatorCBX.getSelectedItem().toString();
            String operStr = getOperatorQLText();
            if (!(criteriaStrs[0] instanceof String) && !(criteriaStrs[0] instanceof Pair)) {
                //XXX - If the field has a formatter and it returned non-String data
                // then assume all parsing and conversion has been accomplished??
                //(hopefully this will never occur)
                log.info(fieldQRI.getFieldInfo() + ": formatter returned non-string data.");
                criteriaFormula = concatCriteria(criteriaStrs, operStr, false, ta);
            } else {
                if (fieldQRI.getDataClass().equals(Boolean.class)) {
                    if (operStr.equals(SpQueryField.OperatorType.getOp(SpQueryField.OperatorType.TRUE.getOrdinal()))
                            || operStr.equals(SpQueryField.OperatorType
                                    .getOp(SpQueryField.OperatorType.TRUEORNULL.getOrdinal()))) {
                        criteriaFormula = "true";
                    } else {
                        criteriaFormula = "false";
                    }
                    addNullConjunction = operStr.equals(
                            SpQueryField.OperatorType.getOp(SpQueryField.OperatorType.FALSEORNULL.getOrdinal()))
                            || operStr.equals(SpQueryField.OperatorType
                                    .getOp(SpQueryField.OperatorType.TRUEORNULL.getOrdinal()));
                    operStr = "=";
                } else if (fieldQRI.getDataClass().equals(String.class) && !isNumericCatalogNumber()) {
                    criteriaFormula = concatCriteria(criteriaStrs, operStr,
                            !(pickList instanceof PickListTableAdapter), ta);
                } else if (fieldQRI.getDataClass().equals(Calendar.class)
                        || fieldQRI.getDataClass().equals(java.sql.Timestamp.class)) {
                    for (int p = 0; p < criteriaStrs.length; p++) {
                        String paramName = "spparam" + paramList.size();
                        try {
                            if (fieldQRI instanceof DateAccessorQRI) {
                                new Integer((String) criteriaStrs[p]);
                            } else {
                                Object arg = dateConverter.convert((String) criteriaStrs[p]);
                                if (fieldQRI.getDataClass().equals(java.sql.Timestamp.class)) {
                                    arg = new java.sql.Timestamp(((Calendar) arg).getTimeInMillis());
                                }
                                paramList.add(new Pair<String, Object>(paramName, arg));
                            }
                        } catch (ParseException ex) {
                            throw new ParseException(getLabel() + " - " + String.format(
                                    UIRegistry.getResourceString("QB_PARSE_ERROR"), ex.getLocalizedMessage()), -1);
                        }
                        if (p > 0) {
                            if (operatorCBX.getSelectedItem() == SpQueryField.OperatorType.BETWEEN) {
                                criteriaFormula += " and ";
                            } else {
                                criteriaFormula += ", ";
                            }
                        }
                        if (fieldQRI instanceof DateAccessorQRI) {
                            criteriaFormula += (String) criteriaStrs[p];
                        } else {
                            criteriaFormula += ":" + paramName;
                        }
                    }
                    if (SpQueryField.OperatorType.getOrdForOp(operStr) == SpQueryField.OperatorType.IN
                            .getOrdinal()) {
                        criteriaFormula = "(" + criteriaFormula + ")";
                    }
                } else if (Number.class.isAssignableFrom(fieldQRI.getDataClass()) || isNumericCatalogNumber()) {
                    Constructor<?> tester;
                    try {
                        tester = isNumericCatalogNumber() ? BigInteger.class.getConstructor(String.class)
                                : fieldQRI.getDataClass().getConstructor(String.class);
                        for (int s = 0; s < criteriaStrs.length; s++) {
                            Object critter = criteriaStrs[s];
                            List<String> strs = new ArrayList<String>(2);
                            if (critter instanceof String) {
                                strs.add(critter.toString());
                            } else {
                                //seriesPresent = true;
                                if (!operStr.equals(SpQueryField.OperatorType
                                        .getOp(SpQueryField.OperatorType.IN.getOrdinal()))) {
                                    throw new ParseException(getLabel() + " - "
                                            + UIRegistry.getResourceString("QB_INVALID_CRITERIA"), -1);
                                }
                                strs.add(((Pair<String, String>) critter).getFirst());
                                strs.add(((Pair<String, String>) critter).getSecond());
                            }
                            List<String> newStrs = new ArrayList<String>(2);
                            for (String str : strs) {
                                tester.newInstance(str);

                                //remove leading zeroes
                                String newString = str;
                                boolean isZeroes = false;
                                while (newString.startsWith("0")) {
                                    newString = newString.substring(1);
                                    isZeroes = true;
                                }
                                if (isZeroes && StringUtils.isBlank(newString)) {
                                    newString = "0";
                                }
                                newStrs.add(newString);
                            }
                            if (newStrs.size() == 2) {
                                ((Pair<String, String>) criteriaStrs[s]).setFirst(newStrs.get(0));
                                ((Pair<String, String>) criteriaStrs[s]).setSecond(newStrs.get(1));
                            } else {
                                criteriaStrs[s] = newStrs.get(0);
                            }
                        }
                    } catch (NoSuchMethodException ex) {
                        edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                        edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryFieldPanel.class, ex);
                        // this will never happen. trust me.
                        throw new RuntimeException(ex);
                    } catch (InvocationTargetException ex) {
                        if (ex.getTargetException() instanceof NumberFormatException) {
                            String msg = ex.getTargetException().getLocalizedMessage();
                            if (StringUtils.isBlank(msg)) {
                                msg = ex.getTargetException().getClass().getSimpleName();

                            }
                            throw new ParseException(
                                    getLabel() + " - "
                                            + String.format(UIRegistry.getResourceString("QB_PARSE_ERROR"), msg),
                                    -1);
                        }
                        throw new RuntimeException(ex);
                    } catch (IllegalAccessException ex) {
                        edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                        edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryFieldPanel.class, ex);
                        throw new RuntimeException(ex);
                    } catch (InstantiationException ex) {
                        edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
                        edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(QueryFieldPanel.class, ex);
                        throw new RuntimeException(ex);
                    } catch (NumberFormatException ex) {
                        String msg = ex.getLocalizedMessage();
                        if (StringUtils.isBlank(msg)) {
                            msg = ex.getClass().getSimpleName();
                        }
                        throw new ParseException(getLabel() + " - "
                                + String.format(UIRegistry.getResourceString("QB_PARSE_ERROR"), msg), -1);
                    }
                    criteriaFormula = concatCriteria(criteriaStrs, operStr, false, ta);
                }
            }
            if (operStr.equals(SpQueryField.OperatorType.getOp(SpQueryField.OperatorType.CONTAINS.getOrdinal()))) {
                operStr = "Like";
            }

            if (criteriaFormula.length() > 0 || nullPick || fieldQRI instanceof TreeLevelQRI) {
                if (fieldQRI instanceof TreeLevelQRI) {
                    try {
                        return ((TreeLevelQRI) fieldQRI).getNodeNumberCriteria(criteriaFormula, ta, operStr,
                                isNotCheckbox.isSelected());
                    } catch (ParseException pe) {
                        throw new ParseException(getLabel() + " - " + String
                                .format(UIRegistry.getResourceString("QB_PARSE_ERROR"), pe.getLocalizedMessage()),
                                -1);

                    }
                }

                StringBuilder str = new StringBuilder();

                str.append(isNotCheckbox.isSelected() ? "(NOT " : "");
                if (!operStr.equals(SpQueryField.OperatorType.getOp(SpQueryField.OperatorType.IN.getOrdinal()))) {
                    str.append(fieldQRI.getSQLFldSpec(ta, true, schemaItem != null, getFormatName()) + " ");
                }
                if (nullPick && "=".equals(operStr)) {
                    str.append(" is null ");
                } else if (!operStr
                        .equals(SpQueryField.OperatorType.getOp(SpQueryField.OperatorType.IN.getOrdinal()))) {
                    str.append(operStr);
                }
                str.append(" ");
                if (!(nullPick && "=".equals(operStr))) {
                    str.append(criteriaFormula);
                }
                if (isNotCheckbox.isSelected()) {
                    if (!operStr.equals(
                            SpQueryField.OperatorType.getOp(SpQueryField.OperatorType.EMPTY.getOrdinal()))) {
                        str.append(" or " + fieldQRI.getSQLFldSpec(ta, true, schemaItem != null, getFormatName())
                                + " is null");
                    }
                    str.append(")");
                }
                String result = str.toString();
                if (addNullConjunction || (StringUtils.isNotBlank(result) && isEnforcedCkbx != null
                        && isEnforcedCkbx.isSelected() && schemaMapping != null)
                        || (nullPick && !"=".equals(operStr))) {
                    //Currently, when the null value is picked with the IN condition, a '' entry is included in the IN list
                    //This is not technically correct, but probably will never matter, and possibly produce more desirable 
                    //results then the technically correct criteria 
                    result = "(" + result + " or "
                            + fieldQRI.getSQLFldSpec(ta, true, schemaItem != null, getFormatName()) + " is null)";
                }
                return result;
            }
        }
        return null;
    }

    /**
     * @return the fieldQRI
     */
    public FieldQRI getFieldQRI() {
        return fieldQRI;
    }

    protected JTextField createTextField(final String id) {
        ValTextField textField = new ValTextField();
        textField.setRequired(false);
        validator.hookupTextField(textField, id, false, UIValidator.Type.Changed, "", true);
        return textField;
    }

    protected PickListCriteriaCombo createPickList(final Component saveBtn) {
        PickListCriteriaCombo result = new PickListCriteriaCombo(pickList);
        if (!ownerQuery.isPromptMode()) {
            result.addActionListener(new ActionListener() {

                /* (non-Javadoc)
                 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
                 */
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (e.getID() == 1001/*ComboBoxChanged*/) {
                        if (saveBtn != null) {
                            saveBtn.setEnabled(true);
                        }
                    }
                }

            });
        }
        return result;
    }

    protected JCheckBox createCheckBox(final String id) {
        ValCheckBox checkbox = new ValCheckBox("", false, false);
        DataChangeNotifier dcn = validator.createDataChangeNotifer(id, checkbox, null);
        checkbox.addActionListener(dcn);
        return checkbox;
    }

    protected JComboBox createComboBox(SpQueryField.OperatorType[] items) {
        ValComboBox cbx = new ValComboBox(items, false);
        DataChangeNotifier dcn = validator.hookupComponent(cbx, "cbx", UIValidator.Type.Changed, "", true);
        cbx.getComboBox().addActionListener(dcn);
        return cbx.getComboBox();
    }

    /**
     * @param iconSize
     * @param returnWidths
     * @return
     */
    protected int[] buildControlLayout(final IconManager.IconSize iconSize, final boolean returnWidths,
            final Component saveBtn) {
        FocusListener focusListener = new FocusListener() {

            /*
             * (non-Javadoc)
             * 
             * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
             */
            // @Override
            public void focusGained(FocusEvent e) {
                //Checking oppositeComponent to work around
                //weird behavior after addBtn is clicked which
                //causes top queryFieldPanel to be selected.
                if (ownerQuery.getAddBtn() != null && e.getOppositeComponent() != ownerQuery.getAddBtn()) {
                    ownerQuery.selectQFP(QueryFieldPanel.this);
                }

            }

            /*
             * (non-Javadoc)
             * 
             * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
             */
            // @Override
            public void focusLost(FocusEvent e) {
                // nada
            }

        };

        KeyListener enterListener = new KeyListener() {

            @Override
            public void keyPressed(KeyEvent arg0) {
                //nuthin
            }

            @Override
            public void keyReleased(KeyEvent arg0) {
                //nuthin
            }

            @Override
            public void keyTyped(KeyEvent arg0) {
                if (arg0.getKeyChar() == KeyEvent.VK_ENTER && ownerQuery != null) {
                    ownerQuery.doSearch();
                }

            }

        };

        comparators = getComparatorList(fieldQRI);
        //XXX need to build schemaItem for header panel too...
        if (schemaMapping != null) {
            schemaItemCBX = edu.ku.brc.ui.UIHelper.createComboBox();
            schemaItemCBX.addItem("empty"); //to work around validator blow up for empty lists.
            DataChangeNotifier dcnsi = validator.hookupComponent(schemaItemCBX, "sicbx", UIValidator.Type.Changed,
                    "", true);
            schemaItemCBX.addActionListener(dcnsi);

            schemaItemCBX.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    if (!QueryFieldPanel.this.ownerQuery.isUpdatingAvailableConcepts()) {
                        if (e.getStateChange() == ItemEvent.SELECTED) {
                            if (e.getItem() instanceof SpExportSchemaItem) {
                                QueryFieldPanel.this.schemaItem = (SpExportSchemaItem) e.getItem();
                            } else {
                                SpExportSchemaItemMapping m = QueryFieldPanel.this.getItemMapping();
                                SpExportSchemaItem si = QueryFieldPanel.this.schemaItem;
                                String item = e.getItem().toString();
                                if (StringUtils.isNotBlank(item)
                                        && ownerQuery.isAvailableExportFieldName(QueryFieldPanel.this, item)) {
                                    if (m != null) {
                                        m.setExportedFieldName(e.getItem().toString());
                                    }
                                    if (si != null) {
                                        si.setFieldName(e.getItem().toString());
                                    }
                                } else {
                                    if (StringUtils.isBlank(item)) {
                                        UIRegistry.displayErrorDlgLocalized(
                                                "QueryFieldPanel.ExportFieldNameInvalid", item);
                                    } else {
                                        UIRegistry.displayErrorDlgLocalized(
                                                "QueryFieldPanel.ExportFieldNameAlreadyUsed", item);
                                    }
                                    schemaItemCBX.setSelectedIndex(0);
                                }
                            }
                            ownerQuery.updateAvailableConcepts();
                        }
                    }
                }
            });
        } else {
            schemaItemCBX = null;
        }

        iconLabel = new JLabel(icon);
        iconLabel.addFocusListener(focusListener);
        String fieldLabelText = fieldQRI != null ? fieldQRI.getTitle() : "WXYZ";
        if (fieldQRI instanceof RelQRI) {
            DBRelationshipInfo.RelationshipType relType = ((RelQRI) fieldQRI).getRelationshipInfo().getType();
            if (relType.equals(DBRelationshipInfo.RelationshipType.OneToMany)
                    || relType.equals(DBRelationshipInfo.RelationshipType.ManyToMany)) {
                fieldLabelText += " " + UIRegistry.getResourceString("QB_AGGREGATED");
            } else {
                fieldLabelText += " " + UIRegistry.getResourceString("QB_FORMATTED");
            }

        }
        fieldLabel = createLabel(fieldLabelText);
        fieldLabel.addFocusListener(focusListener);
        fieldLabel.addKeyListener(enterListener);
        isNotCheckbox = createCheckBox("isNotCheckbox");
        isNotCheckbox.addFocusListener(focusListener);
        isNotCheckbox.addKeyListener(enterListener);
        operatorCBX = createComboBox(comparators);
        operatorCBX.addFocusListener(focusListener);
        operatorCBX.addKeyListener(enterListener);
        boolean isBool = fieldQRI != null && fieldQRI.getDataClass().equals(Boolean.class);
        if (!isBool) {
            operatorCBX.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    if (e.getStateChange() == ItemEvent.SELECTED) {
                        criteria.setVisible(!operatorCBX.getSelectedItem().equals(SpQueryField.OperatorType.EMPTY));
                    }
                }
            });
        }
        if (pickList == null) {
            boolean hasBetweenOp = false;
            for (int o = 0; o < comparators.length; o++) {
                if (comparators[o].equals(OperatorType.BETWEEN)) {
                    hasBetweenOp = true;
                    break;
                }
            }
            if (hasBetweenOp) {
                criteria = new CriteriaPair(enterListener);
                operatorCBX.addActionListener(this);
            } else {
                criteria = createTextField("1");
                criteria.addKeyListener(enterListener);
            }
        } else {
            criteria = createPickList(saveBtn);
            if (!ownerQuery.isPromptMode()) {
                ((PickListCriteriaCombo) criteria)
                        .setCurrentOp((SpQueryField.OperatorType) operatorCBX.getModel().getElementAt(0));
            }
            criteria.addKeyListener(enterListener);
            operatorCBX.addItemListener(new ItemListener() {

                /*
                 * (non-Javadoc)
                 * 
                 * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
                 */
                //@Override
                public void itemStateChanged(ItemEvent e) {
                    if (e.getStateChange() == ItemEvent.SELECTED) {
                        // System.out.println("setting curront op");
                        ((PickListCriteriaCombo) criteria)
                                .setCurrentOp((SpQueryField.OperatorType) operatorCBX.getSelectedItem());
                    }
                }

            });
        }
        //criteria.addFocusListener(focusListener);

        sortCheckbox = new MultiStateIconButon(
                new ImageIcon[] { IconManager.getImage("GrayDot", IconManager.IconSize.Std16),
                        IconManager.getImage("UpArrow", IconManager.IconSize.Std16),
                        IconManager.getImage("DownArrow", IconManager.IconSize.Std16) });
        DataChangeNotifier dcn = validator.hookupComponent(sortCheckbox, "scb", UIValidator.Type.Changed, "", true);
        sortCheckbox.addFocusListener(focusListener);
        sortCheckbox.addActionListener(dcn);
        sortCheckbox.addKeyListener(enterListener);
        if (!this.ownerQuery.isPromptMode()) {
            isEnforcedCkbx = createCheckBox("isEnforcedCkbx");
            dcn = validator.hookupComponent(isEnforcedCkbx, "iecb", UIValidator.Type.Changed, "", true);
            isEnforcedCkbx.addActionListener(dcn);
            isEnforcedCkbx.addFocusListener(focusListener);
            isEnforcedCkbx.addKeyListener(enterListener);
        }
        if (!this.ownerQuery.isPromptMode()) {
            isDisplayedCkbx = createCheckBox("isDisplayedCkbx");
            dcn = validator.hookupComponent(isDisplayedCkbx, "idcb", UIValidator.Type.Changed, "", true);
            isDisplayedCkbx.addFocusListener(focusListener);
            isDisplayedCkbx.addKeyListener(enterListener);
            isDisplayedCkbx.addActionListener(dcn);
            isDisplayedCkbx.addActionListener(new ActionListener() {

                /* (non-Javadoc)
                 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
                 */
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    SwingUtilities.invokeLater(new Runnable() {

                        /* (non-Javadoc)
                         * @see java.lang.Runnable#run()
                         */
                        @Override
                        public void run() {
                            sortCheckbox.setEnabled(isDisplayedCkbx.isSelected());
                            ownerQuery.changeNotification(QueryFieldPanel.this);
                        }
                    });
                }
            });
            isPromptCkbx = createCheckBox("isPromptCkbx");
            dcn = validator.hookupComponent(isPromptCkbx, "ipcb", UIValidator.Type.Changed, "", true);
            isPromptCkbx.addActionListener(dcn);
            isPromptCkbx.addFocusListener(focusListener);
            isPromptCkbx.addKeyListener(enterListener);
            closeBtn = edu.ku.brc.ui.UIHelper.createIconBtn("Close", "QB_REMOVE_FLD", new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    boolean clearIt = schemaItem != null;
                    ownerQuery.removeQueryFieldItem((QueryFieldPanel) ((JComponent) ae.getSource()).getParent());
                    if (clearIt) {
                        setField(null, null);
                    }
                }
            });
            closeBtn.setEnabled(true);
            closeBtn.setFocusable(false);
            closeBtn.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseEntered(MouseEvent e) {
                    ((JButton) e.getSource()).setIcon(IconManager.getIcon("CloseHover"));
                    super.mouseEntered(e);
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    ((JButton) e.getSource()).setIcon(IconManager.getIcon("Close"));
                    super.mouseExited(e);
                }

            });
        } else {
            isDisplayedCkbx = null;
            this.isPromptCkbx = null;
            this.isEnforcedCkbx = null;
            this.closeBtn = null;
        }

        JComponent[] qComps = { iconLabel, fieldLabel, isNotCheckbox, operatorCBX, criteria, sortCheckbox,
                isDisplayedCkbx, isPromptCkbx, isEnforcedCkbx, closeBtn, null };
        JComponent[] sComps = { schemaItemCBX, iconLabel, fieldLabel, isNotCheckbox, operatorCBX, criteria,
                sortCheckbox, isDisplayedCkbx, isEnforcedCkbx, closeBtn, null };
        // 0 1 2 3 4 5 6 7 8 9
        if (schemaMapping == null) {
            comps = qComps;
        } else {
            comps = sComps;
        }

        StringBuilder sb = new StringBuilder();
        Integer[] qFixedCmps = { 300 };
        Integer[] sFixedCmps = { 268, 300 };
        Integer[] fixedCmps;
        if (schemaMapping != null) {
            fixedCmps = sFixedCmps;
        } else {
            fixedCmps = qFixedCmps;
        }
        if (columnDefStr == null) {
            for (int i = 0; i < comps.length; i++) {
                sb.append(i == 0 ? "" : ",");
                if (isCenteredComp(i))
                    sb.append("c:");
                if (i >= fixedCmps.length) {
                    sb.append("p");
                } else {
                    sb.append(fixedCmps[i] + "px");
                }
                if (isGrowComp(i))
                    sb.append(":g");
                sb.append(",4px");
            }
        } else {
            sb.append(columnDefStr);
        }

        PanelBuilder builder = new PanelBuilder(new FormLayout("3px, " + sb.toString() + ", 3px", "3px, p, 3px"),
                this);
        CellConstraints cc = new CellConstraints();

        int col = 1;
        for (JComponent comp : comps) {
            if (comp != null) {
                builder.add(comp, cc.xy(col + 1, 2));
            }
            col += 2;
        }

        if (fieldQRI != null) {
            icon = IconManager.getIcon(fieldQRI.getTableInfo().getName(), iconSize);
            setIcon(icon);
        }
        if (!ownerQuery.isPromptMode()) {
            isDisplayedCkbx.setSelected(true);
            isPromptCkbx.setSelected(!(fieldQRI instanceof RelQRI));
            isEnforcedCkbx.setSelected(false);
        }

        if (fieldQRI == null && !returnWidths) {
            for (int c = 1; c < comps.length; c++) {
                if (comps[c] != null) {
                    comps[c].setVisible(false);
                }
            }
        } else {
            // for now
            boolean isRel = fieldQRI != null && fieldQRI instanceof RelQRI;
            boolean isTreeLevel = fieldQRI instanceof TreeLevelQRI;
            isNotCheckbox.setVisible(!isRel || pickList != null);
            operatorCBX.setVisible(!isRel || pickList != null);
            criteria.setVisible((!isRel && !isBool) || pickList != null);
            if (schemaMapping != null) {
                this.sortCheckbox.setVisible(!(isTreeLevel || isRel));
            } else {
                if (!isRel) {
                    this.sortCheckbox.setVisible(true);
                } else {
                    this.sortCheckbox.setVisible(
                            ((RelQRI) fieldQRI).getRelationshipInfo().getType() != RelationshipType.OneToMany);
                }
            }

            if (!ownerQuery.isPromptMode()) {
                isDisplayedCkbx.setVisible(!isRel);
                isPromptCkbx.setVisible(!isRel);
                isEnforcedCkbx.setVisible(!isRel);
            }
        }
        validate();
        doLayout();

        int[] widths = null;
        if (returnWidths) {
            widths = new int[comps.length];
            for (int i = 0; i < comps.length; i++) {
                widths[i] = comps[i] != null ? comps[i].getSize().width : 0;
            }
            if (this.schemaMapping == null) {
                widths[0] = iconSize.size();
                widths[1] = 200;
            } else {
                widths[1] = iconSize.size();
                widths[2] = 200;
            }
        }
        return widths;
    }

    /**
     * @param compIdx
     * @return true if comps[compIdx] should be centered
     */
    protected boolean isCenteredComp(int compIdx) {
        if (schemaMapping == null) {
            return compIdx == 1 || compIdx == 2 || compIdx == 5 || compIdx == 6 || compIdx == 7;
        } else {
            return compIdx == 2 || compIdx == 3 || compIdx == 6 || compIdx == 7 || compIdx == 8;
        }

    }

    /**
     * @param compIdx
     * @return true if comps[compIdx] should grow.
     */
    protected boolean isGrowComp(int compIdx) {
        return schemaMapping == null ? compIdx == 4 : compIdx == 5;
    }

    /**
     * @param iconSize
     * @param widths
     */
    protected void buildLabelLayout(final int[] widths) {

        StringBuilder sb = new StringBuilder();
        JLabel[] labels = new JLabel[labelStrs.length];
        int[] labelWidths = new int[labelStrs.length];
        for (int i = 0; i < labels.length; i++) {
            labels[i] = createLabel(labelStrs[i], SwingConstants.CENTER);
            labelWidths[i] = Math.max(widths[i], labels[i].getPreferredSize().width);
            //System.out.println(labelStrs[i]+"  "+labelWidths[i]);
        }

        for (int i = 0; i < labels.length; i++) {
            sb.append(i == 0 ? "" : ",");
            if (isCenteredComp(i))
                sb.append("c:");
            sb.append("max(");
            sb.append(labelWidths[i]);
            sb.append(";p)");
            if (isGrowComp(i))
                sb.append(":g");
            sb.append(",4px");
        }

        //System.out.println(sb.toString());
        columnDefStr = sb.toString();

        PanelBuilder builder = new PanelBuilder(new FormLayout(sb.toString(), "p"), this);
        CellConstraints cc = new CellConstraints();

        int x = 1;
        for (JLabel label : labels) {
            builder.add(label, cc.xy(x, 1));
            x += 2;
        }
    }

    /**
     * Split apart the name keying on upper case
     * @param nameToFix the name of the field
     * @return the split apart name
     */
    protected String fixName(final String nameToFix) {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < nameToFix.length(); i++) {
            if (i == 0) {
                s.append(Character.toUpperCase(nameToFix.charAt(i)));
            } else {
                char c = nameToFix.charAt(i);
                if (Character.isUpperCase(c)) {
                    s.append(' ');
                }
                s.append(c);
            }
        }
        return s.toString();
    }

    /**
     * @param icon
     */
    public void setIcon(ImageIcon icon) {
        this.icon = icon == null ? blankIcon : icon;
        iconLabel.setIcon(this.icon);
    }

    /**
     * @return the TableInfo object
     */
    public DBFieldInfo getFieldInfo() {
        if (fieldQRI != null) {
            return fieldQRI.getFieldInfo();
        }
        return null;
    }

    /**
     * Returns the field name.
     * @return the field name.
     */
    public String getFieldName() {
        return fieldLabel.getText();
    }

    /**
     * @return true if field is displayed in results
     */
    public boolean isForDisplay() {
        if (ownerQuery.isPromptMode()) {
            if (queryField != null) {
                return queryField.getIsDisplay();
            } else {
                return true;
            }
        } else {
            return isDisplayedCkbx.isSelected();
        }
    }

    /**
     * @return the field label text
     */
    public String getLabel() {
        return this.fieldLabel.getText();
    }

    /**
     * @return the schemaItem combo box
     */
    public JComboBox getSchemaItemCBX() {
        return this.schemaItemCBX;
    }

    /**
     * @return the labelQualified
     */
    public boolean isLabelQualified() {
        return labelQualified;
    }

    /**
     * @param otherLabels
     * @param unQualify if true then label is un-qualified.
     * @return a label for the field that is 'qualified' to distinguish it from other labels with the same title.
     */
    public String qualifyLabel(final List<String> otherLabels, final boolean unQualify) {
        boolean needToQualify;
        List<String> labels;
        //the following block is not currently used, but keeping it here in case the current strategy
        //doesn't work out.
        if (otherLabels == null) {
            needToQualify = false;
            labels = new ArrayList<String>(ownerQuery.getFields() - 1);
            for (int i = 0; i < this.ownerQuery.getFields(); i++) {
                QueryFieldPanel p = ownerQuery.getField(i);
                if (this != p) {
                    labels.add(p.getLabel());
                    if (p.getFieldTitle().equals(getFieldTitle())) {
                        needToQualify = true;
                    }
                }
            }
        } else {
            needToQualify = !unQualify;
            labels = otherLabels;
        }

        if (needToQualify) {
            String newLabel = getFieldTitle();
            TableTree parent = fieldQRI.getTableTree();
            int checkParent = 1;
            do {
                newLabel = getQualifiedLabel(parent, checkParent-- > 0);
                parent = parent.getParent();
            } while (parent != null && labels.indexOf(newLabel) != -1 && !parent.getName().equals("root"));

            labelQualified = true;
            fieldLabel.setText(newLabel);
        } else {
            labelQualified = false;
            fieldLabel.setText(getFieldTitle());
        }
        return fieldLabel.getText();
    }

    /**
     * @return the title of the field.
     */
    protected String getFieldTitle() {
        if (fieldQRI != null) {
            return fieldQRI.getTitle();
        }
        return null;
    }

    /**
     * @param parent
     * @param checkParent
     * @return
     */
    protected String getQualifiedLabel(final TableTree parent, final boolean checkParent) {
        TableTree reParent = parent;
        if (checkParent && reParent.getTableInfo().getClassObj().equals(Agent.class)
                && (StringUtils.isEmpty(reParent.getField())
                        || reParent.getName().equalsIgnoreCase(reParent.getField()))
                && reParent.getParent().getTableQRI() != null)
        // agent (and what others??) generally offers no informative distinguishing info
        {
            reParent = reParent.getParent();
        }
        return reParent.getTableQRI().getTitle() + "/" + getFieldTitle();
    }

    /* (non-Javadoc)
     * @see javax.swing.JComponent#paint(java.awt.Graphics)
     */
    @Override
    public void paint(Graphics g) {
        // TODO Auto-generated method stub
        super.paint(g);

        if (selected) {
            //this block was copied from RolloverCommand.paintComp()

            g.setColor(RolloverCommand.getActiveColor());
            Insets insets = getInsets();
            insets.set(1, 1, 1, 1);
            Dimension size = getSize();
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            RoundRectangle2D.Double rr = new RoundRectangle2D.Double(insets.left, insets.top,
                    size.width - insets.right - insets.left, size.height - insets.bottom - insets.top, 10, 10);
            g2d.draw(rr);
            rr = new RoundRectangle2D.Double(insets.left + 1, insets.top + 1,
                    size.width - insets.right - insets.left - 2, size.height - insets.bottom - insets.top - 2, 10,
                    10);
            g2d.draw(rr);
        }
    }

    /**
     * @return the selected
     */
    public boolean isSelected() {
        return selected;
    }

    /**
     * @param selected the selected to set
     */
    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    /**
     * @return true if this field's criteria should ALWAYS be applied.
     */
    public boolean isEnforced() {
        if (isEnforcedCkbx != null) {
            return isEnforcedCkbx.isSelected();
        }
        if (queryField != null) {
            return queryField.getAlwaysFilter();
        }
        return false;
    }

    /**
     * @return true if nulls are allowed. i.e: if 'or is null' should be appended to the field's criteria.
     */
    public boolean isAllowNulls() {
        //XXX until/if allowNulls is added to general queries, the isEnforcedCkbx is being used to 
        //access it
        if (isEnforcedCkbx != null) {
            return isEnforcedCkbx.isSelected();
        }
        if (queryField != null) {
            return queryField.getAllowNulls();
        }
        return false;
    }

    /**
     * @return a string identifier unique to this field within the query that is independent of the field's title.
     */
    public String getStringId() {
        return fieldQRI.getStringId();
    }

    /**
     * @return the pickList
     */
    public PickListDBAdapterIFace getPickList() {
        return pickList;
    }

    /* (non-Javadoc)
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource().equals(operatorCBX)) {
            OperatorType op = (OperatorType) operatorCBX.getSelectedItem();
            if (op != null && op.equals(OperatorType.BETWEEN)) {
                ((CriteriaPair) criteria).setShowingPair(true);
            } else {
                if (criteria instanceof CriteriaPair) {
                    ((CriteriaPair) criteria).setShowingPair(false);
                }
            }
        }
    }

    /**
     * @return the schemaItem
     */
    public SpExportSchemaItem getSchemaItem() {
        return schemaItem;
    }

    /**
     * @return the itemMapping
     */
    public SpExportSchemaItemMapping getItemMapping() {
        return queryField != null ? queryField.getMapping() : null;
    }

    /**
     * @return the autoMapped
     */
    public boolean isAutoMapped() {
        return autoMapped;
    }

    /**
     * @param autoMapped the autoMapped to set
     */
    public void setAutoMapped(boolean autoMapped) {
        this.autoMapped = autoMapped;
    }

}