Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.gui

File AppVarna.java

 

Coverage histogram

../../img/srcFileCovDistChart1.png
53% of files have more coverage

Code metrics

78
201
32
2
759
499
83
0.41
6.28
16
2.59

Classes

Class Line # Actions
AppVarna 64 182 73 254
0.0928571459.3%
AppVarna.VarnaHighlighter 95 19 10 31
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

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.AlignSeq;
24    import jalview.datamodel.AlignmentAnnotation;
25    import jalview.datamodel.ColumnSelection;
26    import jalview.datamodel.HiddenColumns;
27    import jalview.datamodel.RnaViewerModel;
28    import jalview.datamodel.SequenceGroup;
29    import jalview.datamodel.SequenceI;
30    import jalview.ext.varna.RnaModel;
31    import jalview.structure.SecondaryStructureListener;
32    import jalview.structure.SelectionListener;
33    import jalview.structure.SelectionSource;
34    import jalview.structure.StructureSelectionManager;
35    import jalview.structure.VamsasSource;
36    import jalview.util.Comparison;
37    import jalview.util.MessageManager;
38    import jalview.util.ShiftList;
39   
40    import java.awt.BorderLayout;
41    import java.awt.Color;
42    import java.util.Collection;
43    import java.util.Hashtable;
44    import java.util.LinkedHashMap;
45    import java.util.List;
46    import java.util.Map;
47   
48    import javax.swing.JInternalFrame;
49    import javax.swing.JSplitPane;
50    import javax.swing.event.InternalFrameAdapter;
51    import javax.swing.event.InternalFrameEvent;
52   
53    import fr.orsay.lri.varna.VARNAPanel;
54    import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
55    import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
56    import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
57    import fr.orsay.lri.varna.interfaces.InterfaceVARNASelectionListener;
58    import fr.orsay.lri.varna.models.BaseList;
59    import fr.orsay.lri.varna.models.FullBackup;
60    import fr.orsay.lri.varna.models.annotations.HighlightRegionAnnotation;
61    import fr.orsay.lri.varna.models.rna.ModeleBase;
62    import fr.orsay.lri.varna.models.rna.RNA;
63   
 
