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