JAL-2788 possible adjustments to sequence accesses
[jalview.git] / src / jalview / datamodel / HiddenSequences.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.datamodel;
22
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26
27 public class HiddenSequences
28 {
29   /**
30    * holds a list of hidden sequences associated with an alignment.
31    */
32   public SequenceI[] hiddenSequences;
33
34   AlignmentI alignment;
35
36   /**
37    * Constructor given a reference to an alignment (with no hidden sequences)
38    * 
39    * @param al
40    */
41   public HiddenSequences(AlignmentI al)
42   {
43     alignment = al;
44   }
45
46   /**
47    * Answers the number of hidden sequences
48    * 
49    * @return
50    */
51   public int getSize()
52   {
53     if (hiddenSequences == null)
54     {
55       return 0;
56     }
57     int count = 0;
58     for (SequenceI seq : hiddenSequences)
59     {
60       if (seq != null)
61       {
62         count++;
63       }
64     }
65
66     return count;
67   }
68
69   /**
70    * Answers the length of the longest hidden sequence
71    * 
72    * @return
73    */
74   public int getWidth()
75   {
76     if (hiddenSequences == null)
77     {
78       return 0;
79     }
80     int width = 0;
81     for (SequenceI seq : hiddenSequences)
82     {
83       if (seq != null && seq.getLength() > width)
84       {
85         width = seq.getLength();
86       }
87     }
88
89     return width;
90   }
91
92   /**
93    * Call this method after a sequence is removed from the main alignment
94    */
95   public void adjustHeightSequenceDeleted(int seqIndex)
96   {
97     if (hiddenSequences == null)
98     {
99       return;
100     }
101
102     int alHeight = alignment.getHeight();
103
104     SequenceI[] tmp = new SequenceI[alHeight + getSize()];
105     int deletionIndex = adjustForHiddenSeqs(seqIndex);
106
107     for (int i = 0; i < hiddenSequences.length; i++)
108     {
109       if (hiddenSequences[i] == null)
110       {
111         continue;
112       }
113
114       if (i > deletionIndex)
115       {
116         tmp[i - 1] = hiddenSequences[i];
117       }
118       else
119       {
120         tmp[i] = hiddenSequences[i];
121       }
122     }
123
124     hiddenSequences = tmp;
125
126   }
127
128   /**
129    * Call this method after a sequence is added to the main alignment
130    */
131   public void adjustHeightSequenceAdded()
132   {
133     if (hiddenSequences == null)
134     {
135       return;
136     }
137
138     int alHeight = alignment.getHeight();
139
140     SequenceI[] tmp = new SequenceI[alHeight + getSize()];
141     System.arraycopy(hiddenSequences, 0, tmp, 0, hiddenSequences.length);
142     hiddenSequences = tmp;
143   }
144
145   /**
146    * Mark the specified sequence as hidden
147    * 
148    * @param sequence
149    */
150   public void hideSequence(SequenceI sequence)
151   {
152     if (hiddenSequences == null)
153     {
154       hiddenSequences = new SequenceI[alignment.getHeight()];
155     }
156
157     int absAlignmentIndex = alignment.findIndex(sequence);
158     int alignmentIndex = adjustForHiddenSeqs(absAlignmentIndex);
159
160     if (alignmentIndex < 0 || hiddenSequences[alignmentIndex] != null)
161     {
162       System.out.println("ERROR!!!!!!!!!!!");
163       return;
164     }
165
166     hiddenSequences[alignmentIndex] = sequence;
167
168     alignment.deleteHiddenSequence(absAlignmentIndex);
169   }
170
171   public List<SequenceI> showAll(
172           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
173   {
174     List<SequenceI> revealedSeqs = new ArrayList<>();
175
176     if (hiddenSequences == null)
177     {
178       return revealedSeqs;
179     }
180
181     for (int i = 0; i < hiddenSequences.length; i++)
182     {
183       if (hiddenSequences[i] != null)
184       {
185         List<SequenceI> tmp = showSequence(i, hiddenRepSequences);
186         for (SequenceI seq : tmp)
187         {
188           revealedSeqs.add(seq);
189         }
190       }
191     }
192     return revealedSeqs;
193   }
194
195   /**
196    * Reveals (unhides) consecutive hidden sequences just above the given
197    * alignment index. The revealed sequences are selected (including their
198    * visible representative sequence if there was one and 'reveal' is being
199    * performed on it).
200    * 
201    * @param alignmentIndex
202    * @param hiddenRepSequences
203    *          a map of representative sequences to the sequences they represent
204    * @return
205    */
206   public List<SequenceI> showSequence(int alignmentIndex,
207           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
208   {
209     List<SequenceI> revealedSeqs = new ArrayList<>();
210     SequenceI repSequence = alignment.getSequenceAt(alignmentIndex);
211     if (repSequence != null && hiddenRepSequences != null
212             && hiddenRepSequences.containsKey(repSequence))
213     {
214       hiddenRepSequences.remove(repSequence);
215       revealedSeqs.add(repSequence);
216     }
217
218     int start = adjustForHiddenSeqs(alignmentIndex - 1);
219     int end = adjustForHiddenSeqs(alignmentIndex);
220     if (end >= hiddenSequences.length)
221     {
222       end = hiddenSequences.length - 1;
223     }
224
225     List<SequenceI> asequences;
226     synchronized (asequences = alignment.getSequences())
227     {
228       for (int index = end; index > start; index--)
229       {
230         SequenceI seq = hiddenSequences[index];
231         hiddenSequences[index] = null;
232
233         if (seq != null)
234         {
235           if (seq.getLength() > 0)
236           {
237             revealedSeqs.add(seq);
238             asequences.add(alignmentIndex, seq);
239           }
240           else
241           {
242             System.out.println(
243                     seq.getName() + " has been deleted whilst hidden");
244           }
245         }
246       }
247     }
248     return revealedSeqs;
249   }
250
251   public SequenceI getHiddenSequence(int alignmentIndex)
252   {
253     return hiddenSequences == null ? null : hiddenSequences[alignmentIndex];
254   }
255
256   /**
257    * Convert absolute alignment index to visible alignment index (or -1 if
258    * before the first visible sequence)
259    * 
260    * @param alignmentIndex
261    * @return
262    */
263   public int findIndexWithoutHiddenSeqs(int alignmentIndex)
264   {
265     if (hiddenSequences == null)
266     {
267       return alignmentIndex;
268     }
269     int index = 0;
270     int hiddenSeqs = 0;
271     int diff = 0;
272     if (hiddenSequences.length <= alignmentIndex)
273     {
274       // if the alignmentIndex runs past the end of hidden sequences
275       // and therefore actually past the end of the alignment
276       // store the difference to add back on at the end, so that behaviour
277       // is consistent with hidden columns behaviour (used by overview panel)
278       diff = alignmentIndex - hiddenSequences.length + 1;
279       alignmentIndex = hiddenSequences.length - 1;
280     }
281
282     while (index <= alignmentIndex)
283     {
284       if (hiddenSequences[index] != null)
285       {
286         hiddenSeqs++;
287       }
288       index++;
289     }
290
291     return (alignmentIndex - hiddenSeqs + diff);
292   }
293
294   /**
295    * Find the visible row which is a given visible number of rows above another
296    * visible row. i.e. for a startRow x, the row which is distance 1 away will
297    * be row x-1.
298    * 
299    * @param visibleDistance
300    *          the number of visible rows to offset by
301    * @param startRow
302    *          the row to start from
303    * @return the position of the row in the visible alignment
304    */
305   public int subtractVisibleRows(int visibleDistance, int startRow)
306   {
307     // walk upwards through the alignment
308     // count all the non-null sequences until we have visibleDistance counted
309     // then return the next visible sequence
310     if (hiddenSequences == null)
311     {
312       return startRow - visibleDistance;
313     }
314
315     int index = Math.min(startRow, hiddenSequences.length - 1);
316     int count = 0;
317     while ((index > -1) && (count < visibleDistance))
318     {
319       if (hiddenSequences[index] == null)
320       {
321         // count visible sequences
322         count++;
323       }
324       index--;
325     }
326     return index;
327   }
328
329   /**
330    * Convert alignment index from visible alignment to absolute alignment
331    * 
332    * @param alignmentIndex
333    * @return
334    */
335   public int adjustForHiddenSeqs(int alignmentIndex)
336   {
337     if (hiddenSequences == null)
338     {
339       return alignmentIndex;
340     }
341     int index = 0;
342     int hSize = hiddenSequences.length;
343     while (index <= alignmentIndex && index < hSize)
344     {
345       if (hiddenSequences[index] != null)
346       {
347         alignmentIndex++;
348       }
349       index++;
350     }
351     ;
352
353     return alignmentIndex;
354   }
355
356   /**
357    * makes a copy of the alignment with hidden sequences included. Using the
358    * copy for anything other than simple output is not recommended. Note - this
359    * method DOES NOT USE THE AlignmentI COPY CONSTRUCTOR!
360    * 
361    * @return
362    */
363   public AlignmentI getFullAlignment()
364   {
365     SequenceI[] seq;
366     if (hiddenSequences == null)
367     {
368       seq = alignment.getSequencesArray();
369     }
370     else
371     {
372       int isize = hiddenSequences.length;
373       seq = new Sequence[isize];
374
375       int index = 0;
376       for (int i = 0; i < hiddenSequences.length; i++)
377       {
378         if (hiddenSequences[i] != null)
379         {
380           seq[i] = hiddenSequences[i];
381         }
382         else
383         {
384           seq[i] = alignment.getSequenceAt(index);
385           index++;
386         }
387       }
388     }
389     Alignment fAlignmt = new Alignment(seq);
390     fAlignmt.annotations = alignment.getAlignmentAnnotation();
391     fAlignmt.alignmentProperties = alignment.getProperties();
392     fAlignmt.groups = alignment.getGroups();
393     fAlignmt.hasRNAStructure = alignment.hasRNAStructure();
394     fAlignmt.setSeqrep(alignment.getSeqrep());
395
396     return fAlignmt;
397   }
398
399   public boolean isHidden(SequenceI seq)
400   {
401     if (hiddenSequences != null)
402     {
403       for (int i = 0; i < hiddenSequences.length; i++)
404       {
405         if (hiddenSequences[i] != null && hiddenSequences[i] == seq)
406         {
407           return true;
408         }
409       }
410     }
411
412     return false;
413   }
414
415   /**
416    * Answers if a sequence is hidden
417    * 
418    * @param seq
419    *          (absolute) index to test
420    * @return true if sequence at index seq is hidden
421    */
422   public boolean isHidden(int seq)
423   {
424     if (hiddenSequences != null)
425     {
426       return (hiddenSequences[seq] != null);
427     }
428     return false;
429   }
430 }