64    public class AppVarna extends JInternalFrame
65    implements SelectionListener, SecondaryStructureListener,
66    InterfaceVARNASelectionListener, VamsasSource
67    {
68    private static final byte[] PAIRS = new byte[] { '(', ')', '[', ']', '{',
69    '}', '<', '>' };
70   
71    private AppVarnaBinding vab;
72   
73    private AlignmentPanel ap;
74   
75    private String viewId;
76   
77    private StructureSelectionManager ssm;
78   
79    /*
80    * Lookup for sequence and annotation mapped to each RNA in the viewer. Using
81    * a linked hashmap means that order is preserved when saved to the project.
82    */
83    private Map<RNA, RnaModel> models = new LinkedHashMap<RNA, RnaModel>();
84   
85    private Map<RNA, ShiftList> offsets = new Hashtable<RNA, ShiftList>();
86   
87    private Map<RNA, ShiftList> offsetsInv = new Hashtable<RNA, ShiftList>();
88   
89    private JSplitPane split;
90   
91    private VarnaHighlighter mouseOverHighlighter = new VarnaHighlighter();
92   
93    private VarnaHighlighter selectionHighlighter = new VarnaHighlighter();
94   
 
95    private class VarnaHighlighter
96    {
97    private HighlightRegionAnnotation _lastHighlight;
98   
99    private RNA _lastRNAhighlighted = null;
100   
 
101  0 toggle public VarnaHighlighter()
102    {
103   
104    }
105   
106    /**
107    * Constructor when restoring from Varna session, including any highlight
108    * state
109    *
110    * @param rna
111    */
 
112  0 toggle public VarnaHighlighter(RNA rna)
113    {
114    // TODO nice try but doesn't work; do we need a highlighter per model?
115  0 _lastRNAhighlighted = rna;
116  0 List<HighlightRegionAnnotation> highlights = rna.getHighlightRegion();
117  0 if (highlights != null && !highlights.isEmpty())
118    {
119  0 _lastHighlight = highlights.get(0);
120    }
121    }
122   
 
123  0 toggle public void highlightRegion(RNA rna, int start, int end)
124    {
125  0 clearLastSelection();
126  0 HighlightRegionAnnotation highlight = new HighlightRegionAnnotation(
127    rna.getBasesBetween(start, end));
128  0 rna.addHighlightRegion(highlight);
129  0 _lastHighlight = highlight;
130  0 _lastRNAhighlighted = rna;
131    }
132   
 
133  0 toggle public HighlightRegionAnnotation getLastHighlight()
134    {
135  0 return _lastHighlight;
136    }
137   
138    /**
139    * Clears all structure selection and refreshes the display
140    */
 
141  0 toggle public void clearSelection()
142    {
143  0 if (_lastRNAhighlighted != null)
144    {
145  0 _lastRNAhighlighted.getHighlightRegion().clear();
146  0 vab.updateSelectedRNA(_lastRNAhighlighted);
147  0 _lastRNAhighlighted = null;
148  0 _lastHighlight = null;
149    }
150    }
151   
152    /**
153    * Clear the last structure selection
154    */
 
155  0 toggle public void clearLastSelection()
156    {
157  0 if (_lastRNAhighlighted != null)
158    {
159  0 _lastRNAhighlighted.removeHighlightRegion(_lastHighlight);
160  0 _lastRNAhighlighted = null;
161  0 _lastHighlight = null;
162    }
163    }
164    }
165   
166    /**
167    * Constructor
168    *
169    * @param seq
170    * the RNA sequence
171    * @param aa
172    * the annotation with the secondary structure string
173    * @param ap
174    * the AlignmentPanel creating this object
175    */
 
176  0 toggle public AppVarna(SequenceI seq, AlignmentAnnotation aa, AlignmentPanel ap)
177    {
178  0 this(ap);
179   
180  0 String sname = aa.sequenceRef == null
181    ? "secondary structure (alignment)"
182    : seq.getName() + " structure";
183  0 String theTitle = sname
184  0 + (aa.sequenceRef == null ? " trimmed to " + seq.getName()
185    : "");
186  0 theTitle = MessageManager.formatMessage("label.varna_params",
187    new String[]
188    { theTitle });
189  0 setTitle(theTitle);
190   
191  0 String gappedTitle = sname + " (with gaps)";
192  0 RnaModel gappedModel = new RnaModel(gappedTitle, aa, seq, null, true);
193  0 addModel(gappedModel, gappedTitle);
194   
195  0 String trimmedTitle = "trimmed " + sname;
196  0 RnaModel trimmedModel = new RnaModel(trimmedTitle, aa, seq, null,
197    false);
198  0 addModel(trimmedModel, trimmedTitle);
199  0 vab.setSelectedIndex(0);
200    }
201   
202    /**
203    * Constructor that links the viewer to a parent panel (but has no structures
204    * yet - use addModel to add them)
205    *
206    * @param ap
207    */
 
208  0 toggle protected AppVarna(AlignmentPanel ap)
209    {
210  0 this.ap = ap;
211  0 this.viewId = System.currentTimeMillis() + "." + this.hashCode();
212  0 vab = new AppVarnaBinding();
213  0 initVarna();
214   
215  0 this.ssm = ap.getStructureSelectionManager();
216  0 ssm.addStructureViewerListener(this);
217  0 ssm.addSelectionListener(this);
218  0 addInternalFrameListener(new InternalFrameAdapter()
219    {
 
220  0 toggle @Override
221    public void internalFrameClosed(InternalFrameEvent evt)
222    {
223  0 close();
224    }
225    });
226    }
227   
228    /**
229    * Constructor given viewer data read from a saved project file
230    *
231    * @param model
232    * @param ap
233    * the (or a) parent alignment panel
234    */
 
235  0 toggle public AppVarna(RnaViewerModel model, AlignmentPanel ap)
236    {
237  0 this(ap);
238  0 setTitle(model.title);
239  0 this.viewId = model.viewId;
240  0 setBounds(model.x, model.y, model.width, model.height);
241  0 this.split.setDividerLocation(model.dividerLocation);
242    }
243   
244    /**
245    * Constructs a split pane with an empty selection list and display panel, and
246    * adds it to the desktop
247    */
 
248  0 toggle public void initVarna()
249    {
250  0 VARNAPanel varnaPanel = vab.get_varnaPanel();
251  0 setBackground(Color.white);
252  0 split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
253    vab.getListPanel(), varnaPanel);
254  0 getContentPane().setLayout(new BorderLayout());
255  0 getContentPane().add(split, BorderLayout.CENTER);
256   
257  0 varnaPanel.addSelectionListener(this);
258  0 jalview.gui.Desktop.addInternalFrame(this, "", getBounds().width,
259    getBounds().height);
260  0 this.pack();
261  0 showPanel(true);
262    }
263   
264    /**
265    * Constructs a new RNA model from the given one, without gaps. Also
266    * calculates and saves a 'shift list'
267    *
268    * @param rna
269    * @param name
270    * @return
271    */
 
