Java tutorial
/* Copyright (C) 2015, University of Kansas Center for Research * * Specify Software Project, specify@ku.edu, Biodiversity Institute, * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package edu.ku.brc.specify.ui; import static edu.ku.brc.ui.UIRegistry.getResourceString; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ScrollPaneConstants; 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.AppContextMgr; import edu.ku.brc.af.core.expresssearch.ExpressResultsTableInfo; import edu.ku.brc.af.core.expresssearch.ExpressSearchConfigCache; import edu.ku.brc.af.core.expresssearch.QueryAdjusterForDomain; import edu.ku.brc.af.core.expresssearch.QueryForIdResultsSQL; import edu.ku.brc.af.ui.ESTermParser; import edu.ku.brc.af.ui.SearchTermField; import edu.ku.brc.af.ui.db.ERTICaptionInfo; import edu.ku.brc.af.ui.db.QueryForIdResultsIFace; import edu.ku.brc.af.ui.db.ViewBasedSearchQueryBuilderIFace; import edu.ku.brc.af.ui.db.ERTICaptionInfo.ColInfo; import edu.ku.brc.af.ui.forms.FormDataObjIFace; import edu.ku.brc.af.ui.forms.FormViewObj; import edu.ku.brc.af.ui.forms.MultiView; import edu.ku.brc.af.ui.forms.ViewFactory; import edu.ku.brc.af.ui.forms.Viewable; import edu.ku.brc.af.ui.forms.persist.FormCellFieldIFace; import edu.ku.brc.af.ui.forms.persist.ViewIFace; import edu.ku.brc.dbsupport.DataProviderFactory; import edu.ku.brc.dbsupport.DataProviderSessionIFace; import edu.ku.brc.specify.tasks.subpane.ESResultsTablePanel; import edu.ku.brc.specify.tasks.subpane.ESResultsTablePanelIFace; import edu.ku.brc.specify.tasks.subpane.ExpressSearchResultsPaneIFace; import edu.ku.brc.ui.UIHelper; import edu.ku.brc.ui.UIRegistry; /** * This is a "generic" or more specifically "configurable" search panel class. This enables you to specify a form to be used to enter the search criteria * and then the search definition it is to use to do the search and display the results as a table in the dialog. The resulting class is to be passed in * on construction so the results of the search can actually yield a Hibernate object. * * NOTE: The second Constructor has not been tested! (It is designed as a single text field only that doesn't need a form view). * * @code_status Beta * * @author rods * */ @SuppressWarnings("serial") public class DBObjSearchPanel extends JPanel implements ExpressSearchResultsPaneIFace, PropertyChangeListener { private static final Logger log = Logger.getLogger(DBObjSearchDialog.class); protected SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // Form Stuff protected ViewIFace formView = null; protected Viewable form = null; protected List<String> fieldIds = new ArrayList<String>(); protected List<String> fieldNames = new ArrayList<String>(); protected ActionListener doQuery = null; // Members needed for creating results protected String className; protected String idFieldName; protected String searchName; protected String fieldName; // UI protected JButton okBtn; protected JTextField searchText; protected JPanel panel; protected JScrollPane scrollPane; //protected JTable table; protected JButton searchBtn; protected Color textBGColor = null; protected Color badSearchColor = new Color(255, 235, 235); protected Hashtable<String, ExpressResultsTableInfo> tables = new Hashtable<String, ExpressResultsTableInfo>(); protected ExpressResultsTableInfo esTableInfo; protected ESResultsTablePanelIFace etrb = null; protected List<Integer> idList = null; protected String sqlStr; protected Hashtable<String, Object> dataMap = new Hashtable<String, Object>(); protected ViewBasedSearchQueryBuilderIFace queryBuilder = null; protected QueryForIdResultsIFace queryForIdResults = null; protected boolean isMultipleSelection = true; /** * Constructs a search dialog from form infor and from search info. * @param viewSetName the viewset name * @param viewName the form name from the viewset * @param searchName the search name, this is looked up by name in the "search_config.xml" file * @param className the name of the class to be created from the selected results * @param idFieldName the name of the field in the clas that is the primary key which is filled in from the search table id * @throws HeadlessException an exception */ public DBObjSearchPanel(final String viewSetName, final String viewName, final String searchName, final String className, final String idFieldName, final int searchBtnPos) throws HeadlessException { //this((Window)parent, viewSetName, viewName, searchName, className, idFieldName); super(new BorderLayout()); this.className = className; this.idFieldName = idFieldName; this.searchName = searchName; init(); String rowDef; if (searchBtnPos == SwingConstants.TOP) { rowDef = "t:p"; } else if (searchBtnPos == SwingConstants.BOTTOM) { rowDef = "b:p"; } else { rowDef = "p"; } PanelBuilder pb = new PanelBuilder(new FormLayout("f:p:g,1dlu,p", rowDef + ",2px,p,10px,p")); CellConstraints cc = new CellConstraints(); formView = AppContextMgr.getInstance().getView(viewSetName, viewName); if (formView != null) { form = ViewFactory.createFormView(null, formView, null, dataMap, MultiView.NO_OPTIONS, null); } else { log.error("Couldn't load form with name [" + viewSetName + "] Id [" + viewName + "]"); } if (form != null) { fieldIds = new ArrayList<String>(); form.getFieldIds(fieldIds); for (String id : fieldIds) { Component comp = form.getCompById(id); if (comp instanceof JTextField) { ((JTextField) comp).addActionListener(doQuery); } } form.getFieldNames(fieldNames); createSearchBtn(); pb.add(form.getUIComponent(), cc.xy(1, 1)); pb.add(searchBtn, cc.xy(3, 1)); if (!"collectionobjectsearch".equalsIgnoreCase(searchName)) { pb.add(UIHelper.createI18NLabel("DBObjSearchPanel.ASTERISK_HINT"), cc.xyw(1, 3, 3)); } pb.addSeparator(UIRegistry.getResourceString("DBObjSearchPanel.RESULTS"), cc.xyw(1, 5, 3)); add(pb.getPanel(), BorderLayout.NORTH); createUI(); } else { log.error("ViewSet [" + viewSetName + "] View[" + viewName + "] could not be created."); } } /** * Constructs a search dialog from form infor and from search info. * @param viewSetName the viewset name * @param viewName the form name from the viewset * @param searchName the search name, this is looked up by name in the "search_config.xml" file * @param className the name of the class to be created from the selected results * @param idFieldName the name of the field in the clas that is the primary key which is filled in from the search table id * @throws HeadlessException an exception */ public DBObjSearchPanel(final String fieldName, final String className, final String idFieldName) throws HeadlessException { super(new BorderLayout()); this.className = className; this.idFieldName = idFieldName; this.fieldName = fieldName; init(); searchText = new JTextField(30); searchText.addActionListener(doQuery); searchText.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (searchText.getBackground() != textBGColor) { searchText.setBackground(textBGColor); } } }); PanelBuilder pb = new PanelBuilder(new FormLayout("p,1dlu,p", "p,2dlu,p,2dlu,p")); CellConstraints cc = new CellConstraints(); pb.add(searchText, cc.xy(1, 1)); pb.add(searchBtn, cc.xy(3, 1)); add(pb.getPanel(), BorderLayout.NORTH); createUI(); } /** * @return the searchName */ public String getSearchName() { return searchName; } /** * Gets the data from the UI whether it is using a form or a single field. */ protected void getDataFromUI() { if (form != null) { form.getDataFromUI(); } else { dataMap.put(fieldName, searchText.getText().trim()); } } /** * Common initializer. * @param viewSetName the viewset name * @param viewName the form name from the viewset */ protected void init() { doQuery = new ActionListener() { public void actionPerformed(final ActionEvent e) { doStartQuery((JComponent) e.getSource()); } }; if (queryBuilder == null) { ExpressResultsTableInfo esTblInfo = ExpressSearchConfigCache.getTableInfoByName(searchName); if (esTblInfo != null) { esTableInfo = esTblInfo; esTableInfo.setViewSQLOverridden(true); tables.put(esTableInfo.getTableId(), esTableInfo); sqlStr = esTableInfo.getViewSql(); } else { throw new RuntimeException( "Couldn't find search name[" + searchName + "] in the search_config.xml"); } } } /** * Creates the Search btn and hooks it up. */ protected void createSearchBtn() { searchBtn = UIHelper.createButton(getResourceString("SEARCH")); searchBtn.addActionListener(doQuery); } /** * Creates the Default UI. * */ public void createUI() { panel = new JPanel(new BorderLayout()); add(panel, BorderLayout.CENTER); panel.setPreferredSize(new Dimension(300, 200)); panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0), BorderFactory.createLineBorder(Color.BLACK))); } /** * @return the scroll pane for the results. */ public JScrollPane getScrollPane() { return scrollPane; } /** * @return the form */ public Viewable getForm() { return form; } /** * Sets the okBtn from the dialog. * @param okBtn the btn */ public void setOKBtn(final JButton okBtn) { this.okBtn = okBtn; updateUIControls(); } /** * Updates the OK button (sets whether it is enabled by checking to see if there is a recordset */ protected void updateUIControls() { if (okBtn != null) { okBtn.setEnabled(idList != null && ((idList.size() == 1 && !isMultipleSelection) || (idList.size() > 0 && isMultipleSelection))); } } /** * Makes sure the UI is enabled correctly. * @param enabled true/false */ protected void setUIEnabled(final boolean enabled) { form.getFieldIds(fieldIds); for (String fieldId : fieldIds) { Component comp = form.getCompById(fieldId); if (comp instanceof JTextField) { ((JTextField) comp).setEnabled(enabled); } } searchBtn.setEnabled(enabled); } /** * @param comp */ protected void doStartQuery(final JComponent comp) { getDataFromUI(); QueryForIdResultsIFace resultsInfo = null; if (queryBuilder != null) { sqlStr = queryBuilder.buildSQL(dataMap, fieldNames); if (StringUtils.isNotEmpty(sqlStr)) { resultsInfo = queryBuilder.createQueryForIdResults(); if (resultsInfo != null) { resultsInfo.setSQL(sqlStr); resultsInfo.setMultipleSelection(isMultipleSelection); } } else { UIRegistry.getStatusBar().setLocalizedErrorMessage("ES_SUSPICIOUS_SQL"); return; } } else { QueryAdjusterForDomain qafd = QueryAdjusterForDomain.getInstance(); StringBuilder strBuf = new StringBuilder(256); int cnt = 0; for (ERTICaptionInfo captionInfo : esTableInfo.getVisibleCaptionInfo()) { String colName = null; Object value = null; if (captionInfo.getColName() == null) { for (ColInfo colInfo : captionInfo.getColInfoList()) { colName = colInfo.getColumnName(); log.debug("colInfo - colInfoColumn Name[" + colName + "]"); value = dataMap.get(colName); if (value != null) { log.debug("Column Name[" + colName + "][" + captionInfo.getColLabel() + "] [" + captionInfo.getFieldInfo() + "] Value[" + value + "]"); break; } } } else { colName = captionInfo.getColName(); value = StringUtils.isNotEmpty(colName) ? dataMap.get(captionInfo.getColName()) : null; log.debug("Column Name[" + colName + "][" + captionInfo.getColLabel() + "] [" + captionInfo.getFieldInfo() + "] Value[" + value + "]"); } if (value != null) { String valStr = value.toString(); if (valStr.length() > 0) { if (qafd.isUserInputNotInjectable(valStr)) { if (ESTermParser.getInstance().parse(valStr.toLowerCase(), true)) { if (StringUtils.isNotEmpty(valStr)) { List<SearchTermField> fields = ESTermParser.getInstance().getFields(); SearchTermField firstTerm = fields.get(0); if (cnt > 0) { strBuf.append(" AND "); } String clause = null; if (captionInfo.getFieldInfo() != null && form instanceof FormViewObj) { FormViewObj fvo = (FormViewObj) form; FormViewObj.FVOFieldInfo fInfo = fvo.getFieldInfoForName(colName); if (fInfo != null) { if (fInfo.getFormCell() != null && fInfo.getFormCell() .getPropertyAsBoolean("ispartial", false)) { if (fInfo.getFormCell() instanceof FormCellFieldIFace) { FormCellFieldIFace cif = (FormCellFieldIFace) fInfo .getFormCell(); String fmt = cif.getUIFieldFormatterName(); if (StringUtils.isNotEmpty(fmt) && fmt.equals("SearchDate")) // XXX There is a better way to check for this (use the enum) { clause = getDateClause(firstTerm, colName); } } if (clause == null) { firstTerm.setTerm(firstTerm.getTermLowerCase()); firstTerm.setOption(SearchTermField.ENDS_WILDCARD); } } } } if (clause == null) { clause = ESTermParser.getInstance().createWhereClause(firstTerm, null, colName); } strBuf.append(clause); cnt++; } } } else { UIRegistry.getStatusBar().setErrorMessage(getResourceString("ES_SUSPICIOUS_SQL")); return; } } } /* else { log.debug("DataMap was null for Column Name["+captionInfo.getColName()+"] make sure there is a field of this name in the form."); }*/ } if (cnt == 0) { return; } String fullStrSql = QueryAdjusterForDomain.getInstance().adjustSQL(sqlStr); String fullSQL = fullStrSql.replace("%s", strBuf.toString()); log.info(fullSQL); setUIEnabled(false); resultsInfo = new QueryForIdResultsSQL(esTableInfo.getId(), null, esTableInfo, 0, ""); resultsInfo.setSQL(fullSQL); resultsInfo.setMultipleSelection(isMultipleSelection); } addSearchResults(resultsInfo); if (comp instanceof JTextField) { final JTextField txt = (JTextField) comp; int len = txt.getText().length(); txt.setSelectionEnd(len); txt.setSelectionStart(0); SwingUtilities.invokeLater(new Runnable() { public void run() { txt.requestFocus(); } }); } } /** * @param term * @return */ private String getDateClause(final SearchTermField term, final String columnName) { String termStr = term.getTermLowerCase(); if (termStr.length() >= 4) { try { String[] tokens = StringUtils.split(termStr, '-'); if (tokens.length == 1 || termStr.length() == 4) { sdf.parse(termStr + "-01-01"); return String.format(" YEAR(%s) = %s ", columnName, tokens[0]); } else if (tokens.length == 2) { sdf.parse(termStr + "-01"); return String.format(" (YEAR(%s) = %s AND MONTH(%s) = %s) ", columnName, tokens[0], columnName, tokens[1]); } else if (tokens.length == 3) { sdf.parse(termStr); return String.format(" %s = '%s' ", columnName, termStr); } } catch (ParseException ex) { } } return ""; } /* (non-Javadoc) * @see edu.ku.brc.specify.tasks.subpane.ExpressSearchResultsPaneIFace#addSearchResults(edu.ku.brc.af.core.expresssearch.QueryForIdResultsIFace) */ public void addSearchResults(final QueryForIdResultsIFace results) { idList = null; updateUIControls(); remove(panel != null ? panel : scrollPane); panel = null; if (etrb != null) { etrb.cleanUp(); } if (true) { ESResultsTablePanel etrbPanel = new ESResultsTablePanel(this, results, false, true, false); etrb = etrbPanel; etrb.setPropertyChangeListener(this); etrbPanel.startFilling(); } /* else { etrb = null; // Instantiate your class here etrb.initialize(this, results); etrb.setPropertyChangeListener(this); }*/ scrollPane = new JScrollPane(etrb.getUIComponent(), ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); add(scrollPane, BorderLayout.CENTER); validate(); setUIEnabled(true); repaint(); } /* (non-Javadoc) * @see edu.ku.brc.af.tasks.subpane.ExpressSearchResultsPaneIFace#removeTable(edu.ku.brc.af.tasks.subpane.ExpressTableResultsBase) */ public void removeTable(final ESResultsTablePanelIFace etrbTable) { etrbTable.cleanUp(); } /* (non-Javadoc) * @see edu.ku.brc.specify.tasks.subpane.ExpressSearchResultsPaneIFace#addTable(edu.ku.brc.specify.tasks.subpane.ExpressTableResultsBase) */ public void addTable(final ESResultsTablePanelIFace etrBase) { // It has already been added so don't do anything } /* (non-Javadoc) * @see edu.ku.brc.af.tasks.subpane.ExpressSearchResultsPaneIFace#revalidateScroll() */ public void revalidateScroll() { scrollPane.revalidate(); } /* (non-Javadoc) * @see edu.ku.brc.specify.tasks.subpane.ExpressSearchResultsPaneIFace#hasResults() */ public boolean hasResults() { return etrb.hasResults(); } /** * @return */ private Object getSelectedObjects(final boolean doAll) { List<Object> objList = null; String errMsg = null; if (idList != null && idList.size() > 0) { DataProviderSessionIFace session = null; try { session = DataProviderFactory.getInstance().createSession(); for (Integer id : idList) { log.debug("getSelectedObject class[" + className + "] idFieldName[" + idFieldName + "] id[" + id + "]"); Class<?> classObj = Class.forName(className); List<?> list = session.getDataList(classObj, idFieldName, id, DataProviderSessionIFace.CompareType.Restriction); if (list.size() == 1) { if (doAll) { if (objList == null) { objList = new Vector<Object>(); } ((FormDataObjIFace) list.get(0)).forceLoad(); objList.add(list.get(0)); } else { return list.get(0); } } else if (list.size() == 0) { errMsg = "Why could we NOT load the object with id[" + id + "] for class[" + className + "]in DBObjSearchDialog?"; } else { errMsg = "Why would more than one object be found in DBObjSearchDialog? return size[" + list.size() + "]"; } } return objList; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DBObjSearchPanel.class, ex); errMsg = ex.toString(); ex.printStackTrace(); } finally { if (session != null) { session.close(); } } } if (errMsg != null) { throw new RuntimeException(errMsg); } return null; } /** * @return */ public Object getSelectedObject() { return getSelectedObjects(false); } /** * @return */ @SuppressWarnings("unchecked") public List<Object> getSelectedObjects() { return (List<Object>) getSelectedObjects(true); } /* (non-Javadoc) * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("selection")) { Integer numRowsSelected = (Integer) evt.getOldValue(); if (numRowsSelected > 0) { idList = etrb.getListOfIds(false); } else { idList = null; } updateUIControls(); } else if (evt.getPropertyName().equals("doubleClick")) { okBtn.doClick(); } else if (evt.getPropertyName().equals("loaded")) { // If there is only one returned, then select it and focus the OK defult btn if (evt.getNewValue() != null && ((Integer) evt.getNewValue()) == 1) { ESResultsTablePanel etrbPanel = (ESResultsTablePanel) etrb; etrbPanel.getTable().setRowSelectionInterval(0, 0); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { okBtn.requestFocus(); } }); } } } /** * @param builder */ public void registerQueryBuilder(final ViewBasedSearchQueryBuilderIFace builder) { this.queryBuilder = builder; } /* (non-Javadoc) * @see edu.ku.brc.ui.db.QueryForIdResultsIFace#isMultipleSelection() */ public boolean isMultipleSelection() { return isMultipleSelection; } /* (non-Javadoc) * @see edu.ku.brc.ui.db.QueryForIdResultsIFace#setMultipleSelection(boolean) */ public void setMultipleSelection(boolean isMultiple) { isMultipleSelection = isMultiple; } /* (non-Javadoc) * @see edu.ku.brc.specify.tasks.subpane.ExpressSearchResultsPaneIFace#doQueriesSynchronously() */ public boolean doQueriesSynchronously() { return false; } /* (non-Javadoc) * @see edu.ku.brc.specify.tasks.subpane.ExpressSearchResultsPaneIFace#done() */ public void done() { // not needed } }