JAL-1432 updated copyright notices
[jalview.git] / src / jalview / datamodel / CigarArray.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3  * Copyright (C) 2014 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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.datamodel;
20
21 import java.util.Vector;
22
23 public class CigarArray extends CigarBase
24 {
25   /**
26    * Do CIGAR operations on a set of sequences from many other cigars BAD THINGS
27    * WILL HAPPEN IF A CIGARARRAY IS PASSED TO A CIGARARRAY or a CIGARCIGAR is
28    * given a CIGARARRAY to insert gaps into.
29    */
30   /**
31    * array of subject cigars
32    */
33   public CigarSimple refCigars[] = null;
34
35   private boolean seqcigararray = false;
36
37   private CigarArray()
38   {
39     super();
40   }
41
42   /**
43    * isSeqCigarArray()
44    * 
45    * @return boolean true if all refCigars resolve to a SeqCigar or a CigarCigar
46    */
47   public boolean isSeqCigarArray()
48   {
49     return seqcigararray;
50   }
51
52   /**
53    * Apply CIGAR operations to several cigars in parallel will throw an error if
54    * any of cigar are actually CigarArrays.
55    * 
56    * @param cigar
57    *          Cigar[]
58    */
59   public CigarArray(CigarSimple[] cigars)
60   {
61     super();
62     seqcigararray = true;
63     if (cigars != null && cigars.length > 0)
64     {
65       refCigars = new CigarSimple[cigars.length];
66       for (int c = 0; c < cigars.length; c++)
67       {
68         refCigars[c] = cigars[c];
69         if (!((cigars[c] instanceof SeqCigar) || cigars[c] instanceof CigarCigar))
70         {
71           seqcigararray = false;
72         }
73       }
74     }
75   }
76
77   /**
78    * construct a cigar array from the current alignment, or just the subset of
79    * the current alignment specified by selectionGroup. Any columns marked as
80    * hidden in columnSelection will be marked as deleted in the array.
81    * 
82    * @param alignment
83    * @param columnSelection
84    * @param selectionGroup
85    */
86   public CigarArray(AlignmentI alignment, ColumnSelection columnSelection,
87           SequenceGroup selectionGroup)
88   {
89     this(constructSeqCigarArray(alignment, selectionGroup));
90     constructFromAlignment(alignment,
91             columnSelection != null ? columnSelection.getHiddenColumns()
92                     : null, selectionGroup);
93   }
94
95   private static int[] _calcStartEndBounds(AlignmentI alignment,
96           SequenceGroup selectionGroup)
97   {
98     int[] startend = new int[]
99     { 0, 0, 0 };
100     if (selectionGroup != null)
101     {
102       startend[0] = selectionGroup.getSize();
103       startend[1] = selectionGroup.getStartRes();
104       startend[2] = selectionGroup.getEndRes(); // inclusive for start and end
105                                                 // in
106       // SeqCigar constructor
107     }
108     else
109     {
110       startend[0] = alignment.getHeight();
111       startend[2] = alignment.getWidth() - 1;
112     }
113     return startend;
114   }
115
116   public static SeqCigar[] constructSeqCigarArray(AlignmentI alignment,
117           SequenceGroup selectionGroup)
118   {
119     SequenceI[] seqs = null;
120     int i, iSize;
121     int _startend[] = _calcStartEndBounds(alignment, selectionGroup);
122     int start = _startend[1], end = _startend[2];
123     if (selectionGroup != null)
124     {
125       iSize = selectionGroup.getSize();
126       seqs = selectionGroup.getSequencesInOrder(alignment);
127       start = selectionGroup.getStartRes();
128       end = selectionGroup.getEndRes(); // inclusive for start and end in
129       // SeqCigar constructor
130     }
131     else
132     {
133       iSize = alignment.getHeight();
134       seqs = alignment.getSequencesArray();
135       end = alignment.getWidth() - 1;
136     }
137     SeqCigar[] selseqs = new SeqCigar[iSize];
138     for (i = 0; i < iSize; i++)
139     {
140       selseqs[i] = new SeqCigar(seqs[i], start, end);
141     }
142     return selseqs;
143   }
144
145   /**
146    * internal constructor function - called by CigarArray(AlignmentI, ...);
147    * 
148    * @param alignment
149    * @param columnSelection
150    *          - vector of visible regions as returned from
151    *          columnSelection.getHiddenColumns()
152    * @param selectionGroup
153    */
154   private void constructFromAlignment(AlignmentI alignment,
155           Vector columnSelection, SequenceGroup selectionGroup)
156   {
157     int[] _startend = _calcStartEndBounds(alignment, selectionGroup);
158     int start = _startend[1], end = _startend[2];
159     // now construct the CigarArray operations
160     if (columnSelection != null)
161     {
162       int[] region;
163       int hideStart, hideEnd;
164       int last = start;
165       for (int j = 0; last < end & j < columnSelection.size(); j++)
166       {
167         region = (int[]) columnSelection.elementAt(j);
168         hideStart = region[0];
169         hideEnd = region[1];
170         // edit hidden regions to selection range
171         if (hideStart < last)
172         {
173           if (hideEnd > last)
174           {
175             hideStart = last;
176           }
177           else
178           {
179             continue;
180           }
181         }
182
183         if (hideStart > end)
184         {
185           break;
186         }
187
188         if (hideEnd > end)
189         {
190           hideEnd = end;
191         }
192
193         if (hideStart > hideEnd)
194         {
195           break;
196         }
197         /**
198          * form operations...
199          */
200         if (last < hideStart)
201         {
202           addOperation(CigarArray.M, hideStart - last);
203         }
204         addOperation(CigarArray.D, 1 + hideEnd - hideStart);
205         last = hideEnd + 1;
206       }
207       // Final match if necessary.
208       if (last < end)
209       {
210         addOperation(CigarArray.M, end - last + 1);
211       }
212     }
213     else
214     {
215       addOperation(CigarArray.M, end - start + 1);
216     }
217   }
218
219   /**
220    * @see Cigar.getSequenceAndDeletions
221    * @param GapChar
222    *          char
223    * @return Object[][]
224    */
225   protected Object[][] getArrayofSequenceAndDeletions(char GapChar)
226   {
227     if (refCigars == null || refCigars.length == 0 || length == 0)
228     {
229       return null;
230     }
231     Object[][] sqanddels = new Object[refCigars.length][];
232     for (int c = 0; c < refCigars.length; c++)
233     {
234       String refString = refCigars[c].getSequenceString(GapChar);
235       if (refString != null)
236       {
237         sqanddels[c] = getSequenceAndDeletions(refString, GapChar);
238       }
239       else
240       {
241         sqanddels[c] = null;
242       }
243     }
244     return sqanddels;
245   }
246
247   /**
248    * NOTE: this is an improper sequence string function
249    * 
250    * @return String formed by newline concatenated results of applying CIGAR
251    *         operations to each reference object in turn.
252    * @param GapChar
253    *          char
254    * @return '\n' separated strings (empty results included as \n\n)
255    */
256   public String getSequenceString(char GapChar)
257   {
258     if (length == 0 || refCigars == null)
259     {
260       return "";
261     }
262     StringBuffer seqStrings = new StringBuffer();
263     Object[][] sqanddels = getArrayofSequenceAndDeletions(GapChar);
264     for (int c = 0; c < refCigars.length; c++)
265     {
266       if (sqanddels[c] != null)
267       {
268         seqStrings.append((String) sqanddels[c][0]);
269         sqanddels[c][0] = null;
270       }
271       seqStrings.append('\n');
272     }
273     return seqStrings.toString();
274   }
275
276   /**
277    * return string results of applying cigar string to all reference cigars
278    * 
279    * @param GapChar
280    *          char
281    * @return String[]
282    */
283   public String[] getSequenceStrings(char GapChar)
284   {
285
286     if (length == 0 || refCigars == null || refCigars.length == 0)
287     {
288       return null;
289     }
290     Object[][] sqanddels = getArrayofSequenceAndDeletions(GapChar);
291     String[] seqs = new String[sqanddels.length];
292     for (int c = 0; c < refCigars.length; c++)
293     {
294       seqs[c] = (String) sqanddels[c][0];
295     }
296     return seqs;
297   }
298
299   /**
300    * Combines the CigarArray cigar operations with the operations in each
301    * reference cigar - creating a new reference cigar
302    * 
303    * @return Cigar[]
304    * 
305    *         public CigarBase[] getEditedCigars() {
306    * 
307    *         return new CigarBase[] {}; }
308    */
309   /**
310    * applyDeletions edits underlying refCigars to propagate deleted regions, and
311    * removes deletion operations from CigarArray operation list.
312    * 
313    * @return int[] position after deletion occured and range of deletion in
314    *         cigarArray or null if none occured
315    */
316   public int[] applyDeletions()
317   {
318     java.util.Vector delpos = null;
319     if (length == 0)
320     {
321       return null;
322     }
323     int cursor = 0; // range counter for deletions
324     int vcursor = 0; // visible column index
325     int offset = 0; // shift in visible column index as deletions are made
326     int i = 0;
327     while (i < length)
328     {
329       if (operation[i] != D)
330       {
331         if (operation[i] == M)
332         {
333           cursor += range[i];
334         }
335         vcursor += range[i++];
336       }
337       else
338       {
339         if (delpos == null)
340         {
341           delpos = new java.util.Vector();
342         }
343         int delstart = cursor, delend = cursor + range[i] - 1; // inclusive
344         delpos.addElement(new int[]
345         { vcursor + offset, range[i] }); // index of right hand column after
346         // hidden region boundary
347         offset += range[i] - 1; // shift in visible column coordinates
348         System.arraycopy(operation, i + 1, operation, i, length - i);
349         System.arraycopy(range, i + 1, range, i, length - i);
350         length--;
351         /*
352          * int dmax=0; for (int s=0; s<refCigars.length; s++) { int d =
353          * refCigars[s].deleteRange(delstart, delend); if (d>dmax) dmax=d; }
354          * offset+=dmax; // shift in visible column coordinates
355          */
356         for (int s = 0; s < refCigars.length; s++)
357         {
358           int d = refCigars[s].deleteRange(delstart, delend);
359         }
360
361       }
362     }
363     if (delpos != null)
364     {
365       int[] pos = new int[delpos.size() * 2];
366       for (int k = 0, l = delpos.size(); k < l; k++)
367       {
368         int[] dr = ((int[]) delpos.elementAt(k));
369         pos[k * 2] = dr[0];
370         pos[k * 2 + 1] = dr[1];
371         delpos.setElementAt(null, k);
372       }
373       delpos = null;
374       return pos;
375     }
376     return null;
377   }
378
379   /**
380    * 
381    * @return SeqCigar[] or null if CigarArray is not a SeqCigarArray (ie it does
382    *         not resolve to set of seqCigars)
383    */
384   public SeqCigar[] getSeqCigarArray()
385   {
386     if (!isSeqCigarArray())
387     {
388       return null;
389     }
390     SeqCigar[] sa = new SeqCigar[refCigars.length];
391     for (int i = 0; i < refCigars.length; i++)
392     {
393       sa[i] = (SeqCigar) refCigars[i];
394     }
395     return sa;
396   }
397 }