From b4cc53d4cba79e39ead07665ea2807f582f8d6f9 Mon Sep 17 00:00:00 2001
From: tjc <tjc@ee4ac58c-ac51-4696-9907-e4b3aa274f04>
Date: Wed, 22 Jul 2009 12:53:36 +0000
Subject: [PATCH] Add option to read data from a graph table in the database

git-svn-id: svn+ssh://svn.internal.sanger.ac.uk/repos/svn/pathsoft/artemis/trunk@11405 ee4ac58c-ac51-4696-9907-e4b3aa274f04
---
 artemis_sqlmap/Graph.xml                      |  50 ++++++++
 artemis_sqlmap/chado_iBatis_config.xml        |   1 +
 uk/ac/sanger/artemis/chado/GmodDAO.java       |   4 +
 uk/ac/sanger/artemis/chado/Graph.java         |  94 ++++++++++++++
 uk/ac/sanger/artemis/chado/IBatisDAO.java     |  30 ++++-
 uk/ac/sanger/artemis/chado/JdbcDAO.java       |  24 ++++
 .../sanger/artemis/components/GraphMenu.java  |  99 +++++++++++++-
 .../sanger/artemis/util/DatabaseDocument.java |  70 ++++++++++
 .../artemis/util/LargeObjectDocument.java     | 121 ++++++++++++++++++
 9 files changed, 487 insertions(+), 6 deletions(-)
 create mode 100644 artemis_sqlmap/Graph.xml
 create mode 100644 uk/ac/sanger/artemis/chado/Graph.java
 create mode 100644 uk/ac/sanger/artemis/util/LargeObjectDocument.java

diff --git a/artemis_sqlmap/Graph.xml b/artemis_sqlmap/Graph.xml
new file mode 100644
index 000000000..341b9c565
--- /dev/null
+++ b/artemis_sqlmap/Graph.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?> 
+ 
+<!DOCTYPE sqlMap 
+    PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" 
+    "http://ibatis.apache.org/dtd/sql-map-2.dtd"> 
+ 
+
+<sqlMap namespace="Graph"> 
+    
+  <typeAlias alias="Graph"
+        type="uk.ac.sanger.artemis.chado.Graph"/>
+
+  <resultMap id="select-graph" 
+               class="Graph">
+    <result property="graphId"        column="graph_id"/>  
+    <result property="featureId"   column="feature_id"/>           
+    <result property="name"        column="name"/>
+    <result property="description" column="description"/>
+  </resultMap>
+  
+  <resultMap id="select-graphdata" 
+               class="Graph">
+    <result property="graphId"        column="graph_id"/>  
+    <result property="featureId"   column="feature_id"/>           
+    <result property="name"        column="name"/>
+    <result property="description" column="description"/>
+    <result property="data" column="data"/>
+  </resultMap>
+  
+  <select id="getGraphs" resultMap="select-graph">
+    SELECT graph_id, feature_id, name, description FROM graph WHERE feature_id=$featureId$
+  </select>
+  
+  <select id="getGraph" resultMap="select-graphdata">
+    SELECT * FROM graph WHERE graph_id=$graphId$
+  </select>
+  
+  <select id="getTableColumns" parameterClass="java.lang.String"
+             resultClass="java.lang.String">
+    SELECT pg_attribute.attname
+    FROM pg_attribute, pg_class, pg_namespace
+    WHERE pg_namespace.oid=pg_class.relnamespace AND
+           attrelid=pg_class.oid AND
+           relname=#value# AND
+           attnum > 0
+           <!--AND nspname=#value#-->
+   </select>
+  
+  
+</sqlMap>
\ No newline at end of file
diff --git a/artemis_sqlmap/chado_iBatis_config.xml b/artemis_sqlmap/chado_iBatis_config.xml
index 9db915a0f..30a3d4127 100644
--- a/artemis_sqlmap/chado_iBatis_config.xml
+++ b/artemis_sqlmap/chado_iBatis_config.xml
@@ -98,6 +98,7 @@
   <sqlMap resource="artemis_sqlmap/Synonym.xml" />
   <sqlMap resource="artemis_sqlmap/AnalysisFeature.xml" />
   <sqlMap resource="artemis_sqlmap/Analysis.xml" />
