JAL-2403 constants for "NJ", "AV"
[jalview.git] / src / jalview / gui / TreePanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.analysis.AlignmentSorter;
24 import jalview.analysis.NJTree;
25 import jalview.analysis.scoremodels.ScoreModels;
26 import jalview.api.analysis.ScoreModelI;
27 import jalview.api.analysis.ViewBasedAnalysisI;
28 import jalview.bin.Cache;
29 import jalview.commands.CommandI;
30 import jalview.commands.OrderCommand;
31 import jalview.datamodel.Alignment;
32 import jalview.datamodel.AlignmentI;
33 import jalview.datamodel.AlignmentView;
34 import jalview.datamodel.BinaryNode;
35 import jalview.datamodel.ColumnSelection;
36 import jalview.datamodel.DBRefEntry;
37 import jalview.datamodel.NodeTransformI;
38 import jalview.datamodel.SequenceFeature;
39 import jalview.datamodel.SequenceI;
40 import jalview.datamodel.SequenceNode;
41 import jalview.io.JalviewFileChooser;
42 import jalview.io.JalviewFileView;
43 import jalview.io.NewickFile;
44 import jalview.jbgui.GTreePanel;
45 import jalview.util.ImageMaker;
46 import jalview.util.MessageManager;
47 import jalview.viewmodel.AlignmentViewport;
48
49 import java.awt.Font;
50 import java.awt.Graphics;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.awt.image.BufferedImage;
54 import java.beans.PropertyChangeEvent;
55 import java.io.FileOutputStream;
56 import java.util.ArrayList;
57 import java.util.List;
58
59 import javax.imageio.ImageIO;
60 import javax.swing.ButtonGroup;
61 import javax.swing.JMenuItem;
62 import javax.swing.JRadioButtonMenuItem;
63
64 import org.jibble.epsgraphics.EpsGraphics2D;
65
66 /**
67  * DOCUMENT ME!
68  * 
69  * @author $author$
70  * @version $Revision$
71  */
72 public class TreePanel extends GTreePanel
73 {
74   String type;
75
76   String pwtype;
77
78   TreeCanvas treeCanvas;
79
80   NJTree tree;
81
82   AlignViewport av;
83
84   /**
85    * Creates a new TreePanel object.
86    * 
87    * @param av
88    *          DOCUMENT ME!
89    * @param seqVector
90    *          DOCUMENT ME!
91    * @param type
92    *          DOCUMENT ME!
93    * @param pwtype
94    *          DOCUMENT ME!
95    * @param s
96    *          DOCUMENT ME!
97    * @param e
98    *          DOCUMENT ME!
99    */
100   public TreePanel(AlignmentPanel ap, String type, String pwtype)
101   {
102     super();
103     initTreePanel(ap, type, pwtype, null, null);
104
105     // We know this tree has distances. JBPNote TODO: prolly should add this as
106     // a userdefined default
107     // showDistances(true);
108   }
109
110   /**
111    * Creates a new TreePanel object.
112    * 
113    * @param av
114    *          DOCUMENT ME!
115    * @param seqVector
116    *          DOCUMENT ME!
117    * @param newtree
118    *          DOCUMENT ME!
119    * @param type
120    *          DOCUMENT ME!
121    * @param pwtype
122    *          DOCUMENT ME!
123    */
124   public TreePanel(AlignmentPanel ap, String type, String pwtype,
125           NewickFile newtree)
126   {
127     super();
128     initTreePanel(ap, type, pwtype, newtree, null);
129   }
130
131   public TreePanel(AlignmentPanel av, String type, String pwtype,
132           NewickFile newtree, AlignmentView inputData)
133   {
134     super();
135     initTreePanel(av, type, pwtype, newtree, inputData);
136   }
137
138   public AlignmentI getAlignment()
139   {
140     return treeCanvas.av.getAlignment();
141   }
142
143   public AlignmentViewport getViewPort()
144   {
145     return treeCanvas.av;
146   }
147
148   void initTreePanel(AlignmentPanel ap, String type, String pwtype,
149           NewickFile newTree, AlignmentView inputData)
150   {
151
152     av = ap.av;
153     this.type = type;
154     this.pwtype = pwtype;
155
156     treeCanvas = new TreeCanvas(this, ap, scrollPane);
157     scrollPane.setViewportView(treeCanvas);
158
159     PaintRefresher.Register(this, ap.av.getSequenceSetId());
160
161     buildAssociatedViewMenu();
162
163     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
164     {
165       @Override
166       public void propertyChange(PropertyChangeEvent evt)
167       {
168         if (evt.getPropertyName().equals("alignment"))
169         {
170           if (tree == null)
171           {
172             System.out.println("tree is null");
173             // TODO: deal with case when a change event is received whilst a
174             // tree is still being calculated - should save reference for
175             // processing message later.
176             return;
177           }
178           if (evt.getNewValue() == null)
179           {
180             System.out
181                     .println("new alignment sequences vector value is null");
182           }
183
184           tree.UpdatePlaceHolders((List<SequenceI>) evt.getNewValue());
185           treeCanvas.nameHash.clear(); // reset the mapping between canvas
186           // rectangles and leafnodes
187           repaint();
188         }
189       }
190     });
191
192     TreeLoader tl = new TreeLoader(newTree);
193     if (inputData != null)
194     {
195       tl.odata = inputData;
196     }
197     tl.start();
198
199   }
200
201   @Override
202   public void viewMenu_menuSelected()
203   {
204     buildAssociatedViewMenu();
205   }
206
207   void buildAssociatedViewMenu()
208   {
209     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av
210             .getSequenceSetId());
211     if (aps.length == 1 && treeCanvas.ap == aps[0])
212     {
213       associateLeavesMenu.setVisible(false);
214       return;
215     }
216
217     associateLeavesMenu.setVisible(true);
218
219     if ((viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
220     {
221       viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
222     }
223
224     associateLeavesMenu.removeAll();
225
226     JRadioButtonMenuItem item;
227     ButtonGroup buttonGroup = new ButtonGroup();
228     int i, iSize = aps.length;
229     final TreePanel thisTreePanel = this;
230     for (i = 0; i < iSize; i++)
231     {
232       final AlignmentPanel ap = aps[i];
233       item = new JRadioButtonMenuItem(ap.av.viewName, ap == treeCanvas.ap);
234       buttonGroup.add(item);
235       item.addActionListener(new ActionListener()
236       {
237         @Override
238         public void actionPerformed(ActionEvent evt)
239         {
240           treeCanvas.applyToAllViews = false;
241           treeCanvas.ap = ap;
242           treeCanvas.av = ap.av;
243           PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
244         }
245       });
246
247       associateLeavesMenu.add(item);
248     }
249
250     final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
251             MessageManager.getString("label.all_views"));
252     buttonGroup.add(itemf);
253     itemf.setSelected(treeCanvas.applyToAllViews);
254     itemf.addActionListener(new ActionListener()
255     {
256       @Override
257       public void actionPerformed(ActionEvent evt)
258       {
259         treeCanvas.applyToAllViews = itemf.isSelected();
260       }
261     });
262     associateLeavesMenu.add(itemf);
263
264   }
265
266   class TreeLoader extends Thread
267   {
268     NewickFile newtree;
269
270     jalview.datamodel.AlignmentView odata = null;
271
272     public TreeLoader(NewickFile newtree)
273     {
274       this.newtree = newtree;
275       if (newtree != null)
276       {
277         // Must be outside run(), as Jalview2XML tries to
278         // update distance/bootstrap visibility at the same time
279         showBootstrap(newtree.HasBootstrap());
280         showDistances(newtree.HasDistances());
281       }
282     }
283
284     @Override
285     public void run()
286     {
287
288       if (newtree != null)
289       {
290         if (odata == null)
291         {
292           tree = new NJTree(av.getAlignment().getSequencesArray(), newtree);
293         }
294         else
295         {
296           tree = new NJTree(av.getAlignment().getSequencesArray(), odata,
297                   newtree);
298         }
299         if (!tree.hasOriginalSequenceData())
300         {
301           allowOriginalSeqData(false);
302         }
303       }
304       else
305       {
306         int start, end;
307         SequenceI[] seqs;
308         boolean selview = av.getSelectionGroup() != null
309                 && av.getSelectionGroup().getSize() > 1;
310         AlignmentView seqStrings = av.getAlignmentView(selview);
311         if (!selview)
312         {
313           start = 0;
314           end = av.getAlignment().getWidth();
315           seqs = av.getAlignment().getSequencesArray();
316         }
317         else
318         {
319           start = av.getSelectionGroup().getStartRes();
320           end = av.getSelectionGroup().getEndRes() + 1;
321           seqs = av.getSelectionGroup().getSequencesInOrder(
322                   av.getAlignment());
323         }
324         ScoreModelI sm = ScoreModels.getInstance().forName(pwtype);
325         if (sm instanceof ViewBasedAnalysisI)
326         {
327           try
328           {
329             sm = sm.getClass().newInstance();
330             ((ViewBasedAnalysisI) sm)
331                     .configureFromAlignmentView(treeCanvas.ap);
332           } catch (Exception q)
333           {
334             Cache.log.error("Couldn't create a scoremodel instance for "
335                     + sm.getName());
336           }
337           tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end);
338         }
339         else
340         {
341           tree = new NJTree(seqs, seqStrings, type, pwtype, null, start,
342                   end);
343         }
344         showDistances(true);
345       }
346
347       tree.reCount(tree.getTopNode());
348       tree.findHeight(tree.getTopNode());
349       treeCanvas.setTree(tree);
350       treeCanvas.repaint();
351       av.setCurrentTree(tree);
352       if (av.getSortByTree())
353       {
354         sortByTree_actionPerformed();
355       }
356     }
357   }
358
359   public void showDistances(boolean b)
360   {
361     treeCanvas.setShowDistances(b);
362     distanceMenu.setSelected(b);
363   }
364
365   public void showBootstrap(boolean b)
366   {
367     treeCanvas.setShowBootstrap(b);
368     bootstrapMenu.setSelected(b);
369   }
370
371   public void showPlaceholders(boolean b)
372   {
373     placeholdersMenu.setState(b);
374     treeCanvas.setMarkPlaceholders(b);
375   }
376
377   private void allowOriginalSeqData(boolean b)
378   {
379     originalSeqData.setVisible(b);
380   }
381
382   /**
383    * DOCUMENT ME!
384    * 
385    * @return DOCUMENT ME!
386    */
387   public NJTree getTree()
388   {
389     return tree;
390   }
391
392   /**
393    * DOCUMENT ME!
394    * 
395    * @param e
396    *          DOCUMENT ME!
397    */
398   @Override
399   public void textbox_actionPerformed(ActionEvent e)
400   {
401     CutAndPasteTransfer cap = new CutAndPasteTransfer();
402
403     StringBuffer buffer = new StringBuffer();
404
405     if (type.equals(NJTree.AVERAGE_DISTANCE))
406     {
407       buffer.append("Average distance tree using ");
408     }
409     else
410     {
411       buffer.append("Neighbour joining tree using ");
412     }
413
414     ScoreModelI sm = ScoreModels.getInstance().forName(pwtype);
415     buffer.append(sm.getName());
416
417     NewickFile fout = new NewickFile(tree.getTopNode());
418     try
419     {
420       cap.setText(fout.print(tree.isHasBootstrap(), tree.isHasDistances(),
421               tree.isHasRootDistance()));
422       Desktop.addInternalFrame(cap, buffer.toString(), 500, 100);
423     } catch (OutOfMemoryError oom)
424     {
425       new OOMWarning("generating newick tree file", oom);
426       cap.dispose();
427     }
428
429   }
430
431   /**
432    * DOCUMENT ME!
433    * 
434    * @param e
435    *          DOCUMENT ME!
436    */
437   @Override
438   public void saveAsNewick_actionPerformed(ActionEvent e)
439   {
440     JalviewFileChooser chooser = new JalviewFileChooser(
441             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
442     chooser.setFileView(new JalviewFileView());
443     chooser.setDialogTitle(MessageManager
444             .getString("label.save_tree_as_newick"));
445     chooser.setToolTipText(MessageManager.getString("action.save"));
446
447     int value = chooser.showSaveDialog(null);
448
449     if (value == JalviewFileChooser.APPROVE_OPTION)
450     {
451       String choice = chooser.getSelectedFile().getPath();
452       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
453               .getSelectedFile().getParent());
454
455       try
456       {
457         jalview.io.NewickFile fout = new jalview.io.NewickFile(
458                 tree.getTopNode());
459         String output = fout.print(tree.isHasBootstrap(),
460                 tree.isHasDistances(), tree.isHasRootDistance());
461         java.io.PrintWriter out = new java.io.PrintWriter(
462                 new java.io.FileWriter(choice));
463         out.println(output);
464         out.close();
465       } catch (Exception ex)
466       {
467         ex.printStackTrace();
468       }
469     }
470   }
471
472   /**
473    * DOCUMENT ME!
474    * 
475    * @param e
476    *          DOCUMENT ME!
477    */
478   @Override
479   public void printMenu_actionPerformed(ActionEvent e)
480   {
481     // Putting in a thread avoids Swing painting problems
482     treeCanvas.startPrinting();
483   }
484
485   @Override
486   public void originalSeqData_actionPerformed(ActionEvent e)
487   {
488     if (!tree.hasOriginalSequenceData())
489     {
490       jalview.bin.Cache.log
491               .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
492       return;
493     }
494     // decide if av alignment is sufficiently different to original data to
495     // warrant a new window to be created
496     // create new alignmnt window with hidden regions (unhiding hidden regions
497     // yields unaligned seqs)
498     // or create a selection box around columns in alignment view
499     // test Alignment(SeqCigar[])
500     char gc = '-';
501     try
502     {
503       // we try to get the associated view's gap character
504       // but this may fail if the view was closed...
505       gc = av.getGapCharacter();
506
507     } catch (Exception ex)
508     {
509     }
510     ;
511     Object[] alAndColsel = tree.seqData.getAlignmentAndColumnSelection(gc);
512
513     if (alAndColsel != null && alAndColsel[0] != null)
514     {
515       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
516
517       AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
518       AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
519               .getAlignment().getDataset() : null;
520       if (dataset != null)
521       {
522         al.setDataset(dataset);
523       }
524
525       if (true)
526       {
527         // make a new frame!
528         AlignFrame af = new AlignFrame(al,
529                 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
530                 AlignFrame.DEFAULT_HEIGHT);
531
532         // >>>This is a fix for the moment, until a better solution is
533         // found!!<<<
534         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
535
536         // af.addSortByOrderMenuItem(ServiceName + " Ordering",
537         // msaorder);
538
539         Desktop.addInternalFrame(af, MessageManager.formatMessage(
540                 "label.original_data_for_params",
541                 new Object[] { this.title }), AlignFrame.DEFAULT_WIDTH,
542                 AlignFrame.DEFAULT_HEIGHT);
543       }
544     }
545   }
546
547   /**
548    * DOCUMENT ME!
549    * 
550    * @param e
551    *          DOCUMENT ME!
552    */
553   @Override
554   public void fitToWindow_actionPerformed(ActionEvent e)
555   {
556     treeCanvas.fitToWindow = fitToWindow.isSelected();
557     repaint();
558   }
559
560   /**
561    * sort the associated alignment view by the current tree.
562    * 
563    * @param e
564    */
565   @Override
566   public void sortByTree_actionPerformed()
567   {
568
569     if (treeCanvas.applyToAllViews)
570     {
571       final ArrayList<CommandI> commands = new ArrayList<CommandI>();
572       for (AlignmentPanel ap : PaintRefresher.getAssociatedPanels(av
573               .getSequenceSetId()))
574       {
575         commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
576       }
577       av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
578       {
579
580         @Override
581         public void undoCommand(AlignmentI[] views)
582         {
583           for (CommandI tsort : commands)
584           {
585             tsort.undoCommand(views);
586           }
587         }
588
589         @Override
590         public int getSize()
591         {
592           return commands.size();
593         }
594
595         @Override
596         public String getDescription()
597         {
598           return "Tree Sort (many views)";
599         }
600
601         @Override
602         public void doCommand(AlignmentI[] views)
603         {
604
605           for (CommandI tsort : commands)
606           {
607             tsort.doCommand(views);
608           }
609         }
610       });
611       for (AlignmentPanel ap : PaintRefresher.getAssociatedPanels(av
612               .getSequenceSetId()))
613       {
614         // ensure all the alignFrames refresh their GI after adding an undo item
615         ap.alignFrame.updateEditMenuBar();
616       }
617     }
618     else
619     {
620       treeCanvas.ap.alignFrame
621               .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
622     }
623
624   }
625
626   public CommandI sortAlignmentIn(AlignmentPanel ap)
627   {
628     AlignmentViewport av = ap.av;
629     SequenceI[] oldOrder = av.getAlignment().getSequencesArray();
630     AlignmentSorter.sortByTree(av.getAlignment(), tree);
631     CommandI undo;
632     undo = new OrderCommand("Tree Sort", oldOrder, av.getAlignment());
633
634     ap.paintAlignment(true);
635     return undo;
636   }
637
638   /**
639    * DOCUMENT ME!
640    * 
641    * @param e
642    *          DOCUMENT ME!
643    */
644   @Override
645   public void font_actionPerformed(ActionEvent e)
646   {
647     if (treeCanvas == null)
648     {
649       return;
650     }
651
652     new FontChooser(this);
653   }
654
655   public Font getTreeFont()
656   {
657     return treeCanvas.font;
658   }
659
660   public void setTreeFont(Font font)
661   {
662     if (treeCanvas != null)
663     {
664       treeCanvas.setFont(font);
665     }
666   }
667
668   /**
669    * DOCUMENT ME!
670    * 
671    * @param e
672    *          DOCUMENT ME!
673    */
674   @Override
675   public void distanceMenu_actionPerformed(ActionEvent e)
676   {
677     treeCanvas.setShowDistances(distanceMenu.isSelected());
678   }
679
680   /**
681    * DOCUMENT ME!
682    * 
683    * @param e
684    *          DOCUMENT ME!
685    */
686   @Override
687   public void bootstrapMenu_actionPerformed(ActionEvent e)
688   {
689     treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
690   }
691
692   /**
693    * DOCUMENT ME!
694    * 
695    * @param e
696    *          DOCUMENT ME!
697    */
698   @Override
699   public void placeholdersMenu_actionPerformed(ActionEvent e)
700   {
701     treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
702   }
703
704   /**
705    * DOCUMENT ME!
706    * 
707    * @param e
708    *          DOCUMENT ME!
709    */
710   @Override
711   public void epsTree_actionPerformed(ActionEvent e)
712   {
713     boolean accurateText = true;
714
715     String renderStyle = jalview.bin.Cache.getDefault("EPS_RENDERING",
716             "Prompt each time");
717
718     // If we need to prompt, and if the GUI is visible then
719     // Prompt for EPS rendering style
720     if (renderStyle.equalsIgnoreCase("Prompt each time")
721             && !(System.getProperty("java.awt.headless") != null && System
722                     .getProperty("java.awt.headless").equals("true")))
723     {
724       EPSOptions eps = new EPSOptions();
725       renderStyle = eps.getValue();
726
727       if (renderStyle == null || eps.cancelled)
728       {
729         return;
730       }
731
732     }
733
734     if (renderStyle.equalsIgnoreCase("text"))
735     {
736       accurateText = false;
737     }
738
739     int width = treeCanvas.getWidth();
740     int height = treeCanvas.getHeight();
741
742     try
743     {
744       JalviewFileChooser chooser = new JalviewFileChooser(
745               ImageMaker.EPS_EXTENSION, ImageMaker.EPS_EXTENSION);
746       chooser.setFileView(new JalviewFileView());
747       chooser.setDialogTitle(MessageManager
748               .getString("label.create_eps_from_tree"));
749       chooser.setToolTipText(MessageManager.getString("action.save"));
750
751       int value = chooser.showSaveDialog(this);
752
753       if (value != JalviewFileChooser.APPROVE_OPTION)
754       {
755         return;
756       }
757
758       Cache.setProperty("LAST_DIRECTORY", chooser.getSelectedFile()
759               .getParent());
760
761       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
762       EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, height);
763
764       pg.setAccurateTextMode(accurateText);
765
766       treeCanvas.draw(pg, width, height);
767
768       pg.flush();
769       pg.close();
770     } catch (Exception ex)
771     {
772       ex.printStackTrace();
773     }
774   }
775
776   /**
777    * DOCUMENT ME!
778    * 
779    * @param e
780    *          DOCUMENT ME!
781    */
782   @Override
783   public void pngTree_actionPerformed(ActionEvent e)
784   {
785     int width = treeCanvas.getWidth();
786     int height = treeCanvas.getHeight();
787
788     try
789     {
790       JalviewFileChooser chooser = new JalviewFileChooser(
791               ImageMaker.PNG_EXTENSION, ImageMaker.PNG_DESCRIPTION);
792
793       chooser.setFileView(new jalview.io.JalviewFileView());
794       chooser.setDialogTitle(MessageManager
795               .getString("label.create_png_from_tree"));
796       chooser.setToolTipText(MessageManager.getString("action.save"));
797
798       int value = chooser.showSaveDialog(this);
799
800       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
801       {
802         return;
803       }
804
805       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
806               .getSelectedFile().getParent());
807
808       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
809
810       BufferedImage bi = new BufferedImage(width, height,
811               BufferedImage.TYPE_INT_RGB);
812       Graphics png = bi.getGraphics();
813
814       treeCanvas.draw(png, width, height);
815
816       ImageIO.write(bi, "png", out);
817       out.close();
818     } catch (Exception ex)
819     {
820       ex.printStackTrace();
821     }
822   }
823
824   /**
825    * change node labels to the annotation referred to by labelClass TODO:
826    * promote to a datamodel modification that can be undone TODO: make argument
827    * one case of a generic transformation function ie { undoStep = apply(Tree,
828    * TransformFunction)};
829    * 
830    * @param labelClass
831    */
832   public void changeNames(final String labelClass)
833   {
834     tree.applyToNodes(new NodeTransformI()
835     {
836
837       @Override
838       public void transform(BinaryNode node)
839       {
840         if (node instanceof SequenceNode
841                 && !((SequenceNode) node).isPlaceholder()
842                 && !((SequenceNode) node).isDummy())
843         {
844           String newname = null;
845           SequenceI sq = (SequenceI) ((SequenceNode) node).element();
846           if (sq != null)
847           {
848             // search dbrefs, features and annotation
849             DBRefEntry[] refs = jalview.util.DBRefUtils.selectRefs(
850                     sq.getDBRefs(),
851                     new String[] { labelClass.toUpperCase() });
852             if (refs != null)
853             {
854               for (int i = 0; i < refs.length; i++)
855               {
856                 if (newname == null)
857                 {
858                   newname = new String(refs[i].getAccessionId());
859                 }
860                 else
861                 {
862                   newname = newname + "; " + refs[i].getAccessionId();
863                 }
864               }
865             }
866             if (newname == null)
867             {
868               SequenceFeature sf[] = sq.getSequenceFeatures();
869               for (int i = 0; sf != null && i < sf.length; i++)
870               {
871                 if (sf[i].getType().equals(labelClass))
872                 {
873                   if (newname == null)
874                   {
875                     newname = new String(sf[i].getDescription());
876                   }
877                   else
878                   {
879                     newname = newname + "; " + sf[i].getDescription();
880                   }
881                 }
882               }
883             }
884           }
885           if (newname != null)
886           {
887             String oldname = ((SequenceNode) node).getName();
888             // TODO : save in the undo object for this modification.
889             ((SequenceNode) node).setName(newname);
890           }
891         }
892       }
893     });
894   }
895 }