272  0 toggle public RNA trimRNA(RNA rna, String name)
273    {
274  0 ShiftList offset = new ShiftList();
275   
276  0 RNA rnaTrim = new RNA(name);
277  0 try
278    {
279  0 String structDBN = rna.getStructDBN(true);
280  0 rnaTrim.setRNA(rna.getSeq(), replaceOddGaps(structDBN));
281    } catch (ExceptionUnmatchedClosingParentheses e2)
282    {
283  0 e2.printStackTrace();
284    } catch (ExceptionFileFormatOrSyntax e3)
285    {
286  0 e3.printStackTrace();
287    }
288   
289  0 String seq = rnaTrim.getSeq();
290  0 StringBuilder struc = new StringBuilder(256);
291  0 struc.append(rnaTrim.getStructDBN(true));
292  0 int ofstart = -1;
293  0 int sleng = seq.length();
294   
295  0 for (int i = 0; i < sleng; i++)
296    {
297  0 if (Comparison.isGap(seq.charAt(i)))
298    {
299  0 if (ofstart == -1)
300    {
301  0 ofstart = i;
302    }
303    /*
304    * mark base or base & pair in the structure with *
305    */
306  0 if (!rnaTrim.findPair(i).isEmpty())
307    {
308  0 int m = rnaTrim.findPair(i).get(1);
309  0 int l = rnaTrim.findPair(i).get(0);
310   
311  0 struc.replace(m, m + 1, "*");
312  0 struc.replace(l, l + 1, "*");
313    }
314    else
315    {
316  0 struc.replace(i, i + 1, "*");
317    }
318    }
319    else
320    {
321  0 if (ofstart > -1)
322    {
323  0 offset.addShift(offset.shift(ofstart), ofstart - i);
324  0 ofstart = -1;
325    }
326    }
327    }
328    // final gap
329  0 if (ofstart > -1)
330    {
331  0 offset.addShift(offset.shift(ofstart), ofstart - sleng);
332  0 ofstart = -1;
333    }
334   
335    /*
336    * remove the marked gaps from the structure
337    */
338  0 String newStruc = struc.toString().replace("*", "");
339   
340    /*
341    * remove gaps from the sequence
342    */
343  0 String newSeq = AlignSeq.extractGaps(Comparison.GapChars, seq);
344   
345  0 try
346    {
347  0 rnaTrim.setRNA(newSeq, newStruc);
348  0 registerOffset(rnaTrim, offset);
349    } catch (ExceptionUnmatchedClosingParentheses e)
350    {
351  0 e.printStackTrace();
352    } catch (ExceptionFileFormatOrSyntax e)
353    {
354  0 e.printStackTrace();
355    }
356  0 return rnaTrim;
357    }
358   
359    /**
360    * Save the sequence to structure mapping, and also its inverse.
361    *
362    * @param rnaTrim
363    * @param offset
364    */
 