+  <sqlMap resource="artemis_sqlmap/Graph.xml" />
   
 </sqlMapConfig> 
 
diff --git a/uk/ac/sanger/artemis/chado/GmodDAO.java b/uk/ac/sanger/artemis/chado/GmodDAO.java
index 0994d8504..d0c70a8ec 100644
--- a/uk/ac/sanger/artemis/chado/GmodDAO.java
+++ b/uk/ac/sanger/artemis/chado/GmodDAO.java
@@ -52,6 +52,10 @@ public abstract class GmodDAO
   private static org.apache.log4j.Logger logger4j = 
     org.apache.log4j.Logger.getLogger(GmodDAO.class);
 
+  public abstract Graph getGraph(final Integer graphId);
+  public abstract List getGraphs(final Integer featureId);
+  public abstract List getTableColumns(final String tableName);
+  
   public abstract List getOrganismsContainingSrcFeatures();
   public abstract List getSimilarityMatchesByFeatureIds(final List featureIds);
   public abstract List getSimilarityMatches(final Integer srcFeatureId);
diff --git a/uk/ac/sanger/artemis/chado/Graph.java b/uk/ac/sanger/artemis/chado/Graph.java
new file mode 100644
index 000000000..b59634af7
--- /dev/null
+++ b/uk/ac/sanger/artemis/chado/Graph.java
@@ -0,0 +1,94 @@
+/* Graph.java
+ *
+ * created: 2009
+ *
+ * This file is part of Artemis
+ *
+ * Copyright (C) 2009  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.
+ *
+ */
+package uk.ac.sanger.artemis.chado;
+
+import java.io.Serializable;
+
+/**
+ * Database graph.
+ */
+public class Graph implements Serializable 
+{
+  private static final long serialVersionUID = 1L;
+  // table columns
+  private int graphId;
+  private int featureId;
+  private String name;
+  private String description;
+  private byte[] data;
+
+  public Graph() 
+  {
+  }
+
+  public int getGraphId()
+  {
+    return graphId;
+  }
+
+  public void setGraphId(int graphId)
+  {
+    this.graphId = graphId;
+  }
+
+  public int getFeatureId()
+  {
+    return featureId;
+  }
+
+  public void setFeatureId(int featureId)
+  {
+    this.featureId = featureId;
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+  public void setName(String name)
+  {
+    this.name = name;
+  }
+
+  public String getDescription()
+  {
+    return description;
+  }
+
+  public void setDescription(String description)
+  {
+    this.description = description;
+  }
+
+  public byte[] getData()
+  {
+    return data;
+  }
+
+  public void setData(byte[] data)
+  {
+    this.data = data;
+  }
+}
\ No newline at end of file
diff --git a/uk/ac/sanger/artemis/chado/IBatisDAO.java b/uk/ac/sanger/artemis/chado/IBatisDAO.java
index c204c0efb..40810cdc3 100644
--- a/uk/ac/sanger/artemis/chado/IBatisDAO.java
+++ b/uk/ac/sanger/artemis/chado/IBatisDAO.java
@@ -52,6 +52,7 @@ import org.gmod.schema.analysis.AnalysisFeature;
 import org.gmod.schema.cv.Cv;
 import org.gmod.schema.cv.CvTerm;
 
+import javax.sql.DataSource;
 import javax.swing.JPasswordField;
 
 /**
@@ -89,6 +90,11 @@ public class IBatisDAO extends GmodDAO
   {
     sqlMap.close();
   }
+  
+  public DataSource getDataSource() throws SQLException
+  {
+    return sqlMap.getSqlMap().getDataSource();
+  }
 
   /**
    * Test to see if this is an old chado database version
@@ -713,6 +719,26 @@ public class IBatisDAO extends GmodDAO
     return sqlMap.queryForList("getPubDbXRef", null);
   }
   
+  
+  //
+  // Graph data
+  public Graph getGraph(final Integer graphId)
+  {
+    //return (Graph) sqlMap.queryForObject("getGraph", graphId);
+    return null;
+  }
+  
+  
+  public List getGraphs(final Integer featureId)
+  {
+    return sqlMap.queryForList("getGraphs", featureId);
+  }
+  
+  public List getTableColumns(String tableName)
+  {
+    return sqlMap.queryForList("getTableColumns", tableName);
+  }
+  
 //
 // WRITE BACK
 //
@@ -1252,8 +1278,7 @@ public class IBatisDAO extends GmodDAO
   {
     sqlMap.insert("insertPubDbXRef", pubDbXRef);
   }
-  
-  
+
   public void startTransaction() throws SQLException
   { 
     sqlMap.startTransaction();
@@ -1300,5 +1325,4 @@ public class IBatisDAO extends GmodDAO
     }
     return dbxrefHash;
   }
-
 }
diff --git a/uk/ac/sanger/artemis/chado/JdbcDAO.java b/uk/ac/sanger/artemis/chado/JdbcDAO.java
index e5f4a951b..de3b8086f 100644
--- a/uk/ac/sanger/artemis/chado/JdbcDAO.java
+++ b/uk/ac/sanger/artemis/chado/JdbcDAO.java
@@ -2295,4 +2295,28 @@ public class JdbcDAO extends GmodDAO
   {
     return null; 
   }
+
+  public Graph getGraph(Integer graphId)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  public List getGraphs(Integer featureId)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  public List getFeaturesByUniqueNames(List arg0)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  public List getTableColumns(String tableName)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
 }
diff --git a/uk/ac/sanger/artemis/components/GraphMenu.java b/uk/ac/sanger/artemis/components/GraphMenu.java
index 616e2cffc..8b75387f7 100644
--- a/uk/ac/sanger/artemis/components/GraphMenu.java
+++ b/uk/ac/sanger/artemis/components/GraphMenu.java
@@ -20,19 +20,24 @@
  * 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/GraphMenu.java,v 1.9 2009-06-02 15:32:00 tjc Exp $
+ * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/GraphMenu.java,v 1.10 2009-07-22 12:53:36 tjc Exp $
  */
 
 package uk.ac.sanger.artemis.components;
 
 import uk.ac.sanger.artemis.EntryGroup;
 import uk.ac.sanger.artemis.Options;
+import uk.ac.sanger.artemis.chado.Graph;
+import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
+import uk.ac.sanger.artemis.io.DocumentEntry;
 import uk.ac.sanger.artemis.plot.Algorithm;
 import uk.ac.sanger.artemis.plot.BaseAlgorithm;
 import uk.ac.sanger.artemis.plot.CodonUsageAlgorithm;
 import uk.ac.sanger.artemis.plot.CodonUsageWeight;
 import uk.ac.sanger.artemis.plot.UserDataAlgorithm;
 import uk.ac.sanger.artemis.sequence.Strand;
+import uk.ac.sanger.artemis.util.DatabaseDocument;
+import uk.ac.sanger.artemis.util.Document;
 
 import java.awt.Cursor;
 import java.awt.event.ActionEvent;
@@ -41,6 +46,7 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.io.IOException;
 import java.io.File;
+import java.util.List;
 import java.util.Vector;
 
 import javax.swing.*;
@@ -49,7 +55,7 @@ import javax.swing.*;
  *  This menu controls one particular BasePlotGroup.
  *
  *  @author Kim Rutherford <kmr@sanger.ac.uk>
- *  @version $Id: GraphMenu.java,v 1.9 2009-06-02 15:32:00 tjc Exp $
+ *  @version $Id: GraphMenu.java,v 1.10 2009-07-22 12:53:36 tjc Exp $
  **/
 
 public class GraphMenu extends JMenu 
@@ -178,7 +184,20 @@ public class GraphMenu extends JMenu
       });
 
       add (user_plot_item);
