a57dc9c5278064084beab0343e153615491dd4cb
[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 (hiddenSequences[alignmentIndex] != null)
161     {
162       System.out.println("ERROR!!!!!!!!!!!");
163     }
164
165     hiddenSequences[alignmentIndex] = sequence;
166
167     alignment.deleteHiddenSequence(absAlignmentIndex);
168   }
169
170   public List<SequenceI> showAll(
171           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
172   {
173     List<SequenceI> revealedSeqs = new ArrayList<>();
174
175     if (hiddenSequences == null)
176     {
177       return revealedSeqs;
178     }
179
180     for (int i = 0; i < hiddenSequences.length; i++)
181     {
182       if (hiddenSequences[i] != null)
183       {
184         List<SequenceI> tmp = showSequence(i, hiddenRepSequences);
185         for (SequenceI seq : tmp)
186         {
187           revealedSeqs.add(seq);
188         }
189       }
190     }
191     return revealedSeqs;
192   }
193
194   /**
195    * Reveals (unhides) consecutive hidden sequences just above the given
196    * alignment index. The revealed sequences are selected (including their
197    * visible representative sequence if there was one and 'reveal' is being
198    * performed on it).
199    * 
200    * @param alignmentIndex
201    * @param hiddenRepSequences
202    *          a map of representative sequences to the sequences they represent
203    * @return
204    */
205   public List<SequenceI> showSequence(int alignmentIndex,
206           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
207   {
208     List<SequenceI> revealedSeqs = new ArrayList<>();
209     SequenceI repSequence = alignment.getSequenceAt(alignmentIndex);
210     if (repSequence != null && hiddenRepSequences != null
211             && hiddenRepSequences.containsKey(repSequence))
212     {
213       hiddenRepSequences.remove(repSequence);
214       revealedSeqs.add(repSequence);
215     }
216
217     int start = adjustForHiddenSeqs(alignmentIndex - 1);
218     int end = adjustForHiddenSeqs(alignmentIndex);
219     if (end >= hiddenSequences.length)
220     {
221       end = hiddenSequences.length - 1;
222     }
223
224     List<SequenceI> asequences;
225     synchronized (asequences = alignment.getSequences())
226     {
227       for (int index = end; index > start; index--)
228       {
229         SequenceI seq = hiddenSequences[index];
230         hiddenSequences[index] = null;
231
232         if (seq != null)
233         {
234           if (seq.getLength() > 0)
235           {
236             revealedSeqs.add(seq);
237             asequences.add(alignmentIndex, seq);
238           }
239           else
240           {
241             System.out.println(seq.getName()
242                     + " has been deleted whilst hidden");
243           }
244         }
245       }
246     }
247     return revealedSeqs;
248   }
249
250   public SequenceI getHiddenSequence(int alignmentIndex)
251   {
252     return hiddenSequences == null ? null : hiddenSequences[alignmentIndex];
253   }
254
255   /**
256    * Convert absolute alignment index to visible alignment index (or -1 if
257    * before the first visible sequence)
258    * 
259    * @param alignmentIndex
260    * @return
261    */
262   public int findIndexWithoutHiddenSeqs(int alignmentIndex)
263   {
264     if (hiddenSequences == null)
265     {
266       return alignmentIndex;
267     }
268     int index = 0;
269     int hiddenSeqs = 0;
270     int diff = 0;
271     if (hiddenSequences.length <= alignmentIndex)
272     {
273       // if the alignmentIndex runs past the end of hidden sequences
274       // and therefore actually past the end of the alignment
275       // store the difference to add back on at the end, so that behaviour
276       // is consistent with hidden columns behaviour (used by overview panel)
277       diff = alignmentIndex - hiddenSequences.length + 1;
278       alignmentIndex = hiddenSequences.length - 1;
279     }
280
281     while (index <= alignmentIndex)
282     {
283       if (hiddenSequences[index] != null)
284       {
285         hiddenSeqs++;
286       }
287       index++;
288     }
289
290     return (alignmentIndex - hiddenSeqs + diff);
291   }
292
293   /**
294    * Find the visible row which is a given visible number of rows above another
295    * visible row. i.e. for a startRow x, the row which is distance 1 away will
296    * be row x-1.
297    * 
298    * @param visibleDistance
299    *          the number of visible rows to offset by
300    * @param startRow
301    *          the row to start from
302    * @return the position of the row in the visible alignment
303    */
304   public int subtractVisibleRows(int visibleDistance, int startRow)
305   {
306     // walk upwards through the alignment
307     // count all the non-null sequences until we have visibleDistance counted
308     // then return the next visible sequence
309     if (hiddenSequences == null)
310     {
311       return startRow - visibleDistance;
312     }
313
314     int index = startRow;
315     int count = 0;
316     while ((index > -1) && (count < visibleDistance))
317     {
318       if (hiddenSequences[index] == null)
319       {
320         // count visible sequences
321         count++;
322       }
323       index--;
324     }
325     return index;
326   }
327
328   /**
329    * Convert alignment index from visible alignment to absolute alignment
330    * 
331    * @param alignmentIndex
332    * @return
333    */
334   public int adjustForHiddenSeqs(int alignmentIndex)
335   {
336     if (hiddenSequences == null)
337     {
338       return alignmentIndex;
339     }
340     int index = 0;
341     int hSize = hiddenSequences.length;
342     while (index <= alignmentIndex && index < hSize)
343     {
344       if (hiddenSequences[index] != null)
345       {
346         alignmentIndex++;
347       }
348       index++;
349     }
350     ;
351
352     return alignmentIndex;
353   }
354
355   /**
356    * makes a copy of the alignment with hidden sequences included. Using the
357    * copy for anything other than simple output is not recommended. Note - this
358    * method DOES NOT USE THE AlignmentI COPY CONSTRUCTOR!
359    * 
360    * @return
361    */
362   public AlignmentI getFullAlignment()
363   {
364     SequenceI[] seq;
365     if (hiddenSequences == null)
366     {
367       seq = alignment.getSequencesArray();
368     }
369     else
370     {
371       int isize = hiddenSequences.length;
372       seq = new Sequence[isize];
373
374       int index = 0;
375       for (int i = 0; i < hiddenSequences.length; i++)
376       {
377         if (hiddenSequences[i] != null)
378         {
379           seq[i] = hiddenSequences[i];
380         }
381         else
382         {
383           seq[i] = alignment.getSequenceAt(index);
384           index++;
385         }
386       }
387     }
388     Alignment fAlignmt = new Alignment(seq);
389     fAlignmt.annotations = alignment.getAlignmentAnnotation();
390     fAlignmt.alignmentProperties = alignment.getProperties();
391     fAlignmt.groups = alignment.getGroups();
392     fAlignmt.hasRNAStructure = alignment.hasRNAStructure();
393     fAlignmt.setSeqrep(alignment.getSeqrep());
394
395     return fAlignmt;
396   }
397
398   public boolean isHidden(SequenceI seq)
399   {
400     if (hiddenSequences != null)
401     {
402       for (int i = 0; i < hiddenSequences.length; i++)
403       {
404         if (hiddenSequences[i] != null && hiddenSequences[i] == seq)
405         {
406           return true;
407         }
408       }
409     }
410
411     return false;
412   }
413
414   /**
415    * Answers if a sequence is hidden
416    * 
417    * @param seq
418    *          (absolute) index to test
419    * @return true if sequence at index seq is hidden
420    */
421   public boolean isHidden(int seq)
422   {
423     if (hiddenSequences != null)
424     {
425       return (hiddenSequences[seq] != null);
426     }
427     return false;
428   }
429 }