1 /* TransferAnnotationTool.java
2  *
3  * created: 2009
4  *
5  * This file is part of Artemis
6  *
7  * Copyright (C) 2009  Genome Research Limited
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  */
24 
25 package uk.ac.sanger.artemis.components;
26 
27 import java.awt.BorderLayout;
28 import java.awt.Color;
29 import java.awt.Cursor;
30 import java.awt.Dimension;
31 import java.awt.FlowLayout;
32 import java.awt.Font;
33 import java.awt.GridBagConstraints;
34 import java.awt.GridBagLayout;
35 import java.awt.Insets;
36 import java.awt.event.ActionEvent;
37 import java.awt.event.ActionListener;
38 import java.awt.event.ItemEvent;
39 import java.awt.event.ItemListener;
40 import java.util.Enumeration;
41 import java.util.Hashtable;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Set;
45 import java.util.Vector;
46 
47 import javax.swing.BorderFactory;
48 import javax.swing.Box;
49 import javax.swing.JButton;
50 import javax.swing.JCheckBox;
51 import javax.swing.JFrame;
52 import javax.swing.JLabel;
53 import javax.swing.JOptionPane;
54 import javax.swing.JPanel;
55 import javax.swing.JScrollPane;
56 import javax.swing.JTextArea;
57 import javax.swing.SwingConstants;
58 import javax.swing.border.EtchedBorder;
59 import javax.swing.border.TitledBorder;
60 
61 import uk.ac.sanger.artemis.Entry;
62 import uk.ac.sanger.artemis.EntryGroup;
63 import uk.ac.sanger.artemis.Feature;
64 import uk.ac.sanger.artemis.FeaturePredicate;
65 import uk.ac.sanger.artemis.FeatureVector;
66 import uk.ac.sanger.artemis.SimpleEntryGroup;
67 import uk.ac.sanger.artemis.chado.ChadoTransactionManager;
68 import uk.ac.sanger.artemis.components.filetree.LocalAndRemoteFileManager;
69 import uk.ac.sanger.artemis.components.genebuilder.GeneEdit;
70 import uk.ac.sanger.artemis.components.genebuilder.GeneUtils;
71 import uk.ac.sanger.artemis.components.genebuilder.ortholog.MatchPanel;
72 import uk.ac.sanger.artemis.io.ChadoCanonicalGene;
73 import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
74 import uk.ac.sanger.artemis.io.GFFStreamFeature;
75 import uk.ac.sanger.artemis.io.InvalidRelationException;
76 import uk.ac.sanger.artemis.io.PartialSequence;
77 import uk.ac.sanger.artemis.io.Qualifier;
78 import uk.ac.sanger.artemis.io.QualifierVector;
79 import uk.ac.sanger.artemis.util.DatabaseDocument;
80 import uk.ac.sanger.artemis.util.StringVector;
81 
82 public class TransferAnnotationTool extends JFrame
83 {
84   private static final long serialVersionUID = 1L;
85   private static String[] NON_TRANSFERABLE_QUALIFIERS =
86   {
87     "ID",
88     "feature_id",
89     "Derives_from",
90     "feature_relationship_rank",
91     "Parent",
92     "isObsolete",
93     "Start_range",
94     "End_range",
95     "timelastmodified",
96     "cytoplasm_location",
97     "cytoplasmic_polypeptide_region",
98     "membrane_structure",
99     "non_cytoplasm_location",
100     "non_cytoplasmic_polypeptide_region",
101     "orthologous_to",
102     "paralogous_to",
103     "pepstats_file",
104     "PlasmoAP_score",
105     "polypeptide_domain",
106     "fasta_file",
107     "blastp_file",
108     "blastn_file",
109     "systematic_id",
110     "transmembrane",
111     "transmembrane_polypeptide_region",
112     "previous_systematic_id"
113   };
114   private MatchPanel matchPanel;
115 
116   private static org.apache.log4j.Logger logger4j =
117     org.apache.log4j.Logger.getLogger(TransferAnnotationTool.class);
118 
119   protected static Color STEEL_BLUE = new Color(25, 25, 112);
120 
121   /**
122    * Tool for transferring annotation from one feature to other feature(s)
123    * @param feature
124    * @param entryGroup
125    * @param geneNames
126    */
TransferAnnotationTool(final Feature feature, final EntryGroup entryGroup, final MatchPanel matchPanel)127   public TransferAnnotationTool(final Feature feature,
128   		                        final EntryGroup entryGroup,
129   		                        final MatchPanel matchPanel)
130   {
131     super("Transfer Annotation Tool :: "+ feature.getIDString());
132     this.matchPanel = matchPanel;
133 
134     List<String> geneNames = null;
135     if(matchPanel != null)
136       geneNames = matchPanel.getGeneNameList();
137 
138     JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
139     JPanel pane = new JPanel(new GridBagLayout());
140     JScrollPane jsp = new JScrollPane(panel);
141     panel.setBackground(Color.white);
142     pane.setBackground(Color.white);
143     panel.add(pane);
144 
145     JPanel framePanel = (JPanel)getContentPane();
146     framePanel.add(jsp, BorderLayout.CENTER);
147     framePanel.setPreferredSize(new Dimension(600,600));
148 
149     final Vector<JCheckBox> geneNameCheckBoxes = new Vector<JCheckBox>();
150     final Vector<QualifierPanel> qualifierPanels = new Vector<QualifierPanel>();
151 
152     addMainPanel(feature, pane, qualifierPanels,
153                  geneNameCheckBoxes, geneNames);
154     addBottomButtons(qualifierPanels, geneNameCheckBoxes,
155                      framePanel, entryGroup);
156     pack();
157     setVisible(true);
158   }
159 
160   /**
161    * Construct the panel for setting up the gene list and the
162    * list of qualifiers to transfer.
163    * @param feature
164    * @param pane
165    * @param qualifierCheckBoxes
166    * @param geneNameCheckBoxes
167    * @param geneNames
168    */
addMainPanel(final Feature feature, final JPanel pane, final Vector<QualifierPanel> qualifierPanels, final Vector<JCheckBox> geneNameCheckBoxes, final List<String> geneNames)169   private void addMainPanel(final Feature feature,
170                             final JPanel pane,
171                             final Vector<QualifierPanel> qualifierPanels,
172                             final Vector<JCheckBox> geneNameCheckBoxes,
173                             final List<String> geneNames)
174   {
175     GridBagConstraints c = new GridBagConstraints();
176     int nrows = 0;
177 
178     c.anchor = GridBagConstraints.NORTHWEST;
179     c.gridx = 2;
180     c.gridy = 0;
181     c.ipadx = 50;
182 
183     JLabel geneLabel = new JLabel("Qualifier(s)");
184     geneLabel.setFont(geneLabel.getFont().deriveFont(Font.BOLD));
185     pane.add(geneLabel, c);
186 
187     c.gridy = 0;
188     c.gridx = 0;
189     JLabel label = new JLabel("Gene List");
190     label.setFont(label.getFont().deriveFont(Font.BOLD));
191     pane.add(label, c);
192 
193     nrows+=3;
194     c.gridx = 2;
195     c.gridy = nrows;
196     c.anchor = GridBagConstraints.WEST;
197 
198     addQualifierPanel(feature, qualifierPanels, c, nrows, pane);
199     nrows+=2;
200 
201     if(feature.getEmblFeature() instanceof GFFStreamFeature)
202     {
203       GFFStreamFeature gffFeature =
204         ((GFFStreamFeature) feature.getEmblFeature());
205       if (gffFeature.getChadoGene() != null)
206       {
207         String id = GeneUtils.getUniqueName(gffFeature);
208         ChadoCanonicalGene chadoGene = gffFeature.getChadoGene();
209         Feature gene = (Feature) chadoGene.getGene().getUserData();
210 
211         if(!id.equals( GeneUtils.getUniqueName(((GFFStreamFeature)chadoGene.getGene())) ))
212          addQualifierPanel(gene, qualifierPanels, c, nrows, pane);
213 
214         nrows+=2;
215 
216         String transcriptName =
217           chadoGene.getTranscriptFromName(GeneUtils.getUniqueName(gffFeature));
218 
219         if(transcriptName != null)
220         {
221           GFFStreamFeature transcript =
222                  (GFFStreamFeature) chadoGene.getFeatureFromId(transcriptName);
223           addQualifierPanel((Feature)transcript.getUserData(),
224               qualifierPanels, c, nrows, pane);
225           nrows+=2;
226 
227           Set<uk.ac.sanger.artemis.io.Feature> children = chadoGene.getChildren(transcript);
228           Iterator<uk.ac.sanger.artemis.io.Feature> it = children.iterator();
229 
230           while(it.hasNext())
231           {
232             GFFStreamFeature kid = (GFFStreamFeature)it.next();
233             if(id.equals( GeneUtils.getUniqueName(((GFFStreamFeature)kid)) ))
234               continue;
235             addQualifierPanel((Feature)kid.getUserData(), qualifierPanels,
236                               c, nrows, pane);
237             nrows+=2;
238           }
239         }
240       }
241     }
242 
243     c.gridx = 0;
244     c.gridy = 3;
245     c.gridheight = nrows;
246     c.fill = GridBagConstraints.BOTH;
247 
248     final Box geneNameBox = Box.createVerticalBox();
249     pane.add(geneNameBox, c);
250 
251     if(geneNames != null)
252     {
253       for(int i = 0; i < geneNames.size(); i++)
254       {
255         JCheckBox cb = new JCheckBox((String) geneNames.get(i),true);
256         geneNameBox.add(cb);
257         geneNameCheckBoxes.add(cb);
258       }
259     }
260 
261     c.gridy = 1;
262     c.gridheight = 1;
263     c.fill = GridBagConstraints.NONE;
264     c.gridx = 2;
265     final JButton toggle = new JButton("Toggle");
266     toggle.addActionListener(new ActionListener()
267     {
268       public void actionPerformed(ActionEvent e)
269       {
270         for(int i=0; i<qualifierPanels.size(); i++)
271         {
272           QualifierPanel qP = qualifierPanels.get(i);
273           Enumeration<JCheckBox> enumQualifiers = qP.getQualifierCheckBoxes().keys();
274           while(enumQualifiers.hasMoreElements())
275           {
276             JCheckBox cb = enumQualifiers.nextElement();
277             cb.setSelected(!cb.isSelected());
278           }
279         }
280       }
281     });
282     pane.add(toggle, c);
283 
284     Box xBox = Box.createHorizontalBox();
285     final JButton toggleGeneList = new JButton("Toggle");
286     toggleGeneList.addActionListener(new ActionListener()
287     {
288       public void actionPerformed(ActionEvent e)
289       {
290         for(int i = 0; i < geneNameCheckBoxes.size(); i++)
291         {
292           JCheckBox cb = geneNameCheckBoxes.get(i);
293           cb.setSelected(!cb.isSelected());
294         }
295         geneNameBox.repaint();
296       }
297     });
298     xBox.add(toggleGeneList);
299 
300     final JButton addGenes = new JButton("Add");
301     addGenes.addActionListener(new ActionListener()
302     {
303       public void actionPerformed(ActionEvent e)
304       {
305         JTextArea geneNameTextArea = new JTextArea();
306         geneNameTextArea.setEditable(true);
307         JScrollPane jsp = new JScrollPane(geneNameTextArea);
308 
309         int res = JOptionPane.showConfirmDialog(TransferAnnotationTool.this,
310                  jsp, "Paste Feature Names to Add",
311                  JOptionPane.OK_CANCEL_OPTION);
312         if(res == JOptionPane.CANCEL_OPTION)
313           return;
314 
315         String geneNames[] = geneNameTextArea.getText().split("\\s");
316         for(int i=0;i<geneNames.length; i++)
317         {
318           if(geneNames[i] == null || geneNames[i].equals(""))
319             continue;
320            JCheckBox cb = new JCheckBox(geneNames[i],true);
321            geneNameBox.add(cb);
322            geneNameCheckBoxes.add(cb);
323         }
324         pane.revalidate();
325       }
326     });
327     xBox.add(addGenes);
328     c.gridx = 0;
329     pane.add(xBox, c);
330 
331     final List<String> clusterList = (matchPanel == null ? null : matchPanel.getGeneNameList(true));
332     if(clusterList != null && !geneNames.contains(clusterList.get(0)))
333     {
334       final JButton importCluster = new JButton("Import Cluster Names");
335       importCluster.addActionListener(new ActionListener()
336       {
337           public void actionPerformed(ActionEvent e)
338           {
339             for(String n: clusterList)
340             {
341               if(n == null || n.equals(""))
342                 continue;
343                JCheckBox cb = new JCheckBox(n,true);
344                geneNameBox.add(cb);
345                geneNameCheckBoxes.add(cb);
346             }
347             importCluster.setEnabled(false);
348             pane.revalidate();
349           }
350         });
351       c.gridy = 2;
352       pane.add(importCluster, c);
353     }
354   }
355 
356   /**
357    * Add a panel to display a given features qualifiers.
358    * @param f
359    * @param qualifierPanels
360    * @param c
361    * @param nrows
362    * @param pane
363    */
addQualifierPanel(Feature f, Vector<QualifierPanel> qualifierPanels, GridBagConstraints c, int nrows, JPanel pane)364   private void addQualifierPanel(Feature f,
365                                  Vector<QualifierPanel> qualifierPanels,
366                                  GridBagConstraints c,
367                                  int nrows,
368                                  JPanel pane)
369   {
370     QualifierPanel qPanel = new QualifierPanel(f,f.getKey().getKeyString());
371     if(qPanel.nrows == 0)
372       return;
373 
374     c.fill = GridBagConstraints.HORIZONTAL;
375     c.anchor = GridBagConstraints.WEST;
376     c.weightx = 100;
377     qualifierPanels.add(qPanel);
378     c.gridy = ++nrows;
379 
380     JLabel l = new JLabel(f.getIDString());
381     l.setFont(l.getFont().deriveFont(Font.BOLD));
382     l.setForeground(STEEL_BLUE);
383     pane.add(l, c);
384 
385     c.gridy = ++nrows;
386     pane.add(qPanel, c);
387     c.weightx = 0.d;
388   }
389 
390 
391   /**
392    * Add panel for the transfer and close button.
393    * @param qualifierCheckBoxes
394    * @param geneNameCheckBoxes
395    * @param framePanel
396    * @param feature
397    * @param entryGroup
398    */
addBottomButtons(final Vector<QualifierPanel> qualifierPanels, final Vector<JCheckBox> geneNameCheckBoxes, final JPanel framePanel, final EntryGroup entryGroup)399   private void addBottomButtons(final Vector<QualifierPanel> qualifierPanels,
400                                 final Vector<JCheckBox> geneNameCheckBoxes,
401                                 final JPanel framePanel,
402                                 final EntryGroup entryGroup)
403   {
404     final JCheckBox sameKeyCheckBox = new JCheckBox("Add to feature of same key", true);
405 
406     final JCheckBox overwriteCheckBox = new JCheckBox("Overwrite", false);
407     overwriteCheckBox.setToolTipText("overwrite rather than append values");
408 
409     final JCheckBox cvCheckBox = new JCheckBox("Set evidence as ISO and link to source in WITH/FROM", false);
410     cvCheckBox.setToolTipText("for GO and Product qualifiers set the evidence as ISO (Inferred from\n"+
411                               "Sequence Orthology) and add a link to the source in the WITH/FROM field");
412 
413     Box buttonBox = Box.createHorizontalBox();
414     final JButton transfer = new JButton(">>TRANSFER");
415     transfer.setToolTipText("transfer annotation");
416     transfer.addActionListener(new ActionListener()
417     {
418       public void actionPerformed(ActionEvent e)
419       {
420         if(overwriteCheckBox.isSelected())
421         {
422           int res = JOptionPane.showConfirmDialog(TransferAnnotationTool.this,
423               "Overwrite selected annotation?", "Overwrite", JOptionPane.OK_CANCEL_OPTION);
424           if(res == JOptionPane.CANCEL_OPTION)
425             return;
426         }
427 
428         final boolean autoHistorySetting = LocalAndRemoteFileManager.isAutomaticHistory();
429         StringBuffer summary = new StringBuffer();
430         try
431         {
432           LocalAndRemoteFileManager.setAutomaticHistory(false);
433           setCursor(new Cursor(Cursor.WAIT_CURSOR));
434           StringBuffer buff = new StringBuffer();
435 
436           for(int i = 0; i < qualifierPanels.size(); i++)
437           {
438             QualifierPanel qP = qualifierPanels.get(i);
439             int res = transferAnnotation(qP.getQualifierCheckBoxes(),
440               geneNameCheckBoxes, qP.getFeature(), entryGroup,
441               sameKeyCheckBox.isSelected(),
442               overwriteCheckBox.isSelected(),
443               cvCheckBox.isSelected(),
444               buff, summary);
445             if(res == -1)
446               break;
447           }
448 
449           if(buff.length() > 0)
450             logger4j.debug("TRANSFERRED ANNOTATION SUMMARY:\n"+buff.toString());
451           setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
452 
453           if(summary.length()>0)
454           {
455             final JTextArea list = new JTextArea(summary.toString());
456             final JScrollPane jsp = new JScrollPane(list);
457             jsp.setPreferredSize(new Dimension(300,200));
458             JOptionPane.showMessageDialog(
459                 TransferAnnotationTool.this, jsp,
460                 "Summary of Genes Changed",
461                 JOptionPane.INFORMATION_MESSAGE);
462           }
463         }
464         finally
465         {
466           LocalAndRemoteFileManager.setAutomaticHistory(autoHistorySetting);
467         }
468       }
469     });
470     Box yBox = Box.createVerticalBox();
471     yBox.add(transfer);
472     yBox.add(sameKeyCheckBox);
473     yBox.add(overwriteCheckBox);
474     yBox.add(cvCheckBox);
475     buttonBox.add(yBox);
476 
477     final JButton close = new JButton("CLOSE");
478     close.addActionListener(new ActionListener()
479     {
480       public void actionPerformed(ActionEvent e)
481       {
482         dispose();
483       }
484     });
485     yBox = Box.createVerticalBox();
486     yBox.add(close);
487     yBox.add(Box.createVerticalGlue());
488     buttonBox.add(yBox);
489     buttonBox.add(Box.createHorizontalGlue());
490     framePanel.add(buttonBox, BorderLayout.SOUTH);
491   }
492 
493 
494   /**
495    * Returns true if this qualifier is non-transferable
496    * @param qualifierName
497    * @return
498    */
isNonTransferable(String qualifierName)499   protected static boolean isNonTransferable(String qualifierName)
500   {
501     for(int i=0; i<NON_TRANSFERABLE_QUALIFIERS.length; i++)
502     {
503       if(NON_TRANSFERABLE_QUALIFIERS[i].equals(qualifierName))
504         return true;
505     }
506     return false;
507   }
508 
509   /**
510    * Transfer selected qualifiers to the list of features defined
511    * by the selected names.
512    * @param qualifierCheckBoxes - list of qualifier check boxes
513    * @param geneNameTextArea - text with a list of feature names to transfer to
514    * @param feature - feature to copy from
515    * @param entryGroup
516    * @param sameKey
517    * @param overwrite
518    */
transferAnnotation( final Hashtable<JCheckBox, Vector<JCheckBox>> qualifierCheckBoxes, final Vector<JCheckBox> geneNameCheckBoxes, final Feature orginatingFeature, final EntryGroup entryGroup, final boolean sameKey, final boolean overwrite, final boolean setEvidenceAndWithFrom, final StringBuffer buff, final StringBuffer genesUpdated)519   protected static int transferAnnotation(
520              final Hashtable<JCheckBox, Vector<JCheckBox>> qualifierCheckBoxes,
521   		     final Vector<JCheckBox> geneNameCheckBoxes,
522   		     final Feature orginatingFeature,
523   		     final EntryGroup entryGroup,
524   		     final boolean sameKey,
525   		     final boolean overwrite,
526   		     final boolean setEvidenceAndWithFrom,
527   		     final StringBuffer buff,
528   		     final StringBuffer genesUpdated)
529   {
530     // transfer selected annotation to genes
531     final QualifierVector qualifiers = orginatingFeature.getQualifiers();
532     final QualifierVector qualifiersToTransfer = new QualifierVector();
533 
534     Enumeration<JCheckBox> enumQualifiers = qualifierCheckBoxes.keys();
535     while(enumQualifiers.hasMoreElements())
536     {
537       JCheckBox cb = enumQualifiers.nextElement();
538       if (cb.isSelected())
539       {
540         Vector<JCheckBox> qualifierValuesCheckBox = qualifierCheckBoxes.get(cb);
541         final StringVector values = qualifiers.getQualifierByName(cb.getText()).getValues();
542         StringVector valuesToTransfer = new StringVector(values);
543 
544         logger4j.debug("TRANSFER "+cb.getText());
545         for(int i=0; i<qualifierValuesCheckBox.size(); i++)
546         {
547           JCheckBox valuesCb = qualifierValuesCheckBox.get(i);
548           if(!valuesCb.isSelected())
549           {
550             valuesToTransfer.remove(valuesCb.getText());
551             logger4j.debug("NOT TRANSFERING "+valuesCb.getText());
552           }
553         }
554 
555         if(valuesToTransfer.size() < 1)
556           continue;
557 
558         valuesToTransfer = new StringVector( getTransferValues(
559             setEvidenceAndWithFrom, orginatingFeature, cb.getText(), valuesToTransfer) );
560 
561         qualifiersToTransfer.addElement(new Qualifier(cb.getText(), valuesToTransfer));
562       }
563     }
564 
565     int count = 0;
566   	for(int i =0; i<geneNameCheckBoxes.size(); i++)
567   	{
568   	  if( geneNameCheckBoxes.get(i).isSelected() )
569   	    count++;
570   	}
571 
572   	if(count < 1)
573   	{
574   	  JOptionPane.showMessageDialog(null, "No genes selected.",
575         "Warning", JOptionPane.WARNING_MESSAGE);
576   	  return -1;
577   	}
578 
579   	String geneNames[] = new String[count];
580   	count = 0;
581   	for(int i =0; i<geneNameCheckBoxes.size(); i++)
582     {
583   	  JCheckBox cb = geneNameCheckBoxes.get(i);
584       if( cb.isSelected() )
585       {
586         geneNames[count] = cb.getText();
587         logger4j.debug("TRANSFER ANNOTATION TO "+geneNames[count]);
588         count++;
589       }
590     }
591 
592   	final String key = orginatingFeature.getKey().getKeyString();
593   	final FeatureVector features = entryGroup.getAllFeatures();
594 
595   	// transfer selected annotation
596   	entryGroup.getActionController().startAction();
597   	geneNames = transfer(features, qualifiersToTransfer, key, sameKey, overwrite,
598   			             GeneUtils.isDatabaseEntry(entryGroup), geneNames, genesUpdated);
599   	entryGroup.getActionController().endAction();
600 
601   	//
602   	// Commit changes to genes not in Artemis but in the database
603   	//
604     Vector<String> genesNotFound = null;
605     if (geneNames != null &&
606         orginatingFeature.getEntry().getEMBLEntry() instanceof DatabaseDocumentEntry)
607     {
608       DatabaseDocumentEntry db_entry =
609         (DatabaseDocumentEntry) orginatingFeature.getEntry().getEMBLEntry();
610       DatabaseDocument doc = (DatabaseDocument) db_entry.getDocument();
611 
612       for (int i = 0; i < geneNames.length; i++)
613       {
614         DatabaseDocumentEntry newDbEntry = GeneEdit.makeGeneEntry(null,
615             geneNames[i], doc, null);
616 
617         if (newDbEntry == null)
618         {
619           if (genesNotFound == null)
620             genesNotFound = new Vector<String>();
621           genesNotFound.add(geneNames[i]);
622           continue;
623         }
624 
625         char[] c = new char[1];
626         PartialSequence ps = new PartialSequence(c, 100, 0, null, null);
627         newDbEntry.setPartialSequence(ps);
628         Entry entry = null;
629         try
630         {
631           entry = new Entry(newDbEntry);
632         }
633         catch (Exception e)
634         {
635           e.printStackTrace();
636         }
637 
638         SimpleEntryGroup entry_group = new SimpleEntryGroup();
639         entry_group.addElement(entry);
640 
641         ChadoTransactionManager ctm = new ChadoTransactionManager();
642         entry_group.addFeatureChangeListener(ctm);
643         entry_group.addEntryChangeListener(ctm);
644         ctm.setEntryGroup(entry_group);
645 
646         transfer(entry.getAllFeatures(), qualifiersToTransfer, key, sameKey,
647             overwrite, true, geneNames, genesUpdated);
648 
649         for(int j=0; j<ctm.getTransactionCount(); j++)
650           buff.append(ctm.getTransactionAt(j).getLogComment()+"\n");
651         ChadoTransactionManager.commit((DatabaseDocument) newDbEntry
652             .getDocument(), false, ctm);
653 
654         entry_group.removeFeatureChangeListener(ctm);
655         entry_group.removeEntryChangeListener(ctm);
656         // if(newDbEntry != null)
657         // GeneEdit.showGeneEditor(null, geneNames[i], newDbEntry);
658       }
659     }
660 
661   	if(genesNotFound != null)
662   		JOptionPane.showMessageDialog(null,
663   				"Gene(s) Not Found:\n"+genesNotFound.toString(),
664   				"Gene(s) Not Found", JOptionPane.WARNING_MESSAGE);
665   	return 0;
666   }
667 
668   /**
669    *
670    * @param features
671    * @param qualifiersToTransfer
672    * @param key
673    * @param sameKey
674    * @param isDatabaseEntry
675    * @param geneNames
676    * @return
677    */
transfer(final FeatureVector features, final QualifierVector qualifiersToTransfer, final String key, final boolean sameKey, final boolean overwrite, final boolean isDatabaseEntry, String[] geneNames, final StringBuffer genesUpdated)678   private static String[] transfer(final FeatureVector features,
679                             final QualifierVector qualifiersToTransfer,
680                             final String key,
681                             final boolean sameKey,
682                             final boolean overwrite,
683                             final boolean isDatabaseEntry,
684                             String[] geneNames,
685                             final StringBuffer genesUpdated)
686   {
687     final TransferFeaturePredicate predicate = new TransferFeaturePredicate(
688         key, sameKey, isDatabaseEntry, geneNames);
689 
690     for (int i = 0; i < features.size(); i++)
691     {
692       Feature thisFeature = features.elementAt(i);
693       if (predicate.testPredicate(thisFeature))
694       {
695         StringBuffer qualifierBuffer = new StringBuffer();
696         for (int j = 0; j < qualifiersToTransfer.size(); j++)
697         {
698           Qualifier newQualifier = qualifiersToTransfer.elementAt(j);
699           String qualifierName = newQualifier.getName();
700           try
701           {
702             if(overwrite)
703             {
704               thisFeature.setQualifier(newQualifier);
705               qualifierBuffer.append("  "+qualifierName+" (overwritten)\n"+
706                   parseStringVector(newQualifier.getValues()));
707             }
708             else
709             {
710               final StringVector oldValues;
711               if (thisFeature.getQualifierByName(newQualifier.getName()) == null)
712                 oldValues = null;
713               else
714                 oldValues = thisFeature.getQualifierByName(
715                     newQualifier.getName()).getValues();
716 
717               final Qualifier newQualifierTmp = getQualifierWithoutDuplicateValues(
718                   newQualifier, oldValues);
719               if (newQualifierTmp == null)
720                 continue;
721               thisFeature.addQualifierValues(newQualifierTmp);
722               qualifierBuffer.append("  "+qualifierName+" (added)\n"+
723                   parseStringVector(newQualifier.getValues()));
724             }
725           }
726           catch (Exception e1)
727           {
728             e1.printStackTrace();
729           }
730         }
731 
732         geneNames = removeArrayElement(geneNames, predicate.getGeneName());
733         if(qualifierBuffer.length() > 0)
734           genesUpdated.append(thisFeature.getSystematicName()+
735                               " ("+key+")\n"+qualifierBuffer);
736       }
737     }
738     return geneNames;
739   }
740 
741   /**
742    * Get a StringBuffer representation of the values in a StringVector
743    * @param v
744    * @return
745    */
parseStringVector(final StringVector v)746   private static StringBuffer parseStringVector(final StringVector v)
747   {
748     StringBuffer buff = new StringBuffer();
749     for(int i=0; i<v.size(); i++)
750       buff.append("    "+v.elementAt(i)+"\n");
751     return buff;
752   }
753 
754   /**
755    * Return a qualifier copy of the qualifier provided that does not contain
756    * any of the values in the StringVector.
757    * @param newQualifier
758    * @param oldValues
759    * @return
760    * @throws InvalidRelationException
761    */
getQualifierWithoutDuplicateValues( final Qualifier qualifier, final StringVector values)762   protected static Qualifier getQualifierWithoutDuplicateValues(
763       final Qualifier qualifier,
764       final StringVector values) throws InvalidRelationException
765   {
766     final Qualifier newQualifier;
767     if (values == null || values.size() < 1)
768       newQualifier = qualifier;
769     else
770     {
771       StringVector newValues =  qualifier.getValues();
772       StringVector valuesToAdd = new StringVector();
773 
774       for (int k = 0; k < newValues.size(); k++)
775       {
776         if(!values.contains(newValues.get(k)))
777         {
778           if(qualifier.getName().equals("history"))
779           {
780         	if(!uk.ac.sanger.artemis.components.genebuilder.cv.HistoryBox.contains(values, (String)newValues.get(k)))
781         	  valuesToAdd.add(newValues.get(k));
782           }
783           else
784             valuesToAdd.add(newValues.get(k));
785         }
786       }
787 
788       if(valuesToAdd.size() == 0)
789         return null;
790       newQualifier = new Qualifier(qualifier.getName(), valuesToAdd);
791     }
792     return newQualifier;
793   }
794 
795   /**
796    * Remove a string from an array of strings. If the string appears multiple
797    * times in the array this method will delete all occurrences.
798    * @param strArr
799    * @param str
800    * @return
801    */
removeArrayElement(final String strArr[], final String str)802   private static String[] removeArrayElement(final String strArr[], final String str)
803   {
804     if(strArr.length == 1)
805     {
806       if(strArr[0].equals(str))
807         return null;
808       return strArr;
809     }
810     String[] newarray = new String[strArr.length - 1];
811     int count = 0;
812     for (int i = 0; i < strArr.length; i++)
813     {
814       if (strArr[i].equals(str))
815         continue;
816 
817       // not found str return original array
818       if (count >= newarray.length)
819         return strArr;
820       newarray[count] = strArr[i];
821       count++;
822     }
823 
824     if (count < newarray.length)
825     {
826       String[] tmparray = new String[count];
827       System.arraycopy(newarray, 0, tmparray, 0, count);
828       newarray = tmparray;
829     }
830     return newarray;
831   }
832 
833   /**
834    * Optionally transfer GO fields with evidence code ISO and link back to
835    * the original source in the WITH/FROM column.
836    * @param setEvidenceAndWithFrom
837    * @param feature
838    * @param qName
839    * @param values
840    * @return
841    */
getTransferValues(final boolean setEvidenceAndWithFrom, final Feature feature, final String qName, final StringVector values)842   private static StringVector getTransferValues(final boolean setEvidenceAndWithFrom,
843                                                 final Feature feature,
844                                                 final String qName,
845                                                 final StringVector values)
846   {
847     if(!setEvidenceAndWithFrom)
848       return values;
849 
850     if(qName.equals("GO") || qName.equals("product"))
851     {
852       final StringVector tvalues = new StringVector();
853       final String gene = getGeneName(feature);
854       for (int i = 0; i < values.size(); i++)
855       {
856         String val =  changeField("evidence=",
857               "Inferred from Sequence Orthology",
858               null, values.get(i));
859 
860         if(gene != null)
861           val =  changeField("with=",
862               "GeneDB:"+gene, "|", val);
863         tvalues.add(val);
864       }
865       return tvalues;
866     }
867     return values;
868   }
869 
getGeneName(Feature feature)870   private static String getGeneName(Feature feature)
871   {
872     try
873     {
874        return
875         ((GFFStreamFeature)feature.getEmblFeature()).getChadoGene().getGeneUniqueName();
876     }
877     catch(Exception e){}
878     return null;
879   }
880 
881   /**
882    * Replace or add the value of a field in a qualifier string
883    * @param fieldName
884    * @param newFieldStr
885    * @param separator
886    * @param qualStr
887    * @return
888    */
changeField(final String fieldName, final String newFieldStr, final String separator, String qualStr)889   private static String changeField(final String fieldName,
890                              final String newFieldStr,
891                              final String separator,
892                              String qualStr)
893   {
894     int idx1 = qualStr.toLowerCase().indexOf(fieldName.toLowerCase());
895     int idx2 = qualStr.indexOf(";", idx1);
896     int len  = fieldName.length();
897     if(idx2 > idx1 && idx1 > -1)
898     {
899       if(separator != null)
900         qualStr = qualStr.substring(0, idx2) + separator + newFieldStr +
901                   qualStr.substring(idx2);
902       else
903         qualStr = qualStr.substring(0, idx1+len) + newFieldStr +
904                   qualStr.substring(idx2);
905     }
906     else if(idx1 > -1)
907     {
908       if(separator != null)
909         qualStr = qualStr + separator + newFieldStr;
910       else
911         qualStr = qualStr.substring(0, idx1+len) + newFieldStr;
912     }
913     else if(!newFieldStr.equals(""))
914         qualStr = qualStr + ";" +
915                   fieldName + newFieldStr;
916     return qualStr;
917   }
918 }
919 
920 class QualifierPanel extends JPanel
921 {
922   private static final long serialVersionUID = 1L;
923   private Hashtable<JCheckBox, Vector<JCheckBox>> qualifierCheckBoxes = new Hashtable<JCheckBox, Vector<JCheckBox>>();
924   private Feature feature;
925   protected int nrows = 0;
926 
QualifierPanel(Feature feature, String title)927   public QualifierPanel(Feature feature, String title)
928   {
929     super(new GridBagLayout());
930 
931     this.feature = feature;
932 
933     TitledBorder titleBorder = BorderFactory.createTitledBorder(
934         BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), title);
935     titleBorder.setTitleJustification(TitledBorder.LEFT);
936     titleBorder.setTitleColor(TransferAnnotationTool.STEEL_BLUE);
937     setBorder(titleBorder);
938 
939     GridBagConstraints c = new GridBagConstraints();
940     c.anchor = GridBagConstraints.WEST;
941     c.ipadx = 0;
942     final QualifierVector qualifiers = feature.getQualifiers();
943 
944     for(int i = 0; i < qualifiers.size(); i++)
945     {
946       nrows =
947         addQualifierComponents(qualifiers.get(i),
948                               qualifierCheckBoxes, c, nrows);
949     }
950 
951     setMinimumSize(new Dimension(
952         titleBorder.getMinimumSize(this).width,
953                    getMinimumSize().height));
954   }
955 
956   /**
957    * Add a qualifier to the list of transferable annotation
958    * @param qualifier
959    * @param qualifierCheckBoxes
960    * @param c
961    * @param nrows
962    * @return
963    */
addQualifierComponents( final Qualifier qualifier, final Hashtable<JCheckBox, Vector<JCheckBox>> qualifierCheckBoxes, final GridBagConstraints c, int nrows)964   private int addQualifierComponents(
965       final Qualifier qualifier,
966       final Hashtable<JCheckBox, Vector<JCheckBox>> qualifierCheckBoxes,
967       final GridBagConstraints c,
968       int nrows)
969   {
970     if(TransferAnnotationTool.isNonTransferable(qualifier.getName()))
971       return nrows;
972 
973     final JCheckBox qualifierNameCheckBox = new JCheckBox(qualifier.getName(), false);
974     c.gridx = 1;
975     c.fill = GridBagConstraints.NONE;
976     c.weightx = 0.d;
977     Box qualifierValueBox = Box.createVerticalBox();
978 
979     JButton detailsShowHide = new JButton("+");
980     final Vector<JCheckBox> qualifierValuesCheckBox = setExpanderButton(detailsShowHide,
981         qualifier, qualifierValueBox, qualifierNameCheckBox);
982 
983     qualifierNameCheckBox.addItemListener(new ItemListener()
984     {
985       public void itemStateChanged(ItemEvent e)
986       {
987         if(qualifierNameCheckBox.isSelected())
988         {
989           for(int i=0; i<qualifierValuesCheckBox.size(); i++)
990           {
991             JCheckBox cb = qualifierValuesCheckBox.get(i);
992             if(cb.isSelected())
993               return;
994           }
995         }
996 
997         for(int i=0; i<qualifierValuesCheckBox.size(); i++)
998         {
999           JCheckBox cb = qualifierValuesCheckBox.get(i);
1000           cb.setSelected(qualifierNameCheckBox.isSelected());
1001         }
1002       }
1003     });
1004     add(detailsShowHide, c);
1005 
1006     c.fill = GridBagConstraints.HORIZONTAL;
1007     c.weightx = 100;
1008     c.gridx = 2;
1009 
1010     add(qualifierNameCheckBox, c);
1011     qualifierCheckBoxes.put(qualifierNameCheckBox, qualifierValuesCheckBox);
1012     c.gridy = ++nrows;
1013     add(qualifierValueBox, c);
1014     c.gridy = ++nrows;
1015     return nrows;
1016   }
1017 
1018   /**
1019    * Set up the expander button to display qualifier values.
1020    * @param butt - expander button
1021    * @param qualifier - the qualifer that is being displayed
1022    * @param qualifierValueBox - Box containing the values
1023    * @param qualifierNameCheckBox - JCheckBox for the given qualifier
1024    * @param pane
1025    * @return
1026    */
setExpanderButton(final JButton butt, final Qualifier qualifier, final Box qualifierValueBox, final JCheckBox qualifierNameCheckBox)1027   private Vector<JCheckBox> setExpanderButton(final JButton butt,
1028                                    final Qualifier qualifier,
1029                                    final Box qualifierValueBox,
1030                                    final JCheckBox qualifierNameCheckBox)
1031   {
1032     butt.setMargin(new Insets(0, 0, 0, 0));
1033     butt.setHorizontalAlignment(SwingConstants.RIGHT);
1034     butt.setHorizontalTextPosition(SwingConstants.RIGHT);
1035     butt.setBorderPainted(false);
1036     butt.setFont(butt.getFont().deriveFont(Font.BOLD));
1037     butt.setForeground(TransferAnnotationTool.STEEL_BLUE);
1038 
1039     butt.addActionListener(new ActionListener()
1040     {
1041       public void actionPerformed(ActionEvent e)
1042       {
1043         if (butt.getText().equals("+"))
1044           butt.setText("-");
1045         else
1046           butt.setText("+");
1047 
1048         qualifierValueBox.setVisible(butt.getText().equals("-"));
1049         revalidate();
1050       }
1051     });
1052 
1053     // set-up qualifier values list
1054     qualifierValueBox.setVisible(false);
1055     final Vector<JCheckBox> qualifierValuesCheckBox = new Vector<JCheckBox>();
1056     final StringVector values = qualifier.getValues();
1057     if(values != null)
1058     {
1059       for (int i = 0; i < values.size(); i++)
1060       {
1061         final JCheckBox cb = new JCheckBox(values.get(i),
1062           qualifierNameCheckBox.isSelected());
1063         cb.setFont(cb.getFont().deriveFont(Font.ITALIC));
1064         qualifierValueBox.add(cb);
1065         qualifierValuesCheckBox.add(cb);
1066       }
1067     }
1068     return qualifierValuesCheckBox;
1069   }
1070 
getQualifierCheckBoxes()1071   protected Hashtable<JCheckBox, Vector<JCheckBox>> getQualifierCheckBoxes()
1072   {
1073     return qualifierCheckBoxes;
1074   }
1075 
getFeature()1076   protected Feature getFeature()
1077   {
1078     return feature;
1079   }
1080 }
1081 
1082 /**
1083  * Test if the feature is nominated to have annotation transferred
1084  * to it. For genes in a chado database it looks at the gene name
1085  * and transcript name.
1086  */
1087 class TransferFeaturePredicate implements FeaturePredicate
1088 {
1089   private String geneName;
1090   private String key;
1091   private boolean sameKey;
1092   private boolean isDatabaseEntry;
1093   private String[] geneNames;
1094 
TransferFeaturePredicate(final String key, final boolean sameKey, final boolean isDatabaseEntry, final String[] geneNames)1095   public TransferFeaturePredicate(final String key,
1096                                   final boolean sameKey,
1097                                   final boolean isDatabaseEntry,
1098                                   final String[] geneNames)
1099   {
1100     this.key = key;
1101     this.sameKey = sameKey;
1102     this.isDatabaseEntry = isDatabaseEntry;
1103     this.geneNames = geneNames;
1104   }
1105 
testPredicate(Feature targetFeature)1106   public boolean testPredicate(Feature targetFeature)
1107   {
1108     String targetKey = targetFeature.getKey().getKeyString();
1109     if (sameKey && !targetKey.equals(key))
1110       return false;
1111 
1112     Vector<String> chadoNames = null;
1113     if (isDatabaseEntry)
1114     {
1115       GFFStreamFeature gffFeature =
1116         ((GFFStreamFeature) targetFeature.getEmblFeature());
1117       if (gffFeature.getChadoGene() != null)
1118       {
1119         chadoNames = new Vector<String>();
1120 
1121         ChadoCanonicalGene chadoGene = gffFeature.getChadoGene();
1122         chadoNames.add(chadoGene.getGeneUniqueName());
1123         List<uk.ac.sanger.artemis.io.Feature> transcripts = chadoGene.getTranscripts();
1124         for(int i=0;i<transcripts.size();i++)
1125         {
1126           GFFStreamFeature feature = (GFFStreamFeature) transcripts.get(i);
1127           chadoNames.add(GeneUtils.getUniqueName(feature));
1128         }
1129       }
1130     }
1131 
1132     String thisFeatureSystematicName = targetFeature.getSystematicName();
1133     for (int i = 0; i < geneNames.length; i++)
1134     {
1135       if(geneNames[i].equals(thisFeatureSystematicName) ||
1136          (chadoNames != null && chadoNames.contains(geneNames[i])))
1137       {
1138         geneName = geneNames[i];
1139         return true;
1140       }
1141     }
1142     return false;
1143   }
1144 
getGeneName()1145   protected String getGeneName()
1146   {
1147     return geneName;
1148   }
1149 }
1150