-
+      
+      if(getEntryGroup().getSequenceEntry().getEMBLEntry() instanceof 
+         DatabaseDocumentEntry)
+      {
+        final JMenu database_user_plot = new JMenu("Add Database Plot ...");
+        final DatabaseDocument dbDoc = 
+          (DatabaseDocument) ((DocumentEntry) entry_group
+            .getSequenceEntry().getEMBLEntry()).getDocument();
+
+        boolean hasGraphs = addDatabaseUserPlot(dbDoc, database_user_plot);
+        database_user_plot.setEnabled(hasGraphs);
+        add(database_user_plot);
+      }
+      
       addSeparator ();
     }
 
@@ -422,6 +441,80 @@ public class GraphMenu extends JMenu
     return new_plot;
   }
 
+  /**
+   * Add a UserDataAlgorithm from the database to the display. 
+   * This returns true only if graph data is found.
+   * @param dbDoc
+   * @param database_user_plot
+   * @return
+   */
+  private boolean addDatabaseUserPlot(final DatabaseDocument dbDoc,
+                                      final JMenu database_user_plot)
+  {
+    List graphs = dbDoc.getGraphs();
+    if(graphs == null || graphs.size() == 0)
+      return false;
+    
+    for (int i = 0; i < graphs.size(); i++)
+    {
+      final Graph graph = (Graph) graphs.get(i);
+      JMenuItem menuGraph = new JMenuItem(graph.getName());
+      database_user_plot.add(menuGraph);
+      
+      menuGraph.addActionListener(new ActionListener()
+      {
+        public void actionPerformed(ActionEvent event)
+        {
+          try
+          {
+            Document doc = dbDoc.getGraphData(graph.getGraphId(), graph.getName());
+            final JCheckBox logTransform = new JCheckBox("Use log(data+1)", false);
+            JOptionPane.showMessageDialog(frame, logTransform, 
+                "Transform", JOptionPane.QUESTION_MESSAGE);
+            frame.setCursor(new Cursor(Cursor.WAIT_CURSOR));
+            try
+            {
+              UserDataAlgorithm new_algorithm = new UserDataAlgorithm(
+                  getEntryGroup().getBases().getForwardStrand(), doc, 
+                  logTransform.isSelected());
+              final BasePlot new_base_plot = base_plot_group.addAlgorithm(new_algorithm);
+
+              base_plot_group.setVisibleByAlgorithm(new_algorithm, true);
+              addAlgorithm(new_algorithm, true, false);
+
+              // XXX hack to force the BasePlot to initialise
+              final DisplayAdjustmentEvent e = new DisplayAdjustmentEvent(
+                  this, feature_display.getFirstVisibleForwardBase(),
+                  feature_display.getLastVisibleForwardBase(),
+                  feature_display.getMaxVisibleBases(), 
+                  feature_display.getScaleValue(),
+                  feature_display.getScaleFactor(),
+                  feature_display.isRevCompDisplay(),
+                  DisplayAdjustmentEvent.ALL_CHANGE_ADJUST_EVENT);
+
+              base_plot_group.displayAdjustmentValueChanged(e);
+            }
+            catch (IOException e)
+            {
+              e.printStackTrace();
+            }
+            frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+            adjustSplitPane(true);
+          }
+          catch (java.lang.OutOfMemoryError emem)
+          {
+            JOptionPane.showMessageDialog(frame,
+                "Out of memory. Increase the maximum memory"
+                    + "\navailable for this application and try again.",
+                "Out Of Memory", JOptionPane.WARNING_MESSAGE);
+            frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+          }
+        }
+      });
+    }
+    return true;
+  }
+  
   /**
    *  Add a UserDataAlgorithm to the display.
    **/