365  0 toggle private void registerOffset(RNA rnaTrim, ShiftList offset)
366    {
367  0 offsets.put(rnaTrim, offset);
368  0 offsetsInv.put(rnaTrim, offset.getInverse());
369    }
370   
 
371  0 toggle public void showPanel(boolean show)
372    {
373  0 this.setVisible(show);
374    }
375   
376    /**
377    * If a mouseOver event from the AlignmentPanel is noticed the currently
378    * selected RNA in the VARNA window is highlighted at the specific position.
379    * To be able to remove it before the next highlight it is saved in
380    * _lastHighlight
381    *
382    * @param sequence
383    * @param index
384    * the aligned sequence position (base 0)
385    * @param position
386    * the dataset sequence position (base 1)
387    */
 
388  0 toggle @Override
389    public void mouseOverSequence(SequenceI sequence, final int index,
390    final int position)
391    {
392  0 RNA rna = vab.getSelectedRNA();
393  0 if (rna == null)
394    {
395  0 return;
396    }
397  0 RnaModel rnaModel = models.get(rna);
398  0 if (rnaModel.seq == sequence)
399    {
400  0 int highlightPos = rnaModel.gapped ? index : position - 1;
401  0 mouseOverHighlighter.highlightRegion(rna, highlightPos, highlightPos);
402  0 vab.updateSelectedRNA(rna);
403    }
404    }
405   
 
406  0 toggle @Override
407    public void selection(SequenceGroup seqsel, ColumnSelection colsel,
408    HiddenColumns hidden, SelectionSource source)
409    {
410  0 if (source != ap.av)
411    {
412    // ignore events from anything but our parent alignpanel
413    // TODO - reuse many-one panel-view system in jmol viewer
414  0 return;
415    }
416  0 RNA rna = vab.getSelectedRNA();
417  0 if (rna == null)
418    {
419  0 return;
420    }
421  0 if (seqsel != null && seqsel.getSize() > 0)
422    {
423  0 int start = seqsel.getStartRes(), end = seqsel.getEndRes();
424  0 ShiftList shift = offsets.get(rna);
425  0 if (shift != null)
426    {
427  0 start = shift.shift(start);
428  0 end = shift.shift(end);
429    }
430  0 selectionHighlighter.highlightRegion(rna, start, end);
431  0 selectionHighlighter.getLastHighlight()
432    .setOutlineColor(seqsel.getOutlineColour());
433    // TODO - translate column markings to positions on structure if present.
434  0 vab.updateSelectedRNA(rna);
435    }
436    else
437    {
438  0 selectionHighlighter.clearSelection();
439    }
440    }
441   
442    /**
443    * Respond to a change of the base hovered over in the Varna viewer
444    */
 
445  0 toggle @Override
446    public void onHoverChanged(ModeleBase previousBase, ModeleBase newBase)
447    {
448  0 RNA rna = vab.getSelectedRNA();
449  0 ShiftList shift = offsetsInv.get(rna);
450  0 SequenceI seq = models.get(rna).seq;
451  0 if (newBase != null && seq != null)
452    {
453  0 if (shift != null)
454    {
455  0 int i = shift.shift(newBase.getIndex());
456    // System.err.println("shifted "+(arg1.getIndex())+" to "+i);
457  0 ssm.mouseOverVamsasSequence(seq, i, this);
458    }
459    else
460    {
461  0 ssm.mouseOverVamsasSequence(seq, newBase.getIndex(), this);
462    }
463    }
464    }
465   
 
