JAL-2344 push Cache.getProperty("LAST_DIRECTORY") inside method
[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.api.analysis.ScoreModelI;
26 import jalview.api.analysis.ViewBasedAnalysisI;
27 import jalview.bin.Cache;
28 import jalview.commands.CommandI;
29 import jalview.commands.OrderCommand;
30 import jalview.datamodel.Alignment;
31 import jalview.datamodel.AlignmentI;
32 import jalview.datamodel.AlignmentView;
33 import jalview.datamodel.BinaryNode;
34 import jalview.datamodel.ColumnSelection;
35 import jalview.datamodel.DBRefEntry;
36 import jalview.datamodel.NodeTransformI;
37 import jalview.datamodel.SequenceFeature;
38 import jalview.datamodel.SequenceI;
39 import jalview.datamodel.SequenceNode;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.io.NewickFile;
43 import jalview.jbgui.GTreePanel;
44 import jalview.schemes.ResidueProperties;
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 = ResidueProperties.getScoreModel(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("AV"))
406     {
407       buffer.append("Average distance tree using ");
408     }
409     else
410     {
411       buffer.append("Neighbour joining tree using ");
412     }
413
414     if (pwtype.equals("BL"))
415     {
416       buffer.append("BLOSUM62");
417     }
418     else
419     {
420       buffer.append("PID");
421     }
422
423     jalview.io.NewickFile fout = new jalview.io.NewickFile(
424             tree.getTopNode());
425     try
426     {
427       cap.setText(fout.print(tree.isHasBootstrap(), tree.isHasDistances(),
428               tree.isHasRootDistance()));
429       Desktop.addInternalFrame(cap, buffer.toString(), 500, 100);
430     } catch (OutOfMemoryError oom)
431     {
432       new OOMWarning("generating newick tree file", oom);
433       cap.dispose();
434     }
435
436   }
437
438   /**
439    * DOCUMENT ME!
440    * 
441    * @param e
442    *          DOCUMENT ME!
443    */
444   @Override
445   public void saveAsNewick_actionPerformed(ActionEvent e)
446   {
447     JalviewFileChooser chooser = new JalviewFileChooser(
448             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
449     chooser.setFileView(new JalviewFileView());
450     chooser.setDialogTitle(MessageManager
451             .getString("label.save_tree_as_newick"));
452     chooser.setToolTipText(MessageManager.getString("action.save"));
453
454     int value = chooser.showSaveDialog(null);
455
456     if (value == JalviewFileChooser.APPROVE_OPTION)
457     {
458       String choice = chooser.getSelectedFile().getPath();
459       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
460               .getSelectedFile().getParent());
461
462       try
463       {
464         jalview.io.NewickFile fout = new jalview.io.NewickFile(
465                 tree.getTopNode());
466         String output = fout.print(tree.isHasBootstrap(),
467                 tree.isHasDistances(), tree.isHasRootDistance());
468         java.io.PrintWriter out = new java.io.PrintWriter(
469                 new java.io.FileWriter(choice));
470         out.println(output);
471         out.close();
472       } catch (Exception ex)
473       {
474         ex.printStackTrace();
475       }
476     }
477   }
478
479   /**
480    * DOCUMENT ME!
481    * 
482    * @param e
483    *          DOCUMENT ME!
484    */
485   @Override
486   public void printMenu_actionPerformed(ActionEvent e)
487   {
488     // Putting in a thread avoids Swing painting problems
489     treeCanvas.startPrinting();
490   }
491
492   @Override
493   public void originalSeqData_actionPerformed(ActionEvent e)
494   {
495     if (!tree.hasOriginalSequenceData())
496     {
497       jalview.bin.Cache.log
498               .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
499       return;
500     }
501     // decide if av alignment is sufficiently different to original data to
502     // warrant a new window to be created
503     // create new alignmnt window with hidden regions (unhiding hidden regions
504     // yields unaligned seqs)
505     // or create a selection box around columns in alignment view
506     // test Alignment(SeqCigar[])
507     char gc = '-';
508     try
509     {
510       // we try to get the associated view's gap character
511       // but this may fail if the view was closed...
512       gc = av.getGapCharacter();
513
514     } catch (Exception ex)
515     {
516     }
517     ;
518     Object[] alAndColsel = tree.seqData.getAlignmentAndColumnSelection(gc);
519
520     if (alAndColsel != null && alAndColsel[0] != null)
521     {
522       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
523
524       AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
525       AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
526               .getAlignment().getDataset() : null;
527       if (dataset != null)
528       {
529         al.setDataset(dataset);
530       }
531
532       if (true)
533       {
534         // make a new frame!
535         AlignFrame af = new AlignFrame(al,
536                 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
537                 AlignFrame.DEFAULT_HEIGHT);
538
539         // >>>This is a fix for the moment, until a better solution is
540         // found!!<<<
541         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
542
543         // af.addSortByOrderMenuItem(ServiceName + " Ordering",
544         // msaorder);
545
546         Desktop.addInternalFrame(af, MessageManager.formatMessage(
547                 "label.original_data_for_params",
548                 new Object[] { this.title }), AlignFrame.DEFAULT_WIDTH,
549                 AlignFrame.DEFAULT_HEIGHT);
550       }
551     }
552   }
553
554   /**
555    * DOCUMENT ME!
556    * 
557    * @param e
558    *          DOCUMENT ME!
559    */
560   @Override
561   public void fitToWindow_actionPerformed(ActionEvent e)
562   {
563     treeCanvas.fitToWindow = fitToWindow.isSelected();
564     repaint();
565   }
566
567   /**
568    * sort the associated alignment view by the current tree.
569    * 
570    * @param e
571    */
572   @Override
573   public void sortByTree_actionPerformed()
574   {
575
576     if (treeCanvas.applyToAllViews)
577     {
578       final ArrayList<CommandI> commands = new ArrayList<CommandI>();
579       for (AlignmentPanel ap : PaintRefresher.getAssociatedPanels(av
580               .getSequenceSetId()))
581       {
582         commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
583       }
584       av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
585       {
586
587         @Override
588         public void undoCommand(AlignmentI[] views)
589         {
590           for (CommandI tsort : commands)
591           {
592             tsort.undoCommand(views);
593           }
594         }
595
596         @Override
597         public int getSize()
598         {
599           return commands.size();
600         }
601
602         @Override
603         public String getDescription()
604         {
605           return "Tree Sort (many views)";
606         }
607
608         @Override
609         public void doCommand(AlignmentI[] views)
610         {
611
612           for (CommandI tsort : commands)
613           {
614             tsort.doCommand(views);
615           }
616         }
617       });
618       for (AlignmentPanel ap : PaintRefresher.getAssociatedPanels(av
619               .getSequenceSetId()))
620       {
621         // ensure all the alignFrames refresh their GI after adding an undo item
622         ap.alignFrame.updateEditMenuBar();
623       }
624     }
625     else
626     {
627       treeCanvas.ap.alignFrame
628               .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
629     }
630
631   }
632
633   public CommandI sortAlignmentIn(AlignmentPanel ap)
634   {
635     AlignmentViewport av = ap.av;
636     SequenceI[] oldOrder = av.getAlignment().getSequencesArray();
637     AlignmentSorter.sortByTree(av.getAlignment(), tree);
638     CommandI undo;
639     undo = new OrderCommand("Tree Sort", oldOrder, av.getAlignment());
640
641     ap.paintAlignment(true);
642     return undo;
643   }
644
645   /**
646    * DOCUMENT ME!
647    * 
648    * @param e
649    *          DOCUMENT ME!
650    */
651   @Override
652   public void font_actionPerformed(ActionEvent e)
653   {
654     if (treeCanvas == null)
655     {
656       return;
657     }
658
659     new FontChooser(this);
660   }
661
662   public Font getTreeFont()
663   {
664     return treeCanvas.font;
665   }
666
667   public void setTreeFont(Font font)
668   {
669     if (treeCanvas != null)
670     {
671       treeCanvas.setFont(font);
672     }
673   }
674
675   /**
676    * DOCUMENT ME!
677    * 
678    * @param e
679    *          DOCUMENT ME!
680    */
681   @Override
682   public void distanceMenu_actionPerformed(ActionEvent e)
683   {
684     treeCanvas.setShowDistances(distanceMenu.isSelected());
685   }
686
687   /**
688    * DOCUMENT ME!
689    * 
690    * @param e
691    *          DOCUMENT ME!
692    */
693   @Override
694   public void bootstrapMenu_actionPerformed(ActionEvent e)
695   {
696     treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
697   }
698
699   /**
700    * DOCUMENT ME!
701    * 
702    * @param e
703    *          DOCUMENT ME!
704    */
705   @Override
706   public void placeholdersMenu_actionPerformed(ActionEvent e)
707   {
708     treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
709   }
710
711   /**
712    * DOCUMENT ME!
713    * 
714    * @param e
715    *          DOCUMENT ME!
716    */
717   @Override
718   public void epsTree_actionPerformed(ActionEvent e)
719   {
720     boolean accurateText = true;
721
722     String renderStyle = jalview.bin.Cache.getDefault("EPS_RENDERING",
723             "Prompt each time");
724
725     // If we need to prompt, and if the GUI is visible then
726     // Prompt for EPS rendering style
727     if (renderStyle.equalsIgnoreCase("Prompt each time")
728             && !(System.getProperty("java.awt.headless") != null && System
729                     .getProperty("java.awt.headless").equals("true")))
730     {
731       EPSOptions eps = new EPSOptions();
732       renderStyle = eps.getValue();
733
734       if (renderStyle == null || eps.cancelled)
735       {
736         return;
737       }
738
739     }
740
741     if (renderStyle.equalsIgnoreCase("text"))
742     {
743       accurateText = false;
744     }
745
746     int width = treeCanvas.getWidth();
747     int height = treeCanvas.getHeight();
748
749     try
750     {
751       JalviewFileChooser chooser = new JalviewFileChooser(
752               ImageMaker.EPS_EXTENSION, ImageMaker.EPS_EXTENSION);
753       chooser.setFileView(new JalviewFileView());
754       chooser.setDialogTitle(MessageManager
755               .getString("label.create_eps_from_tree"));
756       chooser.setToolTipText(MessageManager.getString("action.save"));
757
758       int value = chooser.showSaveDialog(this);
759
760       if (value != JalviewFileChooser.APPROVE_OPTION)
761       {
762         return;
763       }
764
765       Cache.setProperty("LAST_DIRECTORY", chooser.getSelectedFile()
766               .getParent());
767
768       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
769       EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, height);
770
771       pg.setAccurateTextMode(accurateText);
772
773       treeCanvas.draw(pg, width, height);
774
775       pg.flush();
776       pg.close();
777     } catch (Exception ex)
778     {
779       ex.printStackTrace();
780     }
781   }
782
783   /**
784    * DOCUMENT ME!
785    * 
786    * @param e
787    *          DOCUMENT ME!
788    */
789   @Override
790   public void pngTree_actionPerformed(ActionEvent e)
791   {
792     int width = treeCanvas.getWidth();
793     int height = treeCanvas.getHeight();
794
795     try
796     {
797       JalviewFileChooser chooser = new JalviewFileChooser(
798               ImageMaker.PNG_EXTENSION, ImageMaker.PNG_DESCRIPTION);
799
800       chooser.setFileView(new jalview.io.JalviewFileView());
801       chooser.setDialogTitle(MessageManager
802               .getString("label.create_png_from_tree"));
803       chooser.setToolTipText(MessageManager.getString("action.save"));
804
805       int value = chooser.showSaveDialog(this);
806
807       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
808       {
809         return;
810       }
811
812       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
813               .getSelectedFile().getParent());
814
815       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
816
817       BufferedImage bi = new BufferedImage(width, height,
818               BufferedImage.TYPE_INT_RGB);
819       Graphics png = bi.getGraphics();
820
821       treeCanvas.draw(png, width, height);
822
823       ImageIO.write(bi, "png", out);
824       out.close();
825     } catch (Exception ex)
826     {
827       ex.printStackTrace();
828     }
829   }
830
831   /**
832    * change node labels to the annotation referred to by labelClass TODO:
833    * promote to a datamodel modification that can be undone TODO: make argument
834    * one case of a generic transformation function ie { undoStep = apply(Tree,
835    * TransformFunction)};
836    * 
837    * @param labelClass
838    */
839   public void changeNames(final String labelClass)
840   {
841     tree.applyToNodes(new NodeTransformI()
842     {
843
844       @Override
845       public void transform(BinaryNode node)
846       {
847         if (node instanceof SequenceNode
848                 && !((SequenceNode) node).isPlaceholder()
849                 && !((SequenceNode) node).isDummy())
850         {
851           String newname = null;
852           SequenceI sq = (SequenceI) ((SequenceNode) node).element();
853           if (sq != null)
854           {
855             // search dbrefs, features and annotation
856             DBRefEntry[] refs = jalview.util.DBRefUtils.selectRefs(
857                     sq.getDBRefs(),
858                     new String[] { labelClass.toUpperCase() });
859             if (refs != null)
860             {
861               for (int i = 0; i < refs.length; i++)
862               {
863                 if (newname == null)
864                 {
865                   newname = new String(refs[i].getAccessionId());
866                 }
867                 else
868                 {
869                   newname = newname + "; " + refs[i].getAccessionId();
870                 }
871               }
872             }
873             if (newname == null)
874             {
875               SequenceFeature sf[] = sq.getSequenceFeatures();
876               for (int i = 0; sf != null && i < sf.length; i++)
877               {
878                 if (sf[i].getType().equals(labelClass))
879                 {
880                   if (newname == null)
881                   {
882                     newname = new String(sf[i].getDescription());
883                   }
884                   else
885                   {
886                     newname = newname + "; " + sf[i].getDescription();
887                   }
888                 }
889               }
890             }
891           }
892           if (newname != null)
893           {
894             String oldname = ((SequenceNode) node).getName();
895             // TODO : save in the undo object for this modification.
896             ((SequenceNode) node).setName(newname);
897           }
898         }
899       }
900     });
901   }
902 }