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