466  0 toggle @Override
467    public void onSelectionChanged(BaseList arg0, BaseList arg1,
468    BaseList arg2)
469    {
470    // TODO translate selected regions in VARNA to a selection on the
471    // alignpanel.
472   
473    }
474   
475    /**
476    * Returns the path to a temporary file containing a representation of the
477    * state of one Varna display
478    *
479    * @param rna
480    *
481    * @return
482    */
 
483  0 toggle public String getStateInfo(RNA rna)
484    {
485  0 return vab.getStateInfo(rna);
486    }
487   
 
488  0 toggle public AlignmentPanel getAlignmentPanel()
489    {
490  0 return ap;
491    }
492   
 
493  0 toggle public String getViewId()
494    {
495  0 return viewId;
496    }
497   
498    /**
499    * Returns true if any of the viewer's models (not necessarily the one
500    * currently displayed) is for the given sequence
501    *
502    * @param seq
503    * @return
504    */
 
505  0 toggle public boolean isListeningFor(SequenceI seq)
506    {
507  0 for (RnaModel model : models.values())
508    {
509  0 if (model.seq == seq)
510    {
511  0 return true;
512    }
513    }
514  0 return false;
515    }
516   
517    /**
518    * Returns a value representing the horizontal split divider location
519    *
520    * @return
521    */
 
522  0 toggle public int getDividerLocation()
523    {
524  0 return split == null ? 0 : split.getDividerLocation();
525    }
526   
527    /**
528    * Tidy up as necessary when the viewer panel is closed
529    */
 
530  0 toggle protected void close()
531    {
532    /*
533    * Deregister as a listener, to release references to this object
534    */
535  0 if (ssm != null)
536    {
537  0 ssm.removeStructureViewerListener(AppVarna.this, null);
538  0 ssm.removeSelectionListener(AppVarna.this);
539    }
540    }
541   
542    /**
543    * Returns the secondary structure annotation that this viewer displays for
544    * the given sequence
545    *
546    * @return
547    */
 
548  0 toggle public AlignmentAnnotation getAnnotation(SequenceI seq)
549    {
550  0 for (RnaModel model : models.values())
551    {
552  0 if (model.seq == seq)
553    {
554  0 return model.ann;
555    }
556    }
557  0 return null;
558    }
559   
 
560  0 toggle public int getSelectedIndex()
561    {
562  0 return this.vab.getSelectedIndex();
563    }
564   
565    /**
566    * Returns the set of models shown by the viewer
567    *
568    * @return
569    */
 
570  0 toggle public Collection<RnaModel> getModels()
571    {
572  0 return models.values();
573    }
574   
575    /**
576    * Add a model (e.g. loaded from project file)
577    *
578    * @param rna
579    * @param modelName
580    */
 
581  0 toggle public RNA addModel(RnaModel model, String modelName)
582    {
583  0 if (!model.ann.isValidStruc())
584    {
585  0 throw new IllegalArgumentException(
586    "Invalid RNA structure annotation");
587    }
588   
589    /*
590    * opened on request in Jalview session
591    */
592  0 RNA rna = new RNA(modelName);
593  0 String struc = model.ann.getRNAStruc();
594  0 struc = replaceOddGaps(struc);
595   
596  0 String strucseq = model.seq.getSequenceAsString();
597  0 try
598    {
599  0 rna.setRNA(strucseq, struc);
600    } catch (ExceptionUnmatchedClosingParentheses e2)
601    {
602  0 e2.printStackTrace();
603    } catch (ExceptionFileFormatOrSyntax e3)
604    {
605  0 e3.printStackTrace();
606    }
607   
608  0 if (!model.gapped)
609    {
610  0 rna = trimRNA(rna, modelName);
611    }
612  0 models.put(rna, new RnaModel(modelName, model.ann, model.seq, rna,
613    model.gapped));
614  0 vab.addStructure(rna);
615  0 return rna;
616    }
617   
618    /**
619    * Constructs a shift list that describes the gaps in the sequence
620    *
621    * @param seq
622    * @return
623    */
 
