/*
* Copyright (C) SQL Explorer Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package net.sourceforge.sqlexplorer.history;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.sourceforge.sqlexplorer.ApplicationFiles;
import net.sourceforge.sqlexplorer.IConstants;
import net.sourceforge.sqlexplorer.dbproduct.Alias;
import net.sourceforge.sqlexplorer.dbproduct.Session;
import net.sourceforge.sqlexplorer.plugin.SQLExplorerPlugin;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.tree.DefaultElement;
import org.eclipse.core.runtime.ListenerList;
/**
* SQL History contains all statements that have been executed and is
* responsible for making these persistent.
*/
public class SQLHistory {
private static final String HISTORY = "history";
private List<SQLHistoryElement> _filteredHistory = new ArrayList<SQLHistoryElement>();
private List<SQLHistoryElement> _history = new ArrayList<SQLHistoryElement>();
private ListenerList _listeners = new ListenerList();
private String _qry = null;
private SQLHistorySorter _sorter = new SQLHistorySorter();
private static final String EXECUTION_HINT_MARKER = "#EH#";
private static final String NEWLINE_REPLACEMENT = "#LF#";
private static final String NEWLINE_SEPARATOR = System.getProperty("line.separator");
private static final String TAB_REPLACEMENT = "#T#";
private static final String SESSION_HINT_MARKER = "#SH#";
private static final String TIME_HINT_MARKER = "#TH#";
private int _autoSaveAfterCount = SQLExplorerPlugin.getDefault().getPluginPreferences().getInt(IConstants.HISTORY_AUTOSAVE_AFTER);
private int _queriesAdded = 0;
/**
* Default constructor. Initializes history with statements from file.
*/
public SQLHistory() {
// load all history from file
loadFromFile();
_qry = null;
}
/**
* Add a listener to the view, so we can properly refresh it if the sql
* history has changed.
*
* @param listener
*/
public void addListener(SQLHistoryChangedListener listener) {
_listeners.add(listener);
}
/**
* Add a query string to the sql history. New queries are added to the start
* of the list, so that the most recent entry is always located on the top
* of the history list
*
* @param newSql sql query string
*/
public void addSQL(String rawSqlString, Session session) {
if (rawSqlString == null
|| rawSqlString.equalsIgnoreCase("commit")
|| rawSqlString.trim().length() == 0) {
return;
}
for (int i = 0; i < _history.size(); i++) {
SQLHistoryElement el = (SQLHistoryElement) _history.get(i);
if (el.equals(rawSqlString)) {
_history.remove(i);
el.setUser(session.getUser());
el.increaseExecutionCount();
_history.add(0, el);
refreshHistoryView();
return;
}
}
_history.add(0, new SQLHistoryElement(rawSqlString, session.getUser()));
refreshHistoryView();
// check if we need to save the history
_queriesAdded++;
checkAutoSave();
}
/**
* Save the history if a number of statements have been executed.
*/
private void checkAutoSave() {
if (_autoSaveAfterCount > 0 && _queriesAdded >= _autoSaveAfterCount) {
_queriesAdded = 0;
save();
}
}
/**
* Clear all elements from SQL History
*/
public void clear() {
_history.clear();
refreshHistoryView();
}
/**
* @return number of entries available under current filtering options
*/
public int getEntryCount() {
if (_qry == null) {
return _history.size();
} else {
return _filteredHistory.size();
}
}
private void loadFromFile() {
if (new File(ApplicationFiles.SQLHISTORY_FILE_NAME_V300).exists())
loadFromFileV300();
else if (new File(ApplicationFiles.SQLHISTORY_FILE_NAME_V350).exists())
loadFromFileV350();
}
private void loadFromFileV350() {
try {
File file = new File(ApplicationFiles.SQLHISTORY_FILE_NAME_V350);
SAXReader reader = new SAXReader();
Element root = reader.read(file).getRootElement();
_history.clear();
_filteredHistory.clear();
for (Element elem : root.elements(SQLHistoryElement.ELEMENT))
_history.add(new SQLHistoryElement(elem));
}catch(DocumentException e) {
SQLExplorerPlugin.error("Cannot load history (v3.5.0 format)", e);
}
}
/**
* Load the sql history from previous sessions.
*/
private void loadFromFileV300() {
try {
File file = new File(ApplicationFiles.SQLHISTORY_FILE_NAME_V300);
if (!file.exists()) {
return;
}
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String currentLine = reader.readLine();
while (currentLine != null) {
if (currentLine.trim().length() != 0) {
String sessionHint = null;
String query = null;
String time = null;
String executions = null;
int pos = currentLine.indexOf(SESSION_HINT_MARKER);
if (pos != -1) {
// split line in session and query
sessionHint = currentLine.substring(0, pos);
currentLine = currentLine.substring(pos + SESSION_HINT_MARKER.length());
int posT = currentLine.indexOf(TIME_HINT_MARKER);
if (posT != -1) {
// split line in session and query
query = currentLine.substring(0, posT);
currentLine = currentLine.substring(posT + TIME_HINT_MARKER.length());
int posE = currentLine.indexOf(EXECUTION_HINT_MARKER);
time = currentLine.substring(0, posE);
executions = currentLine.substring(posE + EXECUTION_HINT_MARKER.length());
} else {
query = currentLine;
}
// clean up query
query = query.replaceAll(NEWLINE_REPLACEMENT, NEWLINE_SEPARATOR);
query = query.replaceAll(TAB_REPLACEMENT, " ");
}
if (query != null && query.trim().length() != 0) {
Alias alias = SQLExplorerPlugin.getDefault().getAliasManager().getAlias(sessionHint);
_history.add(new SQLHistoryElement(query, alias.getDefaultUser(), time, executions));
}
}
currentLine = reader.readLine();
}
reader.close();
} catch (Exception e) {
SQLExplorerPlugin.error("Couldn't load sql history.", e);
}
}
/**
* Update the view to show changed history
*/
public void refreshHistoryView() {
filter();
Object[] ls = _listeners.getListeners();
for (int i = 0; i < ls.length; ++i) {
try {
((SQLHistoryChangedListener) ls[i]).changed();
} catch (Throwable e) {
}
}
}
/**
* Remove an entry from the history.
*
* @param element SQLHistoryElement
*/
public void remove(SQLHistoryElement element) {
_history.remove(element);
refreshHistoryView();
}
/**
* Remove the listener to the view.
*
* @param listener
*/
public void removeListener(SQLHistoryChangedListener listener) {
_listeners.remove(listener);
}
/**
* Save all the used queries into a file, so that we can reuse them next
* time.
*/
public void save() {
try {
File file = new File(ApplicationFiles.SQLHISTORY_FILE_NAME_V350);
Element root = new DefaultElement(HISTORY);
for (SQLHistoryElement elem : _history)
root.add(elem.describeAsXml());
XMLWriter xmlWriter = new XMLWriter(new FileWriter(file), OutputFormat.createPrettyPrint());
xmlWriter.write(root);
xmlWriter.flush();
xmlWriter.close();
// Get rid of old versions
new File(ApplicationFiles.SQLHISTORY_FILE_NAME_V300).delete();
} catch (IOException e) {
SQLExplorerPlugin.error("Couldn't save sql history.", e);
}
}
/**
* Restrict displayed history by given filter string
*
* @param filter string
*/
public int setQryString(String qry) {
_qry = qry.trim().toLowerCase();
if (_qry != null && _qry.trim().length() == 0) {
_qry = null;
}
refreshHistoryView();
if (_qry == null) {
return _history.size();
} else {
return _filteredHistory.size();
}
}
/**
* Filter based on query string
*/
private void filter() {
if (_qry == null || _qry.trim().length() == 0) {
_qry = null;
return;
}
_filteredHistory = new ArrayList();
String[] keyword = _qry.split(" ");
Iterator it = _history.iterator();
while (it.hasNext()) {
SQLHistoryElement el = (SQLHistoryElement) it.next();
boolean include = true;
for (int i = 0; i < keyword.length; i++) {
// search SQL, session and dates
if ((el.getSearchableString().indexOf(keyword[i]) == -1)) {
include = false;
break;
}
}
if (include) {
_filteredHistory.add(el);
}
}
}
/**
* Change sorting.
*
* @param column index
* @param direction SWT.UP, SWT.DOWN, SWT.NONE
*/
public void sort(int column, int direction) {
_sorter.setSortColumn(column, direction);
Collections.sort(_history, _sorter);
Collections.sort(_filteredHistory, _sorter);
}
/**
* @return sorted array of filtered history elements
*/
public Object[] toArray() {
if (_qry == null) {
return _history.toArray();
} else {
return _filteredHistory.toArray();
}
}
}