/*
Copyright (C) 2003 EBI, GRL
This library 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 org.ensembl.mart.explorer;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.ensembl.mart.lib.config.ConfigurationException;
import org.ensembl.mart.util.LoggingUtil;
/**
* MartExplorer is a graphical application that enables a
* user to construct queries and execute them against Mart databases.
*
* <p>
* A default registry containing several marts is loaded at
* startup. The user can provide her own initialisation file
* to replace the supplied one. The file should be
* called <b>.martj_adaptors.xml</b> and placed it in her
* home directory.
* </p>
* @author <a href="mailto:craig@ebi.ac.uk">Craig Melsopp</a>
*/
public class MartExplorer
extends JFrame
implements QueryEditorContext, ChangeListener {
// TODO test save/load query
// TODO chained queries
// TODO user resolve datasetConfig name space clashes
// TODO support user renaming queries
// TODO clone query
// TODO Query | Rename
private Logger logger = Logger.getLogger(MartExplorer.class.getName());
private final JFrame THIS_FRAME = this;
/**
* Name of the initial registry file to be loaded at startup.
* This file should be placed in the user's home directory.
* If the file does not exist then a default file is loaded
* instead.
*/
public final static String REGISTRY_FILE_NAME = ".userMartRegistry.xml";
/**
* Default registry file loaded at startup if none
* is found in the user's home directory.
*/
private final static String DEFAULT_REGISTRY_URL = "data/defaultMartRegistry.xml";
private static final String IMAGE_DIR = "data/image";
private AdaptorManager adaptorManager = new AdaptorManager();
private final static String TITLE = " Mart Explorer ";
private static final Dimension PREFERRED_SIZE = new Dimension(1024, 768);
/** Currently available datasets. */
private List datasetConfigs = new ArrayList();
/** Currently available databases. */
private List databaseDSConfigAdaptors = new ArrayList();
private JTabbedPane tabs = new JTabbedPane();
/** Persistent preferences object used to hold user history. */
private Preferences prefs = Preferences.userNodeForPackage(this.getClass());
private Feedback feedback = new Feedback(this);
final JCheckBox advanced = new JCheckBox("Optional Dataset Configuration");
final JCheckBox logging = new JCheckBox("Logging");
private Help help = new Help();
// ----------- ACTIONS ----------------------
private Action newQueryAction =
new AbstractAction("New Query", createImageIcon("new.gif")) {
public void actionPerformed(ActionEvent event) {
doNewQuery();
}
};
private Action executeAction =
new AbstractAction("Execute Query", createImageIcon("run.gif")) {
public void actionPerformed(ActionEvent event) {
if (isQueryEditorSelected())
getSelectedQueryEditor().doPreview();
}
};
private Action countFocusAction =
new AbstractAction("Count Focus", createImageIcon("count_focus.gif")) {
public void actionPerformed(ActionEvent event) {
if (isQueryEditorSelected())
getSelectedQueryEditor().doCountFocus();
}
};
private Action saveResultsAction =
new AbstractAction("Save Results", createImageIcon("save.gif")) {
public void actionPerformed(ActionEvent event) {
if (isQueryEditorSelected())
getSelectedQueryEditor().doSaveResults();
}
};
private Action saveResultsAsAction =
new AbstractAction("Save Results As", null) {
public void actionPerformed(ActionEvent event) {
if (isQueryEditorSelected())
getSelectedQueryEditor().doSaveResultsAs();
}
};
private Action stopAction =
new AbstractAction("Stop query", createImageIcon("stop.gif")) {
public void actionPerformed(ActionEvent event) {
doStop();
}
};
private Action saveAction = new AbstractAction("Save", null) {
public void actionPerformed(ActionEvent event) {
if (isQueryEditorSelected())
getSelectedQueryEditor().doSaveQuery();
}
};
private Action closeQueryAction = new AbstractAction("Close Query", null) {
public void actionPerformed(ActionEvent event) {
if (isQueryEditorSelected())
remove((QueryEditor) tabs.getSelectedComponent());
}
};
private Action documentationAction =
new AbstractAction("Documentation", null) {
public void actionPerformed(ActionEvent event) {
help.showDialog(THIS_FRAME);
}
};
// -------------- METHODS ----------------
public static void main(String[] args) throws ConfigurationException {
// LoggingUtil.setAllRootHandlerLevelsToFinest();
// Logger.getLogger(Query.class.getName()).setLevel(Level.FINE);
// if (!LoggingUtil.isLoggingConfigFileSet())
// Logger.getLogger("org.ensembl.mart").setLevel(Level.FINE);
MartExplorer me = new MartExplorer();
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
me.setVisible(true);
me.loadDefaultAdaptors();
if (me.getNumDatasetConfigsAvailable() > 0)
me.doNewQuery();
}
public MartExplorer() {
super(TITLE);
createUI();
doLogging(false);
tabs.addChangeListener(this);
}
/**
*
*/
private void loadDefaultAdaptors() {
disableCursor();
URL url = getClass().getClassLoader().getResource(DEFAULT_REGISTRY_URL);
String path =
System.getProperty("user.home") + File.separator + REGISTRY_FILE_NAME;
File file = new File(path);
if (file.exists())
try {
url = file.toURL();
} catch (MalformedURLException e) {
feedback.warning(e);
}
logger.fine("Loading default registry file: " + url);
adaptorManager.importRegistry(url);
enableCursor();
}
/**
*
*/
private void createUI() {
setJMenuBar(createMenuBar());
getContentPane().add(createToolBar(), BorderLayout.NORTH);
getContentPane().add(tabs, BorderLayout.CENTER);
setSize(PREFERRED_SIZE);
// add glass pane so we can "disable" cursor
JPanel p = new JPanel();
p.setOpaque(false);
p.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
Toolkit.getDefaultToolkit().beep();
}
});
setGlassPane(p);
}
/**
* Adds component to application as a tabbed pane. The tab's
* name id component.getName().
*/
private void addQueryEditor(QueryEditor component) {
component.addChangeListener(this);
tabs.add(component.getName(), component);
}
/**
*
* @return "Query_INDEX" where INDEX is the next highest
* unique number used in the tab names.
*/
private String nextQueryBuilderTabLabel() {
int next = 1;
int n = tabs.getTabCount();
Pattern p = Pattern.compile("Query_(\\d+)");
for (int i = 0; i < n; i++) {
String title = tabs.getTitleAt(i);
Matcher m = p.matcher(title);
if (m.matches()) {
int tmp = Integer.parseInt(m.group(1)) + 1;
if (tmp > next)
next = tmp;
}
}
return "Query_" + next;
}
private JToolBar createToolBar() {
JToolBar tb = new JToolBar();
tb.add(new ExtendedButton(newQueryAction, "Create a New Query"));
tb.add(new ExtendedButton(saveResultsAction, "Save Results to file"));
tb.addSeparator();
tb.add(new ExtendedButton(countFocusAction, "Count Number Of Focus objects"));
tb.add(new ExtendedButton(executeAction, "Execute Query"));
// does not scale for big tables
//tb.add(new ExtendedButton(countRowsAction, "Count Number of Focus objects"));
tb.add(new ExtendedButton(stopAction, "Stop running Query"));
return tb;
}
/**
*
*/
protected void doStop() {
if (isQueryEditorSelected())
getSelectedQueryEditor().doStop();
}
/**
* @return
*/
private JMenuBar createMenuBar() {
JMenu query = new JMenu("File");
JMenuItem newQuery = new JMenuItem(newQueryAction);
query.add(newQuery).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.CTRL_MASK));
JMenuItem open = new JMenuItem("Open Query");
open.setEnabled(false);
open.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
doLoadQueryFromMQL();
}
});
query.add(open).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_O, Event.CTRL_MASK));
JMenuItem saveQueryAsMQL = new JMenuItem("Save Query as MQL");
saveQueryAsMQL.setEnabled(false);
query.add(saveQueryAsMQL);
JMenuItem saveQueryAsSQL = new JMenuItem("Save Query as SQL");
saveQueryAsSQL.setEnabled(false);
query.add(saveQueryAsSQL);
JMenuItem close = new JMenuItem(closeQueryAction);
query.add(close).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_K, Event.CTRL_MASK));
JMenuItem closeAll = new JMenuItem("Close All Queries");
closeAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
while (tabs.getTabCount() > 0)
closeQueryAction.actionPerformed(null);
}
});
query.add(closeAll);
query.addSeparator();
JMenuItem execute = new JMenuItem(executeAction);
query.add(execute).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_E, Event.CTRL_MASK));
//query.add(new JMenuItem(countRowsAction));
query.add(new JMenuItem(countFocusAction));
query.addSeparator();
query.add(new JMenuItem(saveResultsAction)).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK));
query.add(new JMenuItem(saveResultsAsAction));
query.addSeparator();
JMenuItem exit_explorer = new JMenuItem("Exit");
exit_explorer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
doExit();
}
});
query.add(exit_explorer).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_E, Event.CTRL_MASK));
JMenu settings = new JMenu("Settings");
JMenuItem addDB = new JMenuItem("Add Mart");
settings.add(addDB).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_M, Event.CTRL_MASK));
//final JFrame parents = this;
addDB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
adaptorManager.doAddDatabase();
}
});
settings.add(addDB);
/**
JMenuItem adaptors = new JMenuItem("Add File");
settings.add(adaptors).setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK));
final JFrame parent = this;
adaptors.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
adaptorManager.showDialog(parent);
}
});
*/
JMenu advancedMenu = new JMenu("Enable");
settings.add(advancedMenu);
//advanced.setToolTipText(
// "Enables optional DatasetConfigs.");
advanced.setSelected(adaptorManager.isAdvancedOptionsEnabled());
advancedMenu.add(advanced);
advanced.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
adaptorManager.setAdvancedOptionsEnabled(advanced.isSelected());
}
});
logging.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doLogging(logging.isSelected());
}
});
advancedMenu.add(logging);
//settings.addSeparator();
JMenuItem reset = new JMenuItem("Reset");
reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doReset();
}
});
settings.add(reset);
JMenu help = new JMenu("Help");
JMenuItem docs = new JMenuItem(documentationAction);
help.add(docs);
JMenuBar all = new JMenuBar();
all.add(query);
all.add(settings);
all.add(help);
return all;
}
/**
* Sets the logging level for all handlers. true = Level.CONFIG, false = Level.WARNING.
* For more sophisticated control of the logging system start mart explorer with a logging
* config file parameter, e.g.
* -Djava.util.logging.config.file=some-config-file.properties
* @param log whether logging should be enabled for all handlers.
*/
public void doLogging(boolean log) {
LoggingUtil.setAllHandlerLevels(log ? Level.CONFIG : Level.WARNING);
}
public void doReset() {
if (logging.isSelected())
logging.doClick();
try {
prefs.clear();
} catch (BackingStoreException e1) {
feedback.warning(e1);
}
adaptorManager.clearCache();
tabs.removeAll();
adaptorManager.reset();
loadDefaultAdaptors();
advanced.setSelected(adaptorManager.isAdvancedOptionsEnabled());
if (getNumDatasetConfigsAvailable() > 0)
doNewQuery();
}
/**
*
*/
public void doLoadQueryFromMQL() {
QueryEditor qe = null;
try {
qe = new QueryEditor(this, adaptorManager);
addQueryEditor(qe);
qe.doLoadQuery();
} catch (IOException e) {
feedback.warning(e);
if (qe != null)
tabs.remove(qe);
}
}
/**
* Exits the programs.
*/
public void doExit() {
System.exit(0);
}
/**
*
*/
public void doNewQuery() {
try {
if (getNumDatasetConfigsAvailable() == 0) {
feedback.warning(
"No datasets available please check your db connection details");
} else {
try {
disableCursor();
final QueryEditor qe = new QueryEditor(this, adaptorManager);
qe.setName(nextQueryBuilderTabLabel());
addQueryEditor(qe);
tabs.setSelectedComponent(qe);
qe.openDatasetConfigMenu();
} catch (OutOfMemoryError ex) {
feedback.warning("Out of memory, can not create a new Query.");
} finally {
enableCursor();
}
}
} catch (IOException e) {
feedback.warning(e);
}
}
/**
* Disables the cursor whilst this runs.
* @return number of dataset configs currently available.
*/
public int getNumDatasetConfigsAvailable() {
int n = 0;
try {
disableCursor();
n = adaptorManager.getRootAdaptor().getNumDatasetConfigs(true);
} finally {
enableCursor();
}
return n;
}
private void enableCursor() {
setCursor(Cursor.getDefaultCursor());
getGlassPane().setVisible(false);
}
private void disableCursor() {
getGlassPane().setVisible(true);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
/**
* @see org.ensembl.mart.explorer.QueryEditorManager#remove(org.ensembl.mart.explorer.QueryEditor)
*/
public void remove(QueryEditor editor) {
tabs.remove(editor);
}
/* (non-Javadoc)
* @see org.ensembl.mart.explorer.QueryEditorManager#getQueryEditors()
*/
public QueryEditor[] getQueryEditors() {
// TODO Auto-generated method stub
return null;
}
private QueryEditor getSelectedQueryEditor() {
//return (QueryEditor) tabs.getSelectedComponent();
QueryEditor obj1 = (QueryEditor) tabs.getSelectedComponent();
return obj1;
}
private boolean isQueryEditorSelected() {
return getSelectedQueryEditor() != null;
}
/** Returns an ImageIcon, or null if the path was invalid. */
private ImageIcon createImageIcon(String filename) {
String path = IMAGE_DIR + "/" + filename;
URL imgURL = getClass().getClassLoader().getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
public void stateChanged(ChangeEvent e) {
executeAction.setEnabled(false);
countFocusAction.setEnabled(false);
//countRowsAction.setEnabled(false);
saveResultsAction.setEnabled(false);
saveResultsAsAction.setEnabled(false);
stopAction.setEnabled(false);
QueryEditor qe = getSelectedQueryEditor();
if (qe != null) {
if (qe.isRunning()) {
stopAction.setEnabled(true);
} else if (
(qe.getQuery().getAttributes().length > 0
|| qe.getQuery().getSequenceDescription() !=null)
&& qe.getQuery().getDataset() != null
&& qe.getQuery().getDataSource() != null) {
executeAction.setEnabled(true);
countFocusAction.setEnabled(true);
//countRowsAction.setEnabled(true);
saveResultsAction.setEnabled(true);
saveResultsAsAction.setEnabled(true);
}
}
}
}