diff --git a/uk/ac/sanger/artemis/components/genebuilder/GeneBuilderFrame.java b/uk/ac/sanger/artemis/components/genebuilder/GeneBuilderFrame.java index 585621dd5fd6b361111070aef3cfa135defec7de..b100cb2178d8d9ffcd16e382d79feea7f10dc017 100644 --- a/uk/ac/sanger/artemis/components/genebuilder/GeneBuilderFrame.java +++ b/uk/ac/sanger/artemis/components/genebuilder/GeneBuilderFrame.java @@ -20,89 +20,171 @@ * 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/genebuilder/GeneBuilderFrame.java,v 1.1 2006-05-31 09:49:07 tjc Exp $ + * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/genebuilder/GeneBuilderFrame.java,v 1.2 2006-07-04 15:57:57 tjc Exp $ */ package uk.ac.sanger.artemis.components.genebuilder; import javax.swing.*; + import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import uk.ac.sanger.artemis.Entry; +import uk.ac.sanger.artemis.EntryChangeEvent; +import uk.ac.sanger.artemis.EntryChangeListener; +import uk.ac.sanger.artemis.EntryGroup; import uk.ac.sanger.artemis.Feature; -import uk.ac.sanger.artemis.components.QualifierTextArea; -import uk.ac.sanger.artemis.io.ChadoCanonicalGene; -import uk.ac.sanger.artemis.io.EntryInformation; +import uk.ac.sanger.artemis.FeatureChangeEvent; +import uk.ac.sanger.artemis.FeatureChangeListener; +import uk.ac.sanger.artemis.GotoEventSource; +import uk.ac.sanger.artemis.Selection; +import uk.ac.sanger.artemis.components.FeatureEdit; import uk.ac.sanger.artemis.io.GFFStreamFeature; -import uk.ac.sanger.artemis.io.Qualifier; -import uk.ac.sanger.artemis.io.QualifierInfo; -import uk.ac.sanger.artemis.io.QualifierVector; -import uk.ac.sanger.artemis.io.StreamQualifier; -import uk.ac.sanger.artemis.util.StringVector; + public class GeneBuilderFrame extends JFrame + implements EntryChangeListener, FeatureChangeListener { - public GeneBuilderFrame(final Feature gene_feature) + private Feature active_feature; + private FeatureEdit feature_editor; + + public GeneBuilderFrame(final Feature feature, + final EntryGroup entry_group, + final Selection selection, + final GotoEventSource goto_event_source) { - super("Artemis Gene Builder: " + gene_feature.getIDString() + - (gene_feature.isReadOnly() ? + super("Artemis Gene Builder: " + feature.getIDString() + + (feature.isReadOnly() ? " - (read only)" : "")); - QualifierTextArea qualifier_text_area = new QualifierTextArea(); - GFFStreamFeature gff_feature = (GFFStreamFeature)gene_feature.getEmblFeature(); + this.active_feature = feature; + + GFFStreamFeature gff_feature = (GFFStreamFeature)feature.getEmblFeature(); GeneComponentTree tree = new GeneComponentTree(gff_feature.getChadoGene(), - qualifier_text_area); - getContentPane().add(new JScrollPane(tree), BorderLayout.WEST); + this); + + JScrollPane jsp_tree = new JScrollPane(tree); + jsp_tree.setPreferredSize( new Dimension(150, jsp_tree.getPreferredSize().height) ); + getContentPane().add(jsp_tree, BorderLayout.WEST); GeneViewerPanel viewer = new GeneViewerPanel(gff_feature.getChadoGene()); getContentPane().add(viewer, BorderLayout.CENTER); - qualifier_text_area.setWrapStyleWord(true); - qualifier_text_area.setText(getQualifierString(gene_feature)); + if(entry_group != null) + { + JTabbedPane tabpane = new JTabbedPane(); - getContentPane().add(new JScrollPane(qualifier_text_area), BorderLayout.SOUTH); + feature_editor = new FeatureEdit(feature, entry_group, + selection, goto_event_source, this); + tabpane.addTab("Annotation", feature_editor); + + getContentPane().add(tabpane, BorderLayout.SOUTH); + } + + addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent event) + { + stopListening(); + dispose(); + } + }); pack(); setVisible(true); } + + protected void setActiveFeature(final Feature active_feature) + { + if(this.active_feature != null) + stopListening(); + + this.active_feature = active_feature; + feature_editor.setActiveFeature(active_feature); + } /** - * Return a string containing one qualifier per line. These are the - * original qualifiers, not the qualifiers from the qualifier_text_area. + * Remove this object as a feature and entry change listener. **/ - protected static String getQualifierString(final Feature feature) + private void stopListening() { - final StringBuffer buffer = new StringBuffer(); - final QualifierVector qualifiers = feature.getQualifiers(); + getEntry().removeEntryChangeListener(this); + active_feature.removeFeatureChangeListener(this); + } - for(int qualifier_index = 0; qualifier_index < qualifiers.size(); - ++qualifier_index) + /** + * Implementation of the EntryChangeListener interface. We listen to + * EntryChange events so we can notify the user if of this component if the + * feature gets deleted. + **/ + public void entryChanged(EntryChangeEvent event) + { + switch(event.getType()) { - final Qualifier this_qualifier = (Qualifier)qualifiers.elementAt(qualifier_index); - - final QualifierInfo qualifier_info = - getEntryInformation(feature).getQualifierInfo(this_qualifier.getName()); - - final StringVector qualifier_strings = - StreamQualifier.toStringVector(qualifier_info, this_qualifier); - - for(int value_index = 0; value_index < qualifier_strings.size(); - ++value_index) - { - final String qualifier_string = (String)qualifier_strings.elementAt(value_index); - buffer.append(qualifier_string + "\n"); - } + case EntryChangeEvent.FEATURE_DELETED: + if(event.getFeature() == active_feature) + { + stopListening(); + dispose(); + } + break; + default: + // do nothing; + break; } - - return buffer.toString(); } /** - * Return the EntryInformation object of the entry containing the feature. + * Implementation of the FeatureChangeListener interface. We need to + * listen to feature change events from the Features in this object so that + * we can update the display. + * @param event The change event. **/ - protected static EntryInformation getEntryInformation(final Feature feature) + public void featureChanged(FeatureChangeEvent event) { - return feature.getEntry().getEntryInformation(); - } + active_feature.resetColour(); + /* + // re-read the information from the feature + switch(event.getType()) + { + case FeatureChangeEvent.LOCATION_CHANGED: + updateLocation(); + break; + case FeatureChangeEvent.KEY_CHANGED: + updateKey(); + break; + case FeatureChangeEvent.QUALIFIER_CHANGED: + if(qualifier_text_area.getText().equals(orig_qualifier_text)) + updateFromFeature(); + else + { + final String message = + "warning: the qualifiers have changed outside the editor - " + + "view now?"; + final YesNoDialog yes_no_dialog = + new YesNoDialog(FeatureEdit.this, message); + + if(yes_no_dialog.getResult()) + new FeatureViewer(gene_feature); + } + break; + default: + updateFromFeature(); + break; + } + */ + } + + + private Entry getEntry() + { + return active_feature.getEntry(); + } + + } \ No newline at end of file diff --git a/uk/ac/sanger/artemis/components/genebuilder/GeneComponentTree.java b/uk/ac/sanger/artemis/components/genebuilder/GeneComponentTree.java index 0395a0edeaf1f8acb155ce2e616a49e0d2f07f40..70ff8b0b72e691bd8feb67390d0bdf606b12f2cc 100644 --- a/uk/ac/sanger/artemis/components/genebuilder/GeneComponentTree.java +++ b/uk/ac/sanger/artemis/components/genebuilder/GeneComponentTree.java @@ -20,21 +20,21 @@ * 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/genebuilder/GeneComponentTree.java,v 1.2 2006-05-31 15:40:53 tjc Exp $ + * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/genebuilder/GeneComponentTree.java,v 1.3 2006-07-04 15:57:57 tjc Exp $ */ package uk.ac.sanger.artemis.components.genebuilder; -import uk.ac.sanger.artemis.components.QualifierTextArea; import uk.ac.sanger.artemis.io.ChadoCanonicalGene; import uk.ac.sanger.artemis.io.Feature; import uk.ac.sanger.artemis.io.InvalidRelationException; +import uk.ac.sanger.artemis.chado.ChadoFeature; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; -import java.util.Vector; +import java.util.List; /** * Tree to display a gene hierarchy. @@ -43,9 +43,9 @@ public class GeneComponentTree extends JTree { public GeneComponentTree(final ChadoCanonicalGene chado_gene, - final QualifierTextArea qualifier_text_area) + final GeneBuilderFrame gene_builder) { - final Feature gene = chado_gene.getGene(); + final Feature gene = (Feature)chado_gene.getGene(); final String gene_id; try { @@ -72,9 +72,8 @@ public class GeneComponentTree extends JTree if(node == null) return; - Feature feature = chado_gene.getFeatureFromId((String)node.getUserObject()); - qualifier_text_area.setText( GeneBuilderFrame.getQualifierString( - (uk.ac.sanger.artemis.Feature)feature.getUserData() ) ); + Feature feature = (Feature)chado_gene.getFeatureFromId((String)node.getUserObject()); + gene_builder.setActiveFeature((uk.ac.sanger.artemis.Feature)feature.getUserData()); } }); @@ -91,8 +90,8 @@ public class GeneComponentTree extends JTree final ChadoCanonicalGene chado_gene) throws InvalidRelationException { - Vector transcripts = chado_gene.getTranscripts(); - Vector exons; + List transcripts = chado_gene.getTranscripts(); + List exons; Feature transcript; Feature exon; Feature protein; @@ -116,13 +115,19 @@ public class GeneComponentTree extends JTree for(int j=0; j<exons.size(); j++) { - exon = (Feature)exons.get(j); - exon_id = (String)exon.getQualifierByName("ID").getValues().get(0); + if(exons.get(j) instanceof Feature) + { + exon = (Feature)exons.get(j); + exon_id = (String)exon.getQualifierByName("ID").getValues().get(0); + } + else // ChadoFeature + exon_id = ((ChadoFeature)exons.get(j)).getUniquename(); + exon_node = new DefaultMutableTreeNode(exon_id); transcript_node.add(exon_node); } - protein = chado_gene.getProteinOfTranscript(transcript_id); + protein = (Feature)chado_gene.getProteinOfTranscript(transcript_id); if(protein == null) continue; diff --git a/uk/ac/sanger/artemis/components/genebuilder/GeneEdit.java b/uk/ac/sanger/artemis/components/genebuilder/GeneEdit.java new file mode 100644 index 0000000000000000000000000000000000000000..9967cbd9ddaa3971c442b9248def3e5cf04824a1 --- /dev/null +++ b/uk/ac/sanger/artemis/components/genebuilder/GeneEdit.java @@ -0,0 +1,434 @@ +/* + * + * created: 2006 + * + * This file is part of Artemis + * + * Copyright (C) 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. + * + */ + +package uk.ac.sanger.artemis.components.genebuilder; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + + +import java.sql.SQLException; +import java.util.Hashtable; +import java.util.List; +import java.util.Vector; + + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; + +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import javax.swing.JComboBox; +import javax.swing.JScrollPane; +import javax.swing.JPanel; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.Box; + +import uk.ac.sanger.artemis.chado.*; +import uk.ac.sanger.artemis.io.ChadoCanonicalGene; +import uk.ac.sanger.artemis.io.InvalidRelationException; +import uk.ac.sanger.artemis.io.GFFStreamFeature; +import uk.ac.sanger.artemis.io.ReadFormatException; +import uk.ac.sanger.artemis.util.DatabaseDocument; +import uk.ac.sanger.artemis.util.ByteBuffer; + + +/** + * Chado data access example code. This searches for features by their + * uniquename and returns their properties and attributes. + * + * @author tjc + * + */ +public class GeneEdit +{ + /** JDBC DAO */ + private JdbcDAO jdbcDAO = null; + + /** iBatis DAO */ + private IBatisDAO connIB = null; + + /** database URL */ + private String location; + + /** password fields */ + private JPasswordField pfield; + + /** <code>List</code> of <code>ChadoFeature</code> objects */ + private List featureList; + + /** + * + * + */ + public GeneEdit() + { + try + { + setLocation(); + final ChadoDAO dao = getDAO(); + showFeatureSearchPanel(dao); + } + catch(java.net.ConnectException ce) + { + ce.printStackTrace(); + } + catch(SQLException sqlExp) + { + JOptionPane.showMessageDialog(null, "SQL Problems...\n" + + sqlExp.getMessage(), "SQL Error", JOptionPane.ERROR_MESSAGE); + sqlExp.printStackTrace(); + } + + } + + /** + * Display a window for searching for features. + * + * @throws java.net.ConnectException + * @throws SQLException + */ + private void showFeatureSearchPanel(final ChadoDAO dao) + throws java.net.ConnectException, SQLException + { + int index = location.indexOf('=') + 1; + String schema = location.substring(index); + + final List schemas = dao.getSchema(); + + Vector v_schemas = new Vector(schemas); + v_schemas.add(0, "All"); + + final JPanel panel = new JPanel(new BorderLayout()); + final JComboBox schema_list = new JComboBox(v_schemas); + schema_list.setSelectedItem(schema); + + + JScrollPane jsp = new JScrollPane(schema_list); + panel.add(jsp, BorderLayout.EAST); + + Box xbox = Box.createHorizontalBox(); + final JTextField gene_text = new JTextField(20); + gene_text.setText("AfA24A6.005"); //"SPAC212.04c"); + xbox.add(gene_text); + gene_text.selectAll(); + + + JButton findButt = new JButton("RETRIEVE"); + findButt.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent event) + { + String search_gene = gene_text.getText(); + String schema = (String)schema_list.getSelectedItem(); + List schema_search; + if(schema.equalsIgnoreCase("All")) + schema_search = schemas; + else + { + schema_search = new Vector(); + schema_search.add(schema); + } + + try + { + search(search_gene, schema_search, dao); + } + catch(SQLException sqlExp) + { + JOptionPane.showMessageDialog(null, "SQL Problems...\n" + + sqlExp.getMessage(), "SQL Error", JOptionPane.ERROR_MESSAGE); + sqlExp.printStackTrace(); + } + catch(ReadFormatException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + xbox.add(findButt); + xbox.add(Box.createHorizontalGlue()); + panel.add(xbox, BorderLayout.NORTH); + + JFrame frame = new JFrame("Feature Search"); + frame.getContentPane().add(panel); + frame.setJMenuBar(getJMenuBar(dao)); + frame.pack(); + frame.setVisible(true); + } + + /** + * Build a <code>JMenuBar</code>. + * + * @return a <code>JMenuBar</code> + */ + public JMenuBar getJMenuBar(final ChadoDAO dao) + { + JMenuBar mbar = new JMenuBar(); + JMenu file = new JMenu("File"); + mbar.add(file); + + JMenuItem exit = new JMenuItem("Exit"); + exit.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + System.exit(0); + } + }); + file.add(exit); + + return mbar; + } + + + /** + * Search for a feature/gene name from a <code>List</code> of schemas. + * + * @param search_gene + * the feature name + * @param schema_search + * the <code>List</code> to search + * @param dao + * the data access object + * @return string array of results + * @throws SQLException + * @throws ReadFormatException + */ + public void search(final String search_gene, final List schema_search, + final ChadoDAO dao) throws SQLException, ReadFormatException + { + Hashtable id_store = new Hashtable(); + + ChadoFeature feature = new ChadoFeature(); + feature.setUniquename(search_gene); + featureList = dao.getLazyFeature(feature, + schema_search); + + ChadoCanonicalGene chado_gene = new ChadoCanonicalGene(); + + if(featureList.size() > 1) + System.err.println("More than one feature found!"); + + feature = (ChadoFeature)featureList.get(0); + id_store.put(Integer.toString(feature.getId()), feature.getUniquename()); + + int src = feature.getFeatureloc().getSrcfeature_id(); + + ChadoFeature parent = new ChadoFeature(); + parent.setId(src); + List parentList = dao.getLazyFeature(parent, schema_search); + parent = (ChadoFeature)parentList.get(0); + chado_gene.setSeqlen( parent.getLength() ); + + + ByteBuffer buff = new ByteBuffer(); + DatabaseDocument.chadoToGFF(feature, null, null, null, null, dao, buff); + + //System.out.println(new String(buff.getBytes())); + + GFFStreamFeature gff_gene_feature = new GFFStreamFeature(new String(buff.getBytes())); + chado_gene.setGene(gff_gene_feature); + + // get children of gene + List relations = feature.getFeatureRelationshipsForObjectId(); + + for(int i=0;i<relations.size(); i++) + { + ChadoFeature transcript = new ChadoFeature(); + transcript.setId( ((ChadoFeatureRelationship)relations.get(i)).getSubject_id() ); + featureList = dao.getLazyFeature(transcript, + schema_search); + + transcript = (ChadoFeature)featureList.get(0); + id_store.put(Integer.toString(transcript.getId()), transcript.getUniquename()); + buff = new ByteBuffer(); + DatabaseDocument.chadoToGFF(transcript, feature.getUniquename(), + null, null, id_store, dao, buff); + GFFStreamFeature gff_feature = new GFFStreamFeature(new String(buff.getBytes())); + + new uk.ac.sanger.artemis.Feature(gff_feature); + chado_gene.addTranscript(gff_feature); + + + // get children of transcript - exons and pp + List transcipt_relations = transcript.getFeatureRelationshipsForObjectId(); + + for(int j=0; j<transcipt_relations.size(); j++) + { + ChadoFeature child = new ChadoFeature(); + child.setId( ((ChadoFeatureRelationship)transcipt_relations.get(j)).getSubject_id() ); + featureList = dao.getLazyFeature(child, + schema_search); + + child = (ChadoFeature)featureList.get(0); + id_store.put(Integer.toString(child.getId()), child.getUniquename()); + buff = new ByteBuffer(); + DatabaseDocument.chadoToGFF(child, transcript.getUniquename(), + null, null, id_store, dao, buff); + + gff_feature = new GFFStreamFeature(new String(buff.getBytes())); + new uk.ac.sanger.artemis.Feature(gff_feature); + + try + { + if(child.getCvterm().getName().equalsIgnoreCase("polypeptide")) + chado_gene.addProtein(transcript.getUniquename(), child); + else if(child.getCvterm().getName().equalsIgnoreCase("exon")) + chado_gene.addExon(transcript.getUniquename(), child); + } + catch(InvalidRelationException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + } + + gff_gene_feature.setChadoGene(chado_gene); + + GeneBuilderFrame frame = + new GeneBuilderFrame(new uk.ac.sanger.artemis.Feature(gff_gene_feature), + null, null, null); + + } + + + + /** + * Get the data access object (DAO). + * + * @return data access object + */ + private ChadoDAO getDAO() throws java.net.ConnectException, SQLException + { + if(System.getProperty("ibatis") == null) + { + if(jdbcDAO == null) + jdbcDAO = new JdbcDAO(location, pfield); + return jdbcDAO; + } + else + { + if(connIB == null) + connIB = new IBatisDAO(pfield); + return connIB; + } + } + + /** + * Set the database location as: + * jdbc:postgresql://localhost:13001/chadoCVS?user=es2 + * + * @return true if location chosen + */ + protected boolean setLocation() + { + Container bacross = new Container(); + bacross.setLayout(new GridLayout(6, 2, 5, 5)); + + JLabel lServer = new JLabel("Server : "); + bacross.add(lServer); + JTextField inServer = new JTextField("localhost"); + bacross.add(inServer); + + JLabel lPort = new JLabel("Port : "); + bacross.add(lPort); + JTextField inPort = new JTextField("5432"); + bacross.add(inPort); + + JLabel lDB = new JLabel("Database : "); + bacross.add(lDB); + JTextField inDB = new JTextField("chado"); + bacross.add(inDB); + + JLabel lUser = new JLabel("User : "); + bacross.add(lUser); + JTextField inUser = new JTextField("afumigatus"); + bacross.add(inUser); + + JLabel lpasswd = new JLabel("Password : "); + bacross.add(lpasswd); + pfield = new JPasswordField(16); + bacross.add(pfield); + + // given -Dchado=localhost:port/dbname?username + if(System.getProperty("chado") != null) + { + String db_url = System.getProperty("chado").trim(); + int index; + if((index = db_url.indexOf(":")) > -1) + { + inServer.setText(db_url.substring(0, index)); + int index2; + if((index2 = db_url.indexOf("/")) > -1) + { + inPort.setText(db_url.substring(index + 1, index2)); + int index3; + if((index3 = db_url.indexOf("?")) > -1) + { + inDB.setText(db_url.substring(index2 + 1, index3)); + inUser.setText(db_url.substring(index3 + 1)); + + /* + * if(!prompt_user) { location = "jdbc:postgresql://" + * +inServer.getText().trim()+ ":" +inPort.getText().trim()+ "/" + * +inDB.getText().trim()+ "?user=" +inUser.getText().trim(); return + * true; } + */ + } + } + } + } + + int n = JOptionPane.showConfirmDialog(null, bacross, + "Enter Database Address", JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + if(n == JOptionPane.CANCEL_OPTION) + return false; + + location = "jdbc:postgresql://" + inServer.getText().trim() + ":" + + inPort.getText().trim() + "/" + inDB.getText().trim() + "?user=" + + inUser.getText().trim(); + + return true; + } + + + public static void main(String args[]) + { + new GeneEdit(); + } +} diff --git a/uk/ac/sanger/artemis/components/genebuilder/GeneViewerPanel.java b/uk/ac/sanger/artemis/components/genebuilder/GeneViewerPanel.java index 46506c0ce98c8136e38e6acd91359a0c6c905076..504d73869f2672130dac556ba0b2a3d28d926757 100644 --- a/uk/ac/sanger/artemis/components/genebuilder/GeneViewerPanel.java +++ b/uk/ac/sanger/artemis/components/genebuilder/GeneViewerPanel.java @@ -20,28 +20,36 @@ * 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/genebuilder/GeneViewerPanel.java,v 1.2 2006-05-31 15:40:53 tjc Exp $ + * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/genebuilder/GeneViewerPanel.java,v 1.3 2006-07-04 15:57:57 tjc Exp $ */ package uk.ac.sanger.artemis.components.genebuilder; import javax.swing.*; import java.awt.*; -import java.util.Vector; +import java.util.List; +import java.util.Collections; import java.awt.geom.RoundRectangle2D; +import uk.ac.sanger.artemis.FeatureSegment; +import uk.ac.sanger.artemis.FeatureSegmentVector; +import uk.ac.sanger.artemis.chado.ChadoFeature; +import uk.ac.sanger.artemis.chado.ChadoFeatureLoc; import uk.ac.sanger.artemis.io.Feature; import uk.ac.sanger.artemis.io.ChadoCanonicalGene; import uk.ac.sanger.artemis.io.InvalidRelationException; +import uk.ac.sanger.artemis.io.Range; public class GeneViewerPanel extends JPanel { + private ChadoCanonicalGene chado_gene; private int border = 15; public GeneViewerPanel(final ChadoCanonicalGene chado_gene) { this.chado_gene = chado_gene; + Dimension dim = new Dimension(300,300); setPreferredSize(dim); setBackground(Color.white); @@ -49,9 +57,11 @@ public class GeneViewerPanel extends JPanel public void paintComponent(Graphics g) { + super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; - Feature embl_gene = chado_gene.getGene(); - uk.ac.sanger.artemis.Feature gene = (uk.ac.sanger.artemis.Feature)embl_gene.getUserData(); + Feature embl_gene = (Feature)chado_gene.getGene(); + uk.ac.sanger.artemis.Feature gene = + (uk.ac.sanger.artemis.Feature)embl_gene.getUserData(); setFont(uk.ac.sanger.artemis.Options.getOptions().getFont()); final FontMetrics fm = this.getFontMetrics(getFont()); @@ -61,6 +71,7 @@ public class GeneViewerPanel extends JPanel boolean complement = embl_gene.getLocation().isComplement(); BasicStroke stroke = new BasicStroke(2.f); + Stroke original_stroke = g2d.getStroke(); g2d.setStroke(stroke); g2d.setColor( gene.getColour() ); @@ -72,11 +83,12 @@ public class GeneViewerPanel extends JPanel g2d.drawLine(border, ypos, getSize().width - border, ypos); - Vector transcripts = chado_gene.getTranscripts(); + List transcripts = chado_gene.getTranscripts(); Feature embl_transcript; uk.ac.sanger.artemis.Feature transcript; - float fraction = (float)(getSize().width - (2*border))/(float)(end-start); + float fraction = (float)(getSize().width - (2*border))/ + (float)(end-start); for(int i=0; i<transcripts.size(); i++) { ypos += border; @@ -95,7 +107,7 @@ public class GeneViewerPanel extends JPanel try { - Vector exons = chado_gene.getExonsOfTranscript( + List exons = chado_gene.getExonsOfTranscript( (String)embl_transcript.getQualifierByName("ID").getValues().get(0)); ypos += border; @@ -103,26 +115,87 @@ public class GeneViewerPanel extends JPanel if(exons == null) continue; + int offset = 0; + + if(exons.get(0) instanceof ChadoFeature) + { + int last_ex_start = 0; + int last_ex_end = 0; + int last_ypos = 0; + + ChadoFeature start_exon = (ChadoFeature)exons.get(0); + if(start_exon.getFeatureloc().getStrand() == -1) + { + if( start_exon.getFeatureloc().getFmin() < + ((ChadoFeature)exons.get(exons.size()-1)).getFeatureloc().getFmin()) + Collections.reverse(exons); + } + + for(int j=0; j<exons.size(); j++) + { + ChadoFeature exon = (ChadoFeature)exons.get(j); + + int ex_start = border+(int)((exon.getFeatureloc().getFmin()+1-start)*fraction); + int ex_end = border+(int)((exon.getFeatureloc().getFmax()-start)*fraction); + + Color exon_col = Color.CYAN; + + offset = getFrameID(chado_gene, exon, j, exons) * getFontHeight() * 2; + + boolean isForward = false; + if(exon.getFeatureloc().getStrand() == 1) + isForward = true; + + drawExons(g2d, ex_start, ex_end, + last_ex_start, last_ex_end, last_ypos, + offset, ypos, exon_col, + original_stroke, stroke, isForward); + + last_ex_end = ex_end; + last_ex_start = ex_start; + last_ypos = ypos+offset; + } + continue; + } + + for(int j=0; j<exons.size(); j++) { + int last_ex_start = 0; + int last_ex_end = 0; + int last_ypos = 0; + Feature embl_exon = (Feature)exons.get(j); + uk.ac.sanger.artemis.Feature exon = (uk.ac.sanger.artemis.Feature)embl_exon.getUserData(); - - int ex_start = border+(int)((embl_exon.getFirstBase()-start)*fraction); - int ex_end = border+(int)((embl_exon.getLastBase()-start)*fraction); - - if(exon.getColour() != null) - g2d.setColor( exon.getColour() ); - RoundRectangle2D e = new RoundRectangle2D.Float(ex_start, ypos, ex_end-ex_start, - border, 0, ypos); - GradientPaint gp = new GradientPaint(ex_start, ypos, exon.getColour(), - ex_start, ypos+(border/2), Color.white, true); - g2d.setPaint(gp); - g2d.fill(e); - - //g2d.drawLine(ex_start, ypos, ex_end, ypos); + + FeatureSegmentVector segments = exon.getSegments(); + + for(int k=0; k<segments.size(); k++) + { + FeatureSegment segment = segments.elementAt(k); + + Range range = segment.getRawRange(); + offset = segment.getFrameID() * getFontHeight() * 2; + + int ex_start = border+(int)((range.getStart()-start)*fraction); + int ex_end = border+(int)((range.getEnd()-start)*fraction); + + if(exon.getColour() != null) + g2d.setColor( exon.getColour() ); + + drawExons(g2d, ex_start, ex_end, + last_ex_start, last_ex_end, last_ypos, + offset, ypos, exon.getColour(), + original_stroke, stroke, segment.isForwardSegment()); + + last_ex_end = ex_end; + last_ex_start = ex_start; + last_ypos = ypos+offset; + } } + } catch(InvalidRelationException e) { @@ -130,4 +203,157 @@ public class GeneViewerPanel extends JPanel } } } + + + private void drawExons(Graphics2D g2d, int ex_start, int ex_end, + int last_ex_start, int last_ex_end, int last_ypos, + int offset, int ypos, Color exon_colour, + Stroke original_stroke, Stroke stroke, boolean isForward) + { + RoundRectangle2D e = new RoundRectangle2D.Float(ex_start, ypos+offset, + ex_end-ex_start, + getFontHeight(), 0, ypos+offset); + + GradientPaint gp = new GradientPaint(ex_start, ypos+offset, + exon_colour, + ex_start, ypos+offset+(getFontHeight()/2), + Color.white, true); + g2d.setPaint(gp); + g2d.fill(e); + + // draw connections + if(last_ex_end != 0 || + last_ex_start != 0) + { + g2d.setStroke(original_stroke); + int ymid; + if(last_ypos < ypos+offset) + ymid = last_ypos; + else + ymid = ypos+offset; + + if(isForward) + { + g2d.drawLine(last_ex_end, last_ypos, + last_ex_end+((ex_start-last_ex_end)/2), ymid-getFontHeight()/2); + g2d.drawLine(last_ex_end+((ex_start-last_ex_end)/2), ymid-getFontHeight()/2, + ex_start, ypos+offset); + } + else + { + g2d.drawLine(last_ex_start, last_ypos, + last_ex_start+((ex_end-last_ex_start)/2), ymid-getFontHeight()/2); + g2d.drawLine(last_ex_start+((ex_end-last_ex_start)/2), ymid-getFontHeight()/2, + ex_end, ypos+offset); + } + g2d.setStroke(stroke); + } + + } + + private int getFrameID(ChadoCanonicalGene chado_gene, ChadoFeature feature, + int nexon, List exons) + { + final int position_on_strand; + + if(feature.getFeatureloc().getStrand() == -1) + position_on_strand = chado_gene.getSeqlen()-feature.getFeatureloc().getFmax(); + else + position_on_strand = feature.getFeatureloc().getFmin(); + + // this will be 0, 1 or 2 depending on which frame the segment is in + final int start_base_modulo = + (position_on_strand + getFrameShift(nexon, exons)) % 3; + + if(feature.getFeatureloc().getStrand() == 1) + { + switch (start_base_modulo) + { + case 0: + return FeatureSegment.FORWARD_FRAME_1; + case 1: + return FeatureSegment.FORWARD_FRAME_2; + case 2: + return FeatureSegment.FORWARD_FRAME_3; + } + } + else + { + switch (start_base_modulo) + { + case 0: + return FeatureSegment.REVERSE_FRAME_1; + case 1: + return FeatureSegment.REVERSE_FRAME_2; + case 2: + return FeatureSegment.REVERSE_FRAME_3; + } + } + + return FeatureSegment.NO_FRAME; + } + + + /** + * Returns 0, 1 or 2 depending on which translation frame this segment is + * in. A frame shift of zero means that the bases should be translated + * starting at the start position of this segment, 1 means start + * translating one base ahead of the start position and 2 means start + * translating two bases ahead of the start position. + **/ + private int getFrameShift(int nexon, List exons) + { + // find the number of bases in the segments before this one + int base_count = 0; + int direction = 0; + + for(int i = 0; i < exons.size(); ++i) + { + ChadoFeature this_feature = (ChadoFeature)exons.get(i); + ChadoFeatureLoc featureLoc = this_feature.getFeatureloc(); + + int this_direction; + if(featureLoc.getStrand() == 1) + this_direction = 1; + else + this_direction = -1; + + if(i == nexon) + { + if(i != 0 && this_direction != direction) + base_count = 0; + + break; + } + else + { + if(i == 0) + direction = this_direction; + else if(this_direction != direction) + base_count = 0; + + base_count += featureLoc.getFmax()-featureLoc.getFmin(); + } + } + + int codon_start = ((ChadoFeature)exons.get(nexon)).getFeatureloc().getPhase(); + int mod_value = (base_count + 3 - codon_start) % 3; + + //System.out.println("GVP mod_value="+mod_value+" base_count="+base_count + " codon_start="+codon_start); + if(mod_value == 1) + return 2; + else if(mod_value == 2) + return 1; + else + return 0; + } + + + + private int getFontHeight() + { + final FontMetrics fm = this.getFontMetrics(getFont()); + return fm.getHeight(); + } + } \ No newline at end of file