/* * 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. */ /* * SqlViewer.java * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand * */ package weka.gui.sql; import weka.core.Memory; import weka.gui.LookAndFeel; 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 weka.gui.sql.event.ResultChangedEvent; import weka.gui.sql.event.ResultChangedListener; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.Properties; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JFrame; import javax.swing.JPanel; /** * Represents a little tool for querying SQL databases. * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 4724 $ */ public class SqlViewer extends JPanel implements ConnectionListener, HistoryChangedListener, QueryExecuteListener, ResultChangedListener { /** for serialization. */ private static final long serialVersionUID = -4395028775566514329L; /** the name of the history file (in the home directory). */ protected final static String HISTORY_FILE = "SqlViewerHistory.props"; /** the width property in the history file. */ public final static String WIDTH = "width"; /** the height property in the history file. */ public final static String HEIGHT = "height"; /** the parent of this panel. */ protected JFrame m_Parent; /** the connection panel. */ protected ConnectionPanel m_ConnectionPanel; /** the query panel. */ protected QueryPanel m_QueryPanel; /** the result panel. */ protected ResultPanel m_ResultPanel; /** the info panel. */ protected InfoPanel m_InfoPanel; /** the connect string with which the query was run. */ protected String m_URL; /** the user that was used to connect to the DB. */ protected String m_User; /** the password that was used to connect to the DB. */ protected String m_Password; /** the currently selected query. */ protected String m_Query; /** stores the history. */ protected Properties m_History; /** * initializes the SqlViewer. * * @param parent the parent of this panel */ public SqlViewer(JFrame parent) { super(); m_Parent = parent; m_URL = ""; m_User = ""; m_Password = ""; m_Query = ""; m_History = new Properties(); createPanel(); } /** * builds the interface. */ protected void createPanel() { JPanel panel; JPanel panel2; setLayout(new BorderLayout()); // connection m_ConnectionPanel = new ConnectionPanel(m_Parent); panel = new JPanel(new BorderLayout()); add(panel, BorderLayout.NORTH); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Connection"), BorderFactory.createEmptyBorder(0, 5, 5, 5))); panel.add(m_ConnectionPanel, BorderLayout.CENTER); // query m_QueryPanel = new QueryPanel(m_Parent); panel = new JPanel(new BorderLayout()); add(panel, BorderLayout.CENTER); panel2 = new JPanel(new BorderLayout()); panel2.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Query"), BorderFactory.createEmptyBorder(0, 5, 5, 5))); panel2.add(m_QueryPanel, BorderLayout.NORTH); panel.add(panel2, BorderLayout.NORTH); // result m_ResultPanel = new ResultPanel(m_Parent); m_ResultPanel.setQueryPanel(m_QueryPanel); panel2 = new JPanel(new BorderLayout()); panel2.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Result"), BorderFactory.createEmptyBorder(0, 5, 5, 5))); panel2.add(m_ResultPanel, BorderLayout.CENTER); panel.add(panel2, BorderLayout.CENTER); // info m_InfoPanel = new InfoPanel(m_Parent); panel = new JPanel(new BorderLayout()); add(panel, BorderLayout.SOUTH); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Info"), BorderFactory.createEmptyBorder(0, 5, 5, 5))); panel.add(m_InfoPanel, BorderLayout.CENTER); // listeners addConnectionListener(this); addConnectionListener(m_QueryPanel); addQueryExecuteListener(this); addQueryExecuteListener(m_ResultPanel); addResultChangedListener(this); addHistoryChangedListener(this); // history loadHistory(true); } /** * This method gets called when the connection is either established * or disconnected. * * @param evt the event */ public void connectionChange(ConnectionEvent evt) { if (evt.getType() == ConnectionEvent.DISCONNECT) { m_InfoPanel.append( "disconnect from: " + evt.getDbUtils().getDatabaseURL(), "information_small.gif" ); } else { m_InfoPanel.append( "connecting to: " + evt.getDbUtils().getDatabaseURL() + " = " + evt.isConnected(), "information_small.gif" ); } // did an exception happen? if (evt.getException() != null) m_InfoPanel.append("exception: " + evt.getException(), "error_small.gif"); // set focus if (evt.isConnected()) m_QueryPanel.setFocus(); else m_ConnectionPanel.setFocus(); } /** * This method gets called when a query has been executed. * * @param evt the event */ public void queryExecuted(QueryExecuteEvent evt) { ResultSetHelper helper; if (evt.failed()) { m_InfoPanel.append("Query:" + evt.getQuery(), "error_small.gif"); m_InfoPanel.append("exception: " + evt.getException(), "error_small.gif"); } else { m_InfoPanel.append("Query: " + evt.getQuery(), "information_small.gif"); try { if (evt.hasResult()) { helper = new ResultSetHelper(evt.getResultSet()); if ((evt.getMaxRows() > 0) && (helper.getRowCount() >= evt.getMaxRows())) m_InfoPanel.append(helper.getRowCount() + " rows selected (" + evt.getMaxRows() + " displayed).", "information_small.gif"); else if (helper.getRowCount() == -1) m_InfoPanel.append("Unknown number of rows selected (due to JDBC driver restrictions).", "information_small.gif"); else m_InfoPanel.append(helper.getRowCount() + " rows selected.", "information_small.gif"); } // save max rows loadHistory(false); m_History.setProperty( QueryPanel.MAX_ROWS, Integer.toString(evt.getMaxRows())); saveHistory(); } catch (Exception e) { e.printStackTrace(); } } } /** * This method gets called when a query has been executed. * * @param evt the event */ public void resultChanged(ResultChangedEvent evt) { m_URL = evt.getURL(); m_User = evt.getUser(); m_Password = evt.getPassword(); m_Query = evt.getQuery(); } /** * This method gets called when a history is modified. * It saves the history immediately to the users home directory. * * @param evt the event */ public void historyChanged(HistoryChangedEvent evt) { // load history, in case some other process changed it! loadHistory(false); m_History.setProperty( evt.getHistoryName(), modelToString(evt.getHistory())); // save it saveHistory(); } /** * returns the filename of the history file. * * @return the history file */ protected String getHistoryFilename() { return System.getProperties().getProperty("user.home") + File.separatorChar + HISTORY_FILE; } /** * transforms the given, comma-separated string into a DefaultListModel. * * @param s the string to break up and transform into a list model * @return the generated DefaultListModel */ protected DefaultListModel stringToModel(String s) { DefaultListModel result; String tmpStr; int i; boolean quote; String[] find; String[] replace; int index; result = new DefaultListModel(); // get rid of doubled quotes, \\n, etc. find = new String[]{"\"\"", "\\n", "\\r", "\\t"}; replace = new String[]{"\"", "\n", "\r", "\t"}; for (i = 0; i < find.length; i++) { tmpStr = ""; while (s.length() > 0) { index = s.indexOf(find[i]); if (index > -1) { tmpStr += s.substring(0, index) + replace[i]; s = s.substring(index + 2); } else { tmpStr += s; s = ""; } } s = tmpStr; } quote = false; tmpStr = ""; for (i = 0; i < s.length(); i++) { if (s.charAt(i) == '"') { quote = !quote; tmpStr += "" + s.charAt(i); } else if (s.charAt(i) == ',') { if (quote) { tmpStr += "" + s.charAt(i); } else { if (tmpStr.startsWith("\"")) tmpStr = tmpStr.substring(1, tmpStr.length() - 1); result.addElement(tmpStr); tmpStr = ""; } } else { tmpStr += "" + s.charAt(i); } } // add last element if (!tmpStr.equals("")) { if (tmpStr.startsWith("\"")) tmpStr = tmpStr.substring(1, tmpStr.length() - 1); result.addElement(tmpStr); } return result; } /** * converts the given model into a comma-separated string. * * @param m the model to convert * @return the string representation of the model */ protected String modelToString(DefaultListModel m) { String result; String tmpStr; int i; int n; boolean quote; result = ""; for (i = 0; i < m.size(); i++) { if (i > 0) result += ","; tmpStr = m.get(i).toString(); quote = (tmpStr.indexOf(",") > -1) || (tmpStr.indexOf(" ") > -1); if (quote) result += "\""; for (n = 0; n < tmpStr.length(); n++) { // double quotes if (tmpStr.charAt(n) == '"') result += "" + "\"\""; // normal character else result += "" + tmpStr.charAt(n); } if (quote) result += "\""; } return result; } /** * loads the history properties of the SqlViewer in the user's home directory. * * @param set whether to set the read properties in the panels or not * @see #HISTORY_FILE */ protected void loadHistory(boolean set) { BufferedInputStream str; File file; int width; int height; try { file = new File(getHistoryFilename()); if (file.exists()) { str = new BufferedInputStream( new FileInputStream(getHistoryFilename())); m_History.load(str); } } catch (Exception e) { e.printStackTrace(); } // set the histories if (set) { m_ConnectionPanel.setHistory( stringToModel( m_History.getProperty(ConnectionPanel.HISTORY_NAME, ""))); m_QueryPanel.setHistory( stringToModel( m_History.getProperty(QueryPanel.HISTORY_NAME, ""))); m_QueryPanel.setMaxRows( Integer.parseInt(m_History.getProperty(QueryPanel.MAX_ROWS, "100"))); width = Integer.parseInt(m_History.getProperty(WIDTH, "0")); height = Integer.parseInt(m_History.getProperty(HEIGHT, "0")); if ((width != 0) && (height != 0)) setPreferredSize(new Dimension(width, height)); } } /** * saves the history properties of the SqlViewer in the user's home directory. * * @see #HISTORY_FILE */ protected void saveHistory() { BufferedOutputStream str; try { str = new BufferedOutputStream( new FileOutputStream(getHistoryFilename())); m_History.store(str, "SQL-Viewer-History"); } catch (Exception e) { e.printStackTrace(); } } /** * obtains the size of the panel and saves it in the history. * * @see #saveHistory() */ public void saveSize() { m_History.setProperty(WIDTH, "" + getSize().width); m_History.setProperty(HEIGHT, "" + getSize().height); saveHistory(); } /** * calls the clear method of all sub-panels to set back to default values * and free up memory. */ public void clear() { m_ConnectionPanel.clear(); m_QueryPanel.clear(); m_ResultPanel.clear(); m_InfoPanel.clear(); } /** * returns the database URL from the currently active tab in the ResultPanel, * otherwise an empty string. * * @see ResultPanel * @return the currently selected tab's URL */ public String getURL() { return m_URL; } /** * returns the user from the currently active tab in the ResultPanel, * otherwise an empty string. * * @see ResultPanel * @return the currently selected tab's user */ public String getUser() { return m_User; } /** * returns the password from the currently active tab in the ResultPanel, * otherwise an empty string. * * @see ResultPanel * @return the currently selected tab's password */ public String getPassword() { return m_Password; } /** * returns the query from the currently active tab in the ResultPanel, * otherwise an empty string. * * @see ResultPanel * @return the currently selected tab's query */ public String getQuery() { return m_Query; } /** * adds the given listener to the list of listeners. * * @param l the listener to add to the list */ public void addConnectionListener(ConnectionListener l) { m_ConnectionPanel.addConnectionListener(l); } /** * removes the given listener from the list of listeners. * * @param l the listener to remove */ public void removeConnectionListener(ConnectionListener l) { m_ConnectionPanel.removeConnectionListener(l); } /** * adds the given listener to the list of listeners. * * @param l the listener to add to the list */ public void addQueryExecuteListener(QueryExecuteListener l) { m_QueryPanel.addQueryExecuteListener(l); } /** * removes the given listener from the list of listeners. * * @param l the listener to remove */ public void removeQueryExecuteListener(QueryExecuteListener l) { m_QueryPanel.removeQueryExecuteListener(l); } /** * adds the given listener to the list of listeners. * * @param l the listener to add to the list */ public void addResultChangedListener(ResultChangedListener l) { m_ResultPanel.addResultChangedListener(l); } /** * removes the given listener from the list of listeners. * * @param l the listener to remove */ public void removeResultChangedListener(ResultChangedListener l) { m_ResultPanel.removeResultChangedListener(l); } /** * adds the given listener to the list of listeners. * * @param l the listener to add to the list */ public void addHistoryChangedListener(HistoryChangedListener l) { m_ConnectionPanel.addHistoryChangedListener(l); m_QueryPanel.addHistoryChangedListener(l); } /** * removes the given listener from the list of listeners. * * @param l the listener to remove */ public void removeHistoryChangedListener(HistoryChangedListener l) { m_ConnectionPanel.removeHistoryChangedListener(l); m_QueryPanel.removeHistoryChangedListener(l); } /** for monitoring the Memory consumption. */ private static Memory m_Memory = new Memory(true); /** the sql viewer. */ private static SqlViewer m_Viewer; /** * starts the SQL-Viewer interface. * * @param args the commandline arguments - ignored */ public static void main(String[] args) { weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started"); LookAndFeel.setLookAndFeel(); try { // uncomment to disable the memory management: //m_Memory.setEnabled(false); final JFrame jf = new JFrame("Weka SQL-Viewer"); m_Viewer = new SqlViewer(jf); jf.getContentPane().setLayout(new BorderLayout()); jf.getContentPane().add(m_Viewer, BorderLayout.CENTER); jf.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { m_Viewer.saveSize(); jf.dispose(); System.exit(0); } }); jf.pack(); jf.setSize(800, 600); jf.setVisible(true); Thread memMonitor = new Thread() { public void run() { while (true) { try { this.sleep(4000); System.gc(); if (m_Memory.isOutOfMemory()) { // clean up jf.dispose(); m_Viewer = null; System.gc(); // stop threads m_Memory.stopThreads(); // display error System.err.println("\ndisplayed message:"); m_Memory.showOutOfMemory(); System.err.println("\nexiting"); System.exit(-1); } } catch (InterruptedException ex) { ex.printStackTrace(); } } } }; memMonitor.setPriority(Thread.MAX_PRIORITY); memMonitor.start(); } catch (Exception ex) { ex.printStackTrace(); System.err.println(ex.getMessage()); } } }