content: /*
   *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  /*
   * QueryPanel.java
   * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
   *
   */
  
  package weka.gui.sql;
  
  import weka.gui.ListSelectorDialog;
  import weka.gui.sql.event.ConnectionEvent;
  import weka.gui.sql.event.ConnectionListener;
  import weka.gui.sql.event.HistoryChangedEvent;
  import weka.gui.sql.event.HistoryChangedListener;
  import weka.gui.sql.event.QueryExecuteEvent;
  import weka.gui.sql.event.QueryExecuteListener;
  
  import java.awt.BorderLayout;
  import java.awt.Dimension;
  import java.awt.FlowLayout;
  import java.awt.Font;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.sql.ResultSet;
  import java.util.HashSet;
  import java.util.Iterator;
  
  import javax.swing.DefaultListModel;
  import javax.swing.JButton;
  import javax.swing.JFrame;
  import javax.swing.JLabel;
  import javax.swing.JList;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.JSpinner;
  import javax.swing.JTextArea;
  import javax.swing.SpinnerNumberModel;
  import javax.swing.event.CaretEvent;
  import javax.swing.event.CaretListener;
  
  /**
   * Represents a panel for entering an SQL query.
   *
   * @author      FracPete (fracpete at waikato dot ac dot nz)
   * @version     $Revision: 1.4 $
   */
  public class QueryPanel 
    extends JPanel 
    implements ConnectionListener, CaretListener {
  
    /** for serialization. */
    private static final long serialVersionUID = 4348967824619706636L;
  
    /** the name of the history. */
    public final static String HISTORY_NAME = "query";
  
    /** the name for the max rows in the history. */
    public final static String MAX_ROWS = "max_rows";
    
    /** the parent of this panel. */
    protected JFrame m_Parent;
  
    /** the textarea for the query. */
    protected JTextArea m_TextQuery;
  
    /** the execute button. */
    protected JButton m_ButtonExecute = new JButton("Execute");
  
    /** the clear button. */
    protected JButton m_ButtonClear = new JButton("Clear");
  
    /** the history button. */
    protected JButton m_ButtonHistory = new JButton("History...");
  
    /** the spinner for the maximum number of rows. */
    protected JSpinner m_SpinnerMaxRows = new JSpinner();
  
    /** the connection listeners. */
    protected HashSet m_QueryExecuteListeners;
  
    /** the history listeners. */
    protected HashSet m_HistoryChangedListeners;
  
    /** for working on the database. */
    protected DbUtils m_DbUtils;
  
    /** whether we have a connection to a database or not. */
    protected boolean m_Connected;
  
    /** the query history. */
    protected DefaultListModel m_History = new DefaultListModel();
    
    /**
     * initializes the panel.
     * 
     * @param parent        the parent of this panel
     */
    public QueryPanel(JFrame parent) {
      super();
      
      m_Parent                  = parent;
      m_QueryExecuteListeners   = new HashSet();
      m_HistoryChangedListeners = new HashSet();
      m_DbUtils                 = null;
      m_Connected               = false;
  
      createPanel();
    }
  
    /**
     * creates the panel with all its components.
     */
    protected void createPanel() {
      JPanel              panel;
      JPanel              panel2;
      JPanel              panel3;
      SpinnerNumberModel  model;
      
      setLayout(new BorderLayout());
      
      // textarea
      m_TextQuery = new JTextArea();
      m_TextQuery.addCaretListener(this);
      m_TextQuery.setFont(
          new Font("Monospaced", Font.PLAIN, m_TextQuery.getFont().getSize()));
      add(new JScrollPane(m_TextQuery), BorderLayout.CENTER);
  
      // buttons
      panel = new JPanel(new BorderLayout());
      add(panel, BorderLayout.EAST);
      m_ButtonExecute.setMnemonic('E');
      m_ButtonExecute.addActionListener(new ActionListener() {
  	public void actionPerformed(ActionEvent e) {
  	  execute();
  	}
        });
      panel.add(m_ButtonExecute, BorderLayout.NORTH);
      panel2 = new JPanel(new BorderLayout());
      panel.add(panel2, BorderLayout.CENTER);
      m_ButtonClear.setMnemonic('r');
      m_ButtonClear.addActionListener(new ActionListener() {
  	public void actionPerformed(ActionEvent e) {
  	  clear();
  	}
        });
      panel2.add(m_ButtonClear, BorderLayout.NORTH);
      panel3 = new JPanel(new BorderLayout());
      panel2.add(panel3, BorderLayout.CENTER);
      m_ButtonHistory.addActionListener(new ActionListener() {
  	public void actionPerformed(ActionEvent e) {
  	  showHistory();
  	}
        });
      panel3.add(m_ButtonHistory, BorderLayout.NORTH);
  
      // limit
      panel3 = new JPanel(new FlowLayout());
      panel3.add(new JLabel("max. rows"));
      panel3.add(m_SpinnerMaxRows);
      panel2.add(panel3, BorderLayout.SOUTH);
      model = (SpinnerNumberModel) m_SpinnerMaxRows.getModel();
      model.setMaximum(new Integer(Integer.MAX_VALUE));
      model.setMinimum(new Integer(0));
      model.setValue(new Integer(100));
      model.setStepSize(new Integer(100));
      m_SpinnerMaxRows.setMinimumSize(
          new Dimension(50, m_SpinnerMaxRows.getHeight()));
      m_SpinnerMaxRows.setToolTipText("with 0 all rows are retrieved");
        
      // set initial state
      setButtons();
    }
  
    /**
     * sets the focus in a designated control.
     */
    public void setFocus() {
      m_TextQuery.requestFocus();
    }
  
    /**
     * sets the buttons according to the connected-state.
     */
    protected void setButtons() {
      boolean isEmpty;
      
      isEmpty = m_TextQuery.getText().trim().equals("");
      
      m_ButtonExecute.setEnabled((m_Connected) && (!isEmpty));
      m_ButtonClear.setEnabled(!isEmpty);
      m_ButtonHistory.setEnabled(m_History.size() > 0);
    }
    
    /**
     * This method gets called when the connection is either established
     * or disconnected.
     * 
     * @param evt		the event
     */
    public void connectionChange(ConnectionEvent evt) {
      m_Connected = evt.isConnected();
      m_DbUtils   = evt.getDbUtils();
      setButtons();
    }
  
    /**
     * executes the current query.
     */
    public void execute() {
      Exception     ex;
      ResultSet     rs;
      
      // not connected?
      if (!m_ButtonExecute.isEnabled())
        return;
  
      // no query?
      if (m_TextQuery.getText().trim().equals(""))
        return;
  
      // close old resultset
      try {
        if (m_DbUtils.getResultSet() != null)
          m_DbUtils.close();
      }
      catch (Exception e) {
        // ignore (if no resultset present we get an unncessary NullPointerEx.)
      }
  
      ex = null;
      rs = null;
      
      try {
        if (m_DbUtils.execute(getQuery())) {
          rs = m_DbUtils.getResultSet();
          // add to history
          addHistory(getQuery());
        }
      }
      catch (Exception e) {
        ex = new Exception(e.getMessage());
      }
  
      notifyQueryExecuteListeners(rs, ex);
  
      setButtons();
    }
  
    /**
     * clears the textarea.
     */
    public void clear() {
      m_TextQuery.setText("");
      m_SpinnerMaxRows.setValue(new Integer(100));
    }
  
    /**
     * adds the given string to the history (removes duplicates).
     * 
     * @param s           the string to add
     */
    protected void addHistory(String s) {
      if (s.equals(""))
        return;
      
      // no duplicates!
      if (m_History.contains(s))
        m_History.removeElement(s);
  
      m_History.add(0, s);
      
      // send notification
      notifyHistoryChangedListeners();
    }
  
    /**
     * sets the local history to the given one.
     * 
     * @param history     the history to use
     */
    public void setHistory(DefaultListModel history) {
      int           i;
      
      m_History.clear();
      for (i = 0; i < history.size(); i++)
        m_History.addElement(history.get(i));
  
      setButtons();
    }
  
    /**
     * returns the history.
     * 
     * @return        the current history
     */
    public DefaultListModel getHistory() {
      return m_History;
    }
  
    /**
     * displays the query history.
     */
    public void showHistory() {
      JList                 list;
      ListSelectorDialog    dialog;
  
      list   = new JList(m_History);
      dialog = new ListSelectorDialog(m_Parent, list);
      
      if (dialog.showDialog() == ListSelectorDialog.APPROVE_OPTION) {
        if (list.getSelectedValue() != null)
          setQuery(list.getSelectedValue().toString());
      }
  
      setButtons();
    }
  
    /**
     * sets the query in the textarea.
     * 
     * @param query         the query to display
     */
    public void setQuery(String query) {
      m_TextQuery.setText(query);
    }
  
    /**
     * returns the currently displayed query.
     * 
     * @return		the query
     */
    public String getQuery() {
      return m_TextQuery.getText();
    }
  
    /**
     * sets the maximum number of rows to display. 0 means unlimited.
     * 
     * @param rows	the maximum number of rows
     */
    public void setMaxRows(int rows) {
      if (rows >= 0)
        m_SpinnerMaxRows.setValue(new Integer(rows));
    }
  
    /**
     * returns the current value for the maximum number of rows. 0 means
     * unlimited.
     * 
     * @return		the maximum number of rows
     */
    public int getMaxRows() {
      return ((Integer) m_SpinnerMaxRows.getValue()).intValue();
    }
  
    /**
     * adds the given listener to the list of listeners.
     * 
     * @param l       the listener to add to the list
     */
    public void addQueryExecuteListener(QueryExecuteListener l) {
      m_QueryExecuteListeners.add(l);
    }
  
    /**
     * removes the given listener from the list of listeners.
     * 
     * @param l       the listener to remove
     */
    public void removeQueryExecuteListener(QueryExecuteListener l) {
      m_QueryExecuteListeners.remove(l);
    }
  
    /**
     * notifies the listeners of the event.
     * 
     * @param rs		the resultset
     * @param ex		the exception
     */
    protected void notifyQueryExecuteListeners(ResultSet rs, Exception ex) {
      Iterator              iter;
      QueryExecuteListener  l;
  
      iter = m_QueryExecuteListeners.iterator();
      while (iter.hasNext()) {
        l = (QueryExecuteListener) iter.next();
        l.queryExecuted(
            new QueryExecuteEvent(
              this, m_DbUtils, getQuery(), getMaxRows(), rs, ex));
      }
    }
  
    /**
     * adds the given listener to the list of listeners.
     * 
     * @param l       the listener to add to the list
     */
    public void addHistoryChangedListener(HistoryChangedListener l) {
      m_HistoryChangedListeners.add(l);
    }
  
    /**
     * removes the given listener from the list of listeners.
     * 
     * @param l       the listener to remove
     */
    public void removeHistoryChangedListener(HistoryChangedListener l) {
      m_HistoryChangedListeners.remove(l);
    }
  
    /**
     * notifies the history listeners of the event.
     */
    protected void notifyHistoryChangedListeners() {
      Iterator                iter;
      HistoryChangedListener  l;
  
      iter = m_HistoryChangedListeners.iterator();
      while (iter.hasNext()) {
        l = (HistoryChangedListener) iter.next();
        l.historyChanged(
            new HistoryChangedEvent(this, HISTORY_NAME, getHistory()));
      }
    }
  
    /**
     * Called when the caret position is updated.
     * 
     * @param event	the event
     */
    public void caretUpdate(CaretEvent event) {
      setButtons();
    }
  }
  
  