624  0 toggle protected ShiftList buildOffset(SequenceI seq)
625    {
626    // TODO refactor to avoid duplication with trimRNA()
627    // TODO JAL-1789 bugs in use of ShiftList here
628  0 ShiftList offset = new ShiftList();
629  0 int ofstart = -1;
630  0 int sleng = seq.getLength();
631   
632  0 for (int i = 0; i < sleng; i++)
633    {
634  0 if (Comparison.isGap(seq.getCharAt(i)))
635    {
636  0 if (ofstart == -1)
637    {
638  0 ofstart = i;
639    }
640    }
641    else
642    {
643  0 if (ofstart > -1)
644    {
645  0 offset.addShift(offset.shift(ofstart), ofstart - i);
646  0 ofstart = -1;
647    }
648    }
649    }
650    // final gap
651  0 if (ofstart > -1)
652    {
653  0 offset.addShift(offset.shift(ofstart), ofstart - sleng);
654  0 ofstart = -1;
655    }
656  0 return offset;
657    }
658   
659    /**
660    * Set the selected index in the model selection list
661    *
662    * @param selectedIndex
663    */
 
664  0 toggle public void setInitialSelection(final int selectedIndex)
665    {
666    /*
667    * empirically it needs a second for Varna/AWT to finish loading/drawing
668    * models for this to work; SwingUtilities.invokeLater _not_ a solution;
669    * explanation and/or better solution welcome!
670    */
671  0 synchronized (this)
672    {
673  0 try
674    {
675  0 wait(1000);
676    } catch (InterruptedException e)
677    {
678    // meh
679    }
680    }
681  0 vab.setSelectedIndex(selectedIndex);
682    }
683   
684    /**
685    * Add a model with associated Varna session file
686    *
687    * @param rna
688    * @param modelName
689    */
 
690  0 toggle public RNA addModelSession(RnaModel model, String modelName,
691    String sessionFile)
692    {
693  0 if (!model.ann.isValidStruc())
694    {
695  0 throw new IllegalArgumentException(
696    "Invalid RNA structure annotation");
697    }
698   
699  0 try
700    {
701  0 FullBackup fromSession = vab.vp.loadSession(sessionFile);
702  0 vab.addStructure(fromSession.rna, fromSession.config);
703  0 RNA rna = fromSession.rna;
704    // copy the model, but now including the RNA object
705  0 RnaModel newModel = new RnaModel(model.title, model.ann, model.seq,
706    rna, model.gapped);
707  0 if (!model.gapped)
708    {
709  0 registerOffset(rna, buildOffset(model.seq));
710    }
711  0 models.put(rna, newModel);
712    // capture rna selection state when saved
713  0 selectionHighlighter = new VarnaHighlighter(rna);
714  0 return fromSession.rna;
715    } catch (ExceptionLoadingFailed e)
716    {
717  0 System.err
718    .println("Error restoring Varna session: " + e.getMessage());
719  0 return null;
720    }
721    }
722   
723    /**
724    * Replace everything except RNA secondary structure characters with a period
725    *
726    * @param s
727    * @return
728    */
 
729  5 toggle public static String replaceOddGaps(String s)
730    {
731  5 if (s == null)
732    {
733  1 return null;
734    }
735   
736    // this is measured to be 10 times faster than a regex replace
737  4 boolean changed = false;
738  4 byte[] bytes = s.getBytes();
739  30 for (int i = 0; i < bytes.length; i++)
740    {
741  26 boolean ok = false;
742    // todo check for ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) if
743    // wanted also
744  183 for (int j = 0; !ok && (j < PAIRS.length); j++)
745    {
746  157 if (bytes[i] == PAIRS[j])
747    {
748  15 ok = true;
749    }
750    }
751  26 if (!ok)
752    {
753  11 bytes[i] = '.';
754  11 changed = true;
755    }
756    }
757  4 return changed ? new String(bytes) : s;
758    }
759    }