Skip to content
Snippets Groups Projects
DatabaseJPanel.java 20.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* DatabaseJPanel.java
    
     *
     * created: June 2005
     *
     * This file is part of Artemis
     *
    
    tjc's avatar
    tjc committed
     * 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.
     *
    
    tjc's avatar
    tjc committed
     * $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.*;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.InputStreamProgressEvent;
    
    import uk.ac.sanger.artemis.util.InputStreamProgressListener;
    import uk.ac.sanger.artemis.util.OutOfRangeException;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.DatabaseDocument;
    
    import uk.ac.sanger.artemis.util.StringVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
    
    import uk.ac.sanger.artemis.io.Range;
    
    import javax.swing.Box;
    import javax.swing.JButton;
    
    tjc's avatar
    tjc committed
    import javax.swing.JComponent;
    
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    
    import javax.swing.JTree;
    import javax.swing.JScrollPane;
    
    tjc's avatar
    tjc committed
    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;
    
    tjc's avatar
    tjc committed
    import org.gmod.schema.sequence.Feature;
    
    import org.gmod.schema.sequence.FeatureLoc;
    
    tjc's avatar
    tjc committed
    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;
    
    tjc's avatar
    tjc committed
    import java.awt.FontMetrics;
    
    import java.io.*;
    
    tjc's avatar
    tjc committed
    import java.util.HashMap;
    
    import java.util.Iterator;
    
    import java.util.List;
    
    import java.util.Vector;
    
    tjc's avatar
    tjc committed
    public class DatabaseJPanel extends JPanel
    
    tjc's avatar
    tjc committed
      /** */
      private static final long serialVersionUID = 1L;
    
    tjc's avatar
    tjc committed
      private JLabel status_line = new JLabel("");
    
    tjc's avatar
    tjc committed
      private boolean splitGFFEntry = false;
    
    tjc's avatar
    tjc committed
      private JTree tree;
    
    tjc's avatar
    tjc committed
      private DatabaseDocument doc;
    
      private static Vector<String> opening = new Vector<String>();
    
    tjc's avatar
    tjc committed
      private static org.apache.log4j.Logger logger4j = 
        org.apache.log4j.Logger.getLogger(DatabaseJPanel.class);
    
    tjc's avatar
    tjc committed
      
      public DatabaseJPanel(final DatabaseEntrySource entry_source,
                            final Splash splash_main)
    
    tjc's avatar
    tjc committed
        setLayout(new BorderLayout());
    
        tree = getDatabaseTree(entry_source);
    
    tjc's avatar
    tjc committed
        // Listen for when the selection changes.
    
        MouseListener mouseListener = new MouseAdapter()
        {
          public void mouseClicked(MouseEvent e)
          {
    
            if(e.getClickCount() == 2 && !e.isPopupTrigger())
    
    tjc's avatar
    tjc committed
              showSelected(entry_source, splash_main);
    
          }
        };
        tree.addMouseListener(mouseListener);
    
        JScrollPane scroll = new JScrollPane(tree);
    
        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
    
    tjc's avatar
    tjc committed
        Dimension dim_frame = new Dimension(screen.width * 2 / 10,
                                            screen.height * 6 / 10);
    
        scroll.setPreferredSize(dim_frame);
    
    
    tjc's avatar
    tjc committed
        add(scroll, BorderLayout.CENTER);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final FontMetrics fm = this.getFontMetrics(status_line.getFont());
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final int font_height = fm.getHeight() + 10;
    
    tjc's avatar
    tjc committed
        status_line.setMinimumSize(new Dimension(100, font_height));
        status_line.setPreferredSize(new Dimension(100, font_height));
    
        Border loweredbevel = BorderFactory.createLoweredBevelBorder();
        Border raisedbevel = BorderFactory.createRaisedBevelBorder();
    
    tjc's avatar
    tjc committed
        Border compound = BorderFactory.createCompoundBorder(raisedbevel,
                                                             loweredbevel);
    
    tjc's avatar
    tjc committed
        status_line.setBorder(compound);
    
    tjc's avatar
    tjc committed
        add(status_line, BorderLayout.SOUTH);
        
    
        Box xBox = Box.createHorizontalBox();   
    
    tjc's avatar
    tjc committed
        JLabel title_line = new JLabel("DATABASE :: "+
            entry_source.getDatabaseDocument().getName());
    
    tjc's avatar
    tjc committed
        title_line.setMinimumSize(new Dimension(100, font_height));
    
        title_line.setPreferredSize(new Dimension(250, font_height));
    
    tjc's avatar
    tjc committed
        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);
    
    tjc's avatar
    tjc committed
       * Show the selected sequence in the tree
    
    tjc's avatar
    tjc committed
       * @param entry_source
       * @param tree
       * @param splash_main
    
    tjc's avatar
    tjc committed
       */
    
    tjc's avatar
    tjc committed
      public void showSelected(final DatabaseEntrySource entry_source,
                               final Splash splash_main)
    
          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();
    
            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, 
    
    tjc's avatar
    tjc committed
                status_line, stream_progress_listener, 
                splitGFFEntry, splash_main, 
    
                node_name, userName, readOnly);
    
    tjc's avatar
    tjc committed
        catch(NullPointerException npe)
        {
    
    tjc's avatar
    tjc committed
        }
    
    tjc's avatar
    tjc committed
      
      /**
       * 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
    
    tjc's avatar
    tjc committed
       * @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)
    
    tjc's avatar
    tjc committed
      {
        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(); }
            }
          }
        }
        
    
    tjc's avatar
    tjc committed
        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;
    
    tjc's avatar
    tjc committed
      }
    
    tjc's avatar
    tjc committed
       * Retrieve a database entry.
    
    tjc's avatar
    tjc committed
       * @param srcfeatureId
    
    tjc's avatar
    tjc committed
       * @param entry_source
    
    tjc's avatar
    tjc committed
       * @param srcComponent
       * @param status_line
       * @param stream_progress_listener
       * @param splitGFFEntry
    
    tjc's avatar
    tjc committed
       * @param splash_main
    
    tjc's avatar
    tjc committed
       * @param dbDocumentName
    
    tjc's avatar
    tjc committed
       * @param userName
    
    tjc's avatar
    tjc committed
       */
    
    tjc's avatar
    tjc committed
      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()
          {
    
    tjc's avatar
    tjc committed
            Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR);
            Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
    
            status_line.setText("Retrieving sequence....");
    
    tjc's avatar
    tjc committed
            srcComponent.setCursor(cbusy);
    
              while(DatabaseDocument.isCvThreadAlive())
                Thread.sleep(5);
    
    tjc's avatar
    tjc committed
              openEntry(srcfeatureId, entry_source, srcComponent, status_line, stream_progress_listener,
    
                  splitGFFEntry, splash_main, dbDocumentName, userName, null, readOnly);
    
    tjc's avatar
    tjc committed
            catch(RuntimeException re)
    
    tjc's avatar
    tjc committed
              logger4j.warn(re.getMessage());
    
    tjc's avatar
    tjc committed
    
              re.printStackTrace();
    
    tjc's avatar
    tjc committed
              final DatabaseEntrySource entry_source = new DatabaseEntrySource();
    
    tjc's avatar
    tjc committed
              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();
            }
    
    tjc's avatar
    tjc committed
            srcComponent.setCursor(cdone);
    
       * 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
       */
    
    tjc's avatar
    tjc committed
      private static EntryEdit openEntry(
    
    tjc's avatar
    tjc committed
          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) 
    
    tjc's avatar
    tjc committed
      {
        Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
        try
        {
    
          if(range != null)
            logger4j.info("LOAD FEATURES IN THE RANGE : "+range.toString());
    
    tjc's avatar
    tjc committed
          entry_source.setSplitGFF(splitGFFEntry);
    
          final Entry entry = entry_source.getEntry(srcfeatureId, userName,
    
              stream_progress_listener, range);
    
    tjc's avatar
    tjc committed
          DatabaseDocumentEntry db_entry = (DatabaseDocumentEntry) entry
              .getEMBLEntry();
    
          db_entry.setReadOnly(readOnly);
    
    tjc's avatar
    tjc committed
          DatabaseDocument doc = (DatabaseDocument) db_entry.getDocument();
          doc.setName(dbDocumentName);
    
          if(entry == null)
          {
            srcComponent.setCursor(cdone);
            status_line.setText("No entry.");
    
    tjc's avatar
    tjc committed
            return null;
    
    tjc's avatar
    tjc committed
          }
    
          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);
    
    tjc's avatar
    tjc committed
            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.");
    
    tjc's avatar
    tjc committed
          return new_entry_edit;
    
    tjc's avatar
    tjc committed
        }
        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);
        }
    
    tjc's avatar
    tjc committed
        return null;
    
    tjc's avatar
    tjc committed
      }
    
      /**
       * Create database organism JTree.
       */
      private JTree getDatabaseTree(final DatabaseEntrySource entry_source)
      {
    
    tjc's avatar
    tjc committed
        HashMap entries = null;
    
        
        while(entries == null)
        {
          try
          {
    
    tjc's avatar
    tjc committed
            doc = entry_source.getDatabaseDocument();
    
            final DatabaseTreeNode top = new DatabaseTreeNode("");
    
            File cacheFile = new File(Options.CACHE_PATH+
    
    tjc's avatar
    tjc committed
                ((String)doc.getLocation()).replaceAll("[/:=\\?]", "_"));
    
    tjc's avatar
    tjc committed
            
            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 = 
    
    tjc's avatar
    tjc committed
                  new DatabaseTreeNode(name, false, org, doc.getUserName(), doc);
    
              top.add(orgNode); 
            }
    
            return new DatabaseJTree(top);
          }
    
          catch(Exception e)
    
            if(!entry_source.setLocation(true))
              return null;
    
    tjc's avatar
    tjc committed
      
      /**
       * An InputStreamProgressListener used to update the error label with the
       * current number of chars read.
       */
    
    tjc's avatar
    tjc committed
      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);
        }
      };
    
    tjc's avatar
    tjc committed
    
      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());
      }