DatabaseJPanel.java 20.27 KiB
/* DatabaseJPanel.java
*
* created: June 2005
*
* This file is part of Artemis
*
* Copyright(C) 2005,2006 Genome Research Limited
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/database/DatabaseJPanel.java,v 1.25 2009-06-03 10:24:17 tjc Exp $
*/
package uk.ac.sanger.artemis.components.database;
import uk.ac.sanger.artemis.components.*;
import uk.ac.sanger.artemis.Entry;
import uk.ac.sanger.artemis.FeatureVector;
import uk.ac.sanger.artemis.Options;
import uk.ac.sanger.artemis.Selection;
import uk.ac.sanger.artemis.sequence.*;
import uk.ac.sanger.artemis.util.InputStreamProgressEvent;
import uk.ac.sanger.artemis.util.InputStreamProgressListener;
import uk.ac.sanger.artemis.util.OutOfRangeException;
import uk.ac.sanger.artemis.util.DatabaseDocument;
import uk.ac.sanger.artemis.util.StringVector;
import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
import uk.ac.sanger.artemis.io.Range;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.JScrollPane;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.BorderFactory;
import javax.swing.border.Border;
import javax.swing.tree.TreePath;
import org.gmod.schema.organism.Organism;
import org.gmod.schema.sequence.Feature;
import org.gmod.schema.sequence.FeatureLoc;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Cursor;
import java.awt.FontMetrics;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
public class DatabaseJPanel extends JPanel
{
/** */
private static final long serialVersionUID = 1L;
private JLabel status_line = new JLabel("");
private boolean splitGFFEntry = false;
private JTree tree;
private DatabaseDocument doc;
private static Vector<String> opening = new Vector<String>();
private static org.apache.log4j.Logger logger4j =
org.apache.log4j.Logger.getLogger(DatabaseJPanel.class);
public DatabaseJPanel(final DatabaseEntrySource entry_source,
final Splash splash_main)
{
setLayout(new BorderLayout());
tree = getDatabaseTree(entry_source);
// Listen for when the selection changes.
MouseListener mouseListener = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
if(e.getClickCount() == 2 && !e.isPopupTrigger())
showSelected(entry_source, splash_main);
}
};
tree.addMouseListener(mouseListener);
JScrollPane scroll = new JScrollPane(tree);
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Dimension dim_frame = new Dimension(screen.width * 2 / 10,
screen.height * 6 / 10);
scroll.setPreferredSize(dim_frame);
add(scroll, BorderLayout.CENTER);
final FontMetrics fm = this.getFontMetrics(status_line.getFont());
final int font_height = fm.getHeight() + 10;
status_line.setMinimumSize(new Dimension(100, font_height));
status_line.setPreferredSize(new Dimension(100, font_height));
Border loweredbevel = BorderFactory.createLoweredBevelBorder();
Border raisedbevel = BorderFactory.createRaisedBevelBorder();
Border compound = BorderFactory.createCompoundBorder(raisedbevel,
loweredbevel);
status_line.setBorder(compound);
add(status_line, BorderLayout.SOUTH);
Box xBox = Box.createHorizontalBox();
JLabel title_line = new JLabel("DATABASE :: "+
entry_source.getDatabaseDocument().getName());
title_line.setMinimumSize(new Dimension(100, font_height));
title_line.setPreferredSize(new Dimension(250, font_height));
title_line.setBorder(compound);
xBox.add(title_line);
final JTextField openGeneText = new JTextField(6);
openGeneText.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
getEntryEditFromDatabase(entry_source, splash_main,
openGeneText.getText().trim());
}
});
JButton openBtn = new JButton("Open:");
openBtn.setToolTipText("Open Artemis for a chromosome or at a given gene");
openBtn.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
getEntryEditFromDatabase(entry_source, splash_main,
openGeneText.getText().trim());
}
});
xBox.add(Box.createHorizontalGlue());
xBox.add(openBtn);
xBox.add(openGeneText);
add(xBox, BorderLayout.NORTH);
}
/**
* Show the selected sequence in the tree
* @param entry_source
* @param tree
* @param splash_main
*/
public void showSelected(final DatabaseEntrySource entry_source,
final Splash splash_main)
{
try
{
TreePath path = tree.getLeadSelectionPath();
if(path == null)
return;
DatabaseTreeNode seq_node =
(DatabaseTreeNode)path.getLastPathComponent();
String node_name = (String)seq_node.getUserObject();
String userName = doc.getUserName();
final String id = seq_node.getFeatureId();
if(id != null)
{
boolean isMitochondrial = false;
if(seq_node.getFeatureType() != null &&
seq_node.getFeatureType().startsWith("mitochondrial_"))
isMitochondrial = true;
boolean readOnly = DatabaseTreeNode.setOrganismProps(
seq_node.getOrganism().getOrganismProps(), isMitochondrial);
getEntryEditFromDatabase(id, entry_source, tree,
status_line, stream_progress_listener,
splitGFFEntry, splash_main,
node_name, userName, readOnly);
}
}
catch(NullPointerException npe)
{
npe.printStackTrace();
}
}
/**
* Open an Artemis EntryEdit window
* @param entry_source
* @param splash_main
* @param stream_progress_listener
* @param entryName e.g. Pfalciparum:Pf3D7_09 or
* Pfalciparum:Pf3D7_09:20050..40000
* @return
*/
public static EntryEdit show(final DatabaseEntrySource entry_source,
final Splash splash_main,
final InputStreamProgressListener stream_progress_listener,
final String entryName)
{
return show(entry_source,
(splash_main == null ? null : splash_main.getCanvas()),
(splash_main == null ? null : splash_main.getStatusLabel()),
splash_main, stream_progress_listener,
entryName, false, false);
}
/**
* Open an Artemis EntryEdit window
* @param entry_source
* @param srcComponent
* @param status_line
* @param splash_main
* @param stream_progress_listener
* @param entryName e.g. Pfalciparum:Pf3D7_09 or
* Pfalciparum:Pf3D7_09:20050..40000
* @param isNotSrcFeature true is the entry name may not be a source feature
* @return
*/
private static EntryEdit show(final DatabaseEntrySource entry_source,
final JComponent srcComponent,
final JLabel status_line,
final Splash splash_main,
final InputStreamProgressListener stream_progress_listener,
final String entryName,
final boolean isNotSrcFeature,
final boolean splitGFFEntry)
{
final String entry[] = entryName.split(":");
String url = (String)entry_source.getLocation();
int index = url.indexOf("?");
String userName = url.substring(index+1).trim();
if(userName.startsWith("user="))
userName = userName.substring(5);
DatabaseDocument doc = entry_source.getDatabaseDocument();
Range range = null;
if(entry.length>2)
{
if(entry[2].indexOf("..") > -1)
{
String ranges[] = entry[2].split("\\.\\.");
if(ranges.length == 2)
{
try
{
range = new Range(Integer.parseInt(ranges[0]),
Integer.parseInt(ranges[1]));
}
catch(Exception e){ e.printStackTrace(); }
}
}
}
Feature f = doc.getFeatureByUniquename(entry[1]);
if(isNotSrcFeature && f.getFeatureLocsForFeatureId() != null
&& f.getFeatureLocsForFeatureId().size() > 0)
{
Iterator<FeatureLoc> it = f.getFeatureLocsForFeatureId().iterator();
f = it.next().getFeatureBySrcFeatureId();
}
boolean isMitochondrial = false;
if(f.getCvTerm().getName().startsWith("mitochondrial_"))
isMitochondrial = true;
boolean readOnly = DatabaseTreeNode.setOrganismProps(
f.getOrganism().getOrganismProps(), isMitochondrial);
// warn when opening duplicate entries at the same time
if(opening.contains(f.getUniqueName()))
{
int status = JOptionPane.showOptionDialog(null,
f.getUniqueName()+" already opening. Continue?",
"Open", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null,
new String[] {"Yes", "No"}, "No");
if(status != JOptionPane.YES_OPTION)
return null;
}
opening.add(f.getUniqueName());
EntryEdit ee = openEntry(Integer.toString(f.getFeatureId()), entry_source,
srcComponent, status_line,
stream_progress_listener,
splitGFFEntry, splash_main, f.getUniqueName(), userName, range, readOnly);
opening.remove(f.getUniqueName());
return ee;
}
/**
* Retrieve a database entry.
* @param srcfeatureId
* @param entry_source
* @param srcComponent
* @param status_line
* @param stream_progress_listener
* @param splitGFFEntry
* @param splash_main
* @param dbDocumentName
* @param userName
*/
private static void getEntryEditFromDatabase(
final String srcfeatureId,
final DatabaseEntrySource entry_source,
final JComponent srcComponent,
final JLabel status_line,
final InputStreamProgressListener stream_progress_listener,
final boolean splitGFFEntry,
final Splash splash_main,
final String dbDocumentName,
final String userName,
final boolean readOnly)
{
SwingWorker entryWorker = new SwingWorker()
{
public Object construct()
{
Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR);
Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
status_line.setText("Retrieving sequence....");
srcComponent.setCursor(cbusy);
try
{
while(DatabaseDocument.isCvThreadAlive())
Thread.sleep(5);
openEntry(srcfeatureId, entry_source, srcComponent, status_line, stream_progress_listener,
splitGFFEntry, splash_main, dbDocumentName, userName, null, readOnly);
}
catch(RuntimeException re)
{
logger4j.warn(re.getMessage());
re.printStackTrace();
final DatabaseEntrySource entry_source = new DatabaseEntrySource();
entry_source.setLocation(true);
String url = entry_source.getLocation();
int index = url.indexOf("?");
String userName = url.substring(index+1).trim();
if(userName.startsWith("user="))
userName = userName.substring(5);
openEntry(srcfeatureId, entry_source, srcComponent, status_line, stream_progress_listener,
splitGFFEntry, splash_main, dbDocumentName, userName, null, readOnly);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
srcComponent.setCursor(cdone);
return null;
}
};
entryWorker.start();
}
/**
* Open an Artemis entry given a feature name. If the name is
* a feature on the sequence (i.e. not the source feature) the
* display will open at that region.
* @param entry_source
* @param splash_main
* @param featureName
*/
private void getEntryEditFromDatabase(final DatabaseEntrySource entry_source,
final Splash splash_main,
final String featureName)
{
SwingWorker entryWorker = new SwingWorker()
{
public Object construct()
{
Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR);
Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
try
{
DatabaseJPanel.this.setCursor(cbusy);
EntryEdit ee = show(entry_source,
DatabaseJPanel.this, status_line, splash_main,
stream_progress_listener, ":" + featureName,
true, splitGFFEntry);
goTo(ee, featureName);
}
catch (NullPointerException npe)
{
//npe.printStackTrace();
JOptionPane.showMessageDialog(DatabaseJPanel.this,
featureName + " not opened/found!",
"Failed to Open", JOptionPane.WARNING_MESSAGE);
logger4j.debug(featureName + " not found!");
}
finally
{
DatabaseJPanel.this.setCursor(cdone);
}
return null;
}
};
entryWorker.start();
}
/**
* Open an Artemis entry
* @param srcfeatureId
* @param entry_source
* @param srcComponent
* @param status_line
* @param stream_progress_listener
* @param splitGFFEntry
* @param splash_main
* @param dbDocumentName
* @param userName
* @param range range for to retrieve features in
* @return
*/
private static EntryEdit openEntry(
final String srcfeatureId,
final DatabaseEntrySource entry_source,
final JComponent srcComponent,
final JLabel status_line,
final InputStreamProgressListener stream_progress_listener,
final boolean splitGFFEntry,
final Splash splash_main,
final String dbDocumentName,
final String userName,
final Range range,
final boolean readOnly)
{
Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
try
{
if(range != null)
logger4j.info("LOAD FEATURES IN THE RANGE : "+range.toString());
entry_source.setSplitGFF(splitGFFEntry);
final Entry entry = entry_source.getEntry(srcfeatureId, userName,
stream_progress_listener, range);
DatabaseDocumentEntry db_entry = (DatabaseDocumentEntry) entry
.getEMBLEntry();
db_entry.setReadOnly(readOnly);
DatabaseDocument doc = (DatabaseDocument) db_entry.getDocument();
doc.setName(dbDocumentName);
if(entry == null)
{
srcComponent.setCursor(cdone);
status_line.setText("No entry.");
return null;
}
final EntryEdit new_entry_edit = ArtemisMain.makeEntryEdit(entry);
// add gff entries
if(splitGFFEntry)
{
final DatabaseDocumentEntry[] entries = entry_source.makeFromGff(
(DatabaseDocument) db_entry.getDocument(), srcfeatureId, userName);
for(int i = 0; i < entries.length; i++)
{
if(entries[i] == null)
continue;
final Entry new_entry = new Entry(new_entry_edit.getEntryGroup()
.getBases(), entries[i]);
new_entry_edit.getEntryGroup().add(new_entry);
}
}
new_entry_edit.setVisible(true);
status_line.setText("Sequence loaded.");
return new_entry_edit;
}
catch(OutOfRangeException e)
{
new MessageDialog(splash_main, "read failed: one of the features in "
+ " the entry has an out of range " + "location: "
+ e.getMessage());
}
catch(NoSequenceException e)
{
new MessageDialog(splash_main, "read failed: entry contains no sequence");
}
catch(IOException e)
{
new MessageDialog(splash_main, "read failed due to IO error: " + e);
}
return null;
}
/**
* Create database organism JTree.
*/
private JTree getDatabaseTree(final DatabaseEntrySource entry_source)
{
HashMap entries = null;
while(entries == null)
{
try
{
doc = entry_source.getDatabaseDocument();
final DatabaseTreeNode top = new DatabaseTreeNode("");
File cacheFile = new File(Options.CACHE_PATH+
((String)doc.getLocation()).replaceAll("[/:=\\?]", "_"));
if(System.getProperty("database_manager_cache_off") == null &&
cacheFile.exists())
{
doc.ping();
doc.loadCvTerms();
DatabaseTreeNode node = null;
try
{
FileInputStream fis = new FileInputStream(cacheFile);
logger4j.debug("USING CACHE :: "+cacheFile.getAbsolutePath());
ObjectInputStream in = new ObjectInputStream(fis);
node = (DatabaseTreeNode) in.readObject();
node.setDbDoc(doc);
in.close();
return new DatabaseJTree((DatabaseTreeNode) node.getParent());
}
catch (IOException ex)
{
ex.printStackTrace();
}
catch (ClassNotFoundException ex)
{
ex.printStackTrace();
}
}
else if(System.getProperty("database_manager_cache_off") != null)
logger4j.debug("Database manager cache off");
final List<Organism> organisms = doc.getOrganismsContainingSrcFeatures();
for(int i=0; i<organisms.size(); i++)
{
Organism org = organisms.get(i);
String name = org.getCommonName();
if(name == null || name.equals(""))
name = org.getGenus() + "." + org.getSpecies();
DatabaseTreeNode orgNode =
new DatabaseTreeNode(name, false, org, doc.getUserName(), doc);
top.add(orgNode);
}
return new DatabaseJTree(top);
}
catch(Exception e)
{
doc.reset();
if(!entry_source.setLocation(true))
return null;
}
}
return null;
}
/**
* An InputStreamProgressListener used to update the error label with the
* current number of chars read.
*/
private final InputStreamProgressListener stream_progress_listener =
new InputStreamProgressListener()
{
public void progressMade(final InputStreamProgressEvent event)
{
final int char_count = event.getCharCount();
if(char_count == -1)
status_line.setText("");
else
status_line.setText("chars read so far: " + char_count);
}
public void progressMade(String progress)
{
status_line.setText(progress);
}
};
public void setSplitGFFEntry(boolean splitGFFEntry)
{
this.splitGFFEntry = splitGFFEntry;
}
/**
* Go to a named feature
* @param ee
* @param geneName
*/
private static void goTo(final EntryEdit ee, final String geneName)
{
Selection selection = ee.getSelection();
selection.clear();
final StringVector qualifiers_to_search = new StringVector();
qualifiers_to_search.add(Options.getOptions().getAllGeneNames());
FeatureVector features = ee.getEntryGroup().getAllFeatures();
for (int i = 0; i < features.size(); i++)
{
uk.ac.sanger.artemis.Feature thisFeature = features.elementAt(i);
if (thisFeature.containsText(geneName, true, false, qualifiers_to_search))
{
selection.set(thisFeature);
break;
}
}
ee.getGotoEventSource().gotoBase(selection.getLowestBaseOfSelection());
}
}