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