diff --git a/uk/ac/sanger/artemis/util/DatabaseDocument.java b/uk/ac/sanger/artemis/util/DatabaseDocument.java
index 1529af7e0..7c73946b0 100644
--- a/uk/ac/sanger/artemis/util/DatabaseDocument.java
+++ b/uk/ac/sanger/artemis/util/DatabaseDocument.java
@@ -60,6 +60,9 @@ import org.gmod.schema.general.DbXRef;
 import org.gmod.schema.organism.Organism;
 import org.gmod.schema.pub.PubDbXRef;
 import org.gmod.schema.pub.Pub;
+import org.postgresql.largeobject.LargeObjectManager;
+
+import com.ibatis.common.jdbc.SimpleDataSource;
 
 import java.sql.*;
 import java.text.SimpleDateFormat;
@@ -2453,6 +2456,73 @@ public class DatabaseDocument extends Document
     }
   }
   
+  /**
+   * Get a list of the available graphs
+   * @return
+   */
+  public List getGraphs()
+  {
+    GmodDAO dao = getDAOOnly();
+    
+    List list = dao.getTableColumns("graph");
+    if(list == null || list.size() == 0)
+      return null;
+    return dao.getGraphs(Integer.parseInt(srcFeatureId));
+  }
+  
+  /**
+   * Get the graph data from the database. 
+   * This uses JDBC and the org.postgresql.largeobject API.
+   * @param graphId
+   * @param name
+   * @return
+   */
+  public LargeObjectDocument getGraphData(int graphId, String name)
+  {
+    LargeObjectDocument doc = null;
+    try
+    {
+      // this causes class cast problems probably because it gets
+      // a pool connection rather than the underlying real connection
+      // Connection conn = ((SimpleDataSource)connIB.getDataSource()).getConnection();
+      SimpleDataSource ds = (SimpleDataSource)connIB.getDataSource();
+      Connection conn = DriverManager.getConnection(
+          ds.getJdbcUrl(), ds.getJdbcUsername(), ds.getJdbcPassword());
+
+
+      // All LargeObject API calls must be within a transaction
+      conn.setAutoCommit(false);
+
+      // Get the Large Object Manager to perform operations with
+      LargeObjectManager lobj =
+         ((org.postgresql.PGConnection)conn).getLargeObjectAPI();
+      
+      PreparedStatement pstmt =
+          conn.prepareStatement("SELECT * FROM graph WHERE graph_id=?");
+      pstmt.setInt(1, graphId);
+      ResultSet rs = pstmt.executeQuery();
+      
+      if (rs != null) 
+      {
+        while(rs.next()) 
+        {
+          // open the large object for reading
+          int oid = rs.getInt(5);
+          doc = new LargeObjectDocument(ds.getJdbcUrl(), name, 
+             lobj.open(oid, LargeObjectManager.READ));
+        }
+        rs.close();
+      }
+      pstmt.close();
+    }
+    catch (SQLException e)
+    {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+    return doc;
+  }
+  
   /**
    * Get the data access object (DAO).
    * @return data access object
diff --git a/uk/ac/sanger/artemis/util/LargeObjectDocument.java b/uk/ac/sanger/artemis/util/LargeObjectDocument.java
new file mode 100644
index 000000000..715c248d7
--- /dev/null
+++ b/uk/ac/sanger/artemis/util/LargeObjectDocument.java
@@ -0,0 +1,121 @@
+/* LargeObjectDocument.java
+ *
+ * created: 2009
+ *
+ * This file is part of Artemis
+ * 
+ * Copyright (C) 2009  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.
+ */
+
+package uk.ac.sanger.artemis.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+
+import org.postgresql.largeobject.LargeObject;
+
+/**
+ *  Objects of this class are Documents created from a database
+ *  and for reading blobs as a LargeObject.
+ **/
+public class LargeObjectDocument extends Document 
+{
+  private String name;
+  private LargeObject obj;
+  
+  /**
+   *  Create a new FileDocument from a File.
+   *  @param location This should be a file or directory name.
+   **/
+  public LargeObjectDocument (String location, String name, LargeObject obj)
+  {
+    super (location);
+    this.obj = obj;
+    this.name = name;
+  }
+
+  /**
+   *  Return the name of this Document (the last element of the Document
+   *  location).
+   **/
+  public String getName ()
+  {
+    return name;
+  }
+
+  /**
+   *  Create a new InputStream object from this Document.  The contents of the
+   *  Document can be read from the InputStream.
+   *  @exception IOException Thrown if the Document can't be read from
+   *    (for example if it doesn't exist).
+   **/
+  public InputStream getInputStream ()
+      throws IOException 
+  {
+    try
+    {
+      try
+      {
+        // assume zipped 
+        return new WorkingGZIPInputStream (obj.getInputStream());
+      }
+      catch(IOException e)
+      {
+        obj.seek(0);
+        return obj.getInputStream();
+      }
+    }
+    catch (SQLException e)
+    {
+      e.printStackTrace();
+      throw new IOException( e );
+    }
+  }
+
+  /**
+   *  Create a new OutputStream object from this Document.  The Document can
+   *  then be written to using the new object.  The old centents of the
+   *  Document will be lost.
+   *  @exception ReadOnlyException is thrown if the Document is read only.
+   **/
+  public OutputStream getOutputStream () throws IOException 
+  {
+    return null;
+  }
+
+  public Document append(String name) throws IOException
+  {
+    return null;
+  }
+
+  public Document getParent()
+  {
+    return null;
+  }
+
+  public boolean readable()
+  {
+    return false;
+  }
+
+  public boolean writable()
+  {
+    return false;
+  }
+}
-- 
GitLab