AdjustAnnotations needs position, number and insert
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.datamodel;
20
21 import jalview.analysis.*;
22
23 import java.util.*;
24
25 /** Data structure to hold and manipulate a multiple sequence alignment
26  */
27 public class Alignment implements AlignmentI
28 {
29     protected Alignment dataset;
30     protected Vector sequences;
31     protected Vector groups = new Vector();
32     protected char gapCharacter = '-';
33     protected int type = NUCLEOTIDE;
34     public static final int PROTEIN = 0;
35     public static final int NUCLEOTIDE = 1;
36
37     /** DOCUMENT ME!! */
38     public AlignmentAnnotation[] annotations;
39
40     HiddenSequences hiddenSequences = new HiddenSequences(this);
41
42     private void initAlignment(SequenceI[] seqs) {
43       int i=0;
44
45       if( jalview.util.Comparison.isNucleotide(seqs))
46         type = NUCLEOTIDE;
47       else
48         type = PROTEIN;
49
50       sequences = new Vector();
51
52       for (i = 0; i < seqs.length; i++)
53       {
54         sequences.addElement(seqs[i]);
55       }
56
57     }
58     /** Make an alignment from an array of Sequences.
59      *
60      * @param sequences
61      */
62     public Alignment(SequenceI[] seqs)
63     {
64       initAlignment(seqs);
65     }
66     /**
67      * Make a new alignment from an array of SeqCigars
68      * @param seqs SeqCigar[]
69      */
70     public Alignment(SeqCigar[] alseqs) {
71       SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter, new ColumnSelection(), null);
72       initAlignment(seqs);
73     }
74     /**
75      * Make a new alignment from an CigarArray
76      * JBPNote - can only do this when compactAlignment does not contain hidden regions.
77      * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
78      * @param compactAlignment CigarArray
79      */
80     public static AlignmentI createAlignment(CigarArray compactAlignment) {
81       throw new Error("Alignment(CigarArray) not yet implemented");
82       // this(compactAlignment.refCigars);
83     }
84
85     /**
86      * DOCUMENT ME!
87      *
88      * @return DOCUMENT ME!
89      */
90     public Vector getSequences()
91     {
92         return sequences;
93     }
94
95     public SequenceI [] getSequencesArray()
96     {
97       SequenceI [] reply = new SequenceI[sequences.size()];
98       for(int i=0; i<sequences.size(); i++)
99       {
100         reply[i] = (SequenceI)sequences.elementAt(i);
101       }
102       return reply;
103     }
104
105     /**
106      * DOCUMENT ME!
107      *
108      * @param i DOCUMENT ME!
109      *
110      * @return DOCUMENT ME!
111      */
112     public SequenceI getSequenceAt(int i)
113     {
114         if (i < sequences.size())
115         {
116             return (SequenceI) sequences.elementAt(i);
117         }
118
119         return null;
120     }
121
122     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
123      *
124      * @param snew
125      */
126     public void addSequence(SequenceI snew)
127     {
128       if(dataset!=null)
129       {
130         if(snew.getDatasetSequence()!=null)
131         {
132           getDataset().addSequence(snew.getDatasetSequence());
133         }
134         else
135         {
136           Sequence ds = new Sequence(snew.getName(),
137                                      AlignSeq.extractGaps("-. ",
138               snew.getSequenceAsString()),
139                                      snew.getStart(),
140                                      snew.getEnd());
141
142           snew.setDatasetSequence(ds);
143           getDataset().addSequence(ds);
144         }
145       }
146       sequences.addElement(snew);
147
148       hiddenSequences.adjustHeightSequenceAdded();
149     }
150
151
152     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
153      *
154      * @param snew
155      */
156     public void setSequenceAt(int i, SequenceI snew)
157     {
158         SequenceI oldseq = getSequenceAt(i);
159         deleteSequence(oldseq);
160
161         sequences.setElementAt(snew, i);
162     }
163
164     /**
165      * DOCUMENT ME!
166      *
167      * @return DOCUMENT ME!
168      */
169     public Vector getGroups()
170     {
171         return groups;
172     }
173
174     /**
175      * DOCUMENT ME!
176      *
177      * @param s DOCUMENT ME!
178      */
179     public void deleteSequence(SequenceI s)
180     {
181       deleteSequence(findIndex(s));
182     }
183
184     /**
185      * DOCUMENT ME!
186      *
187      * @param i DOCUMENT ME!
188      */
189     public void deleteSequence(int i)
190     {
191       if(i>-1 && i<getHeight())
192       {
193         sequences.removeElementAt(i);
194         hiddenSequences.adjustHeightSequenceDeleted(i);
195       }
196     }
197
198
199     /**    */
200     public SequenceGroup findGroup(SequenceI s)
201     {
202         for (int i = 0; i < this.groups.size(); i++)
203         {
204             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
205
206             if (sg.getSequences(null).contains(s))
207             {
208                 return sg;
209             }
210         }
211
212         return null;
213     }
214
215     /**
216      * DOCUMENT ME!
217      *
218      * @param s DOCUMENT ME!
219      *
220      * @return DOCUMENT ME!
221      */
222     public SequenceGroup[] findAllGroups(SequenceI s)
223     {
224         Vector temp = new Vector();
225
226         int gSize = groups.size();
227         for (int i = 0; i < gSize; i++)
228         {
229             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
230             if(sg==null || sg.getSequences(null)==null)
231             {
232               this.deleteGroup(sg);
233               gSize--;
234               continue;
235             }
236
237             if (sg.getSequences(null).contains(s))
238             {
239                 temp.addElement(sg);
240             }
241         }
242
243         SequenceGroup[] ret = new SequenceGroup[temp.size()];
244
245         for (int i = 0; i < temp.size(); i++)
246         {
247             ret[i] = (SequenceGroup) temp.elementAt(i);
248         }
249
250         return ret;
251     }
252
253
254
255     /**    */
256     public void addGroup(SequenceGroup sg)
257     {
258         if (!groups.contains(sg))
259         {
260           if(hiddenSequences.getSize()>0)
261           {
262             int i, iSize = sg.getSize();
263             for (i = 0; i < iSize; i++)
264             {
265               if (!sequences.contains(sg.getSequenceAt(i)))
266               {
267                 sg.deleteSequence(sg.getSequenceAt(i), false);
268                 iSize--;
269                 i--;
270               }
271             }
272
273             if (sg.getSize() < 1)
274               return;
275           }
276
277           groups.addElement(sg);
278         }
279     }
280
281     /**
282      * DOCUMENT ME!
283      */
284     public void deleteAllGroups()
285     {
286         groups.removeAllElements();
287     }
288
289     /**    */
290     public void deleteGroup(SequenceGroup g)
291     {
292         if (groups.contains(g))
293         {
294             groups.removeElement(g);
295         }
296     }
297
298     /**    */
299     public SequenceI findName(String name)
300     {
301         int i = 0;
302
303         while (i < sequences.size())
304         {
305             if (getSequenceAt(i).getName().equals(name))
306             {
307                 return getSequenceAt(i);
308             }
309
310             i++;
311         }
312
313         return null;
314     }
315
316     public SequenceI [] findSequenceMatch(String name)
317     {
318       Vector matches = new Vector();
319       int i = 0;
320
321       while (i < sequences.size())
322       {
323           if (getSequenceAt(i).getName().equals(name))
324           {
325               matches.addElement(getSequenceAt(i));
326           }
327           i++;
328       }
329
330       SequenceI [] result = new SequenceI[matches.size()];
331       for(i=0; i<result.length; i++)
332         result[i] = (SequenceI)matches.elementAt(i);
333
334       return result;
335
336     }
337
338
339     /**    */
340     public int findIndex(SequenceI s)
341     {
342         int i = 0;
343
344         while (i < sequences.size())
345         {
346             if (s == getSequenceAt(i))
347             {
348                 return i;
349             }
350
351             i++;
352         }
353
354         return -1;
355     }
356
357     /**
358      * DOCUMENT ME!
359      *
360      * @return DOCUMENT ME!
361      */
362     public int getHeight()
363     {
364         return sequences.size();
365     }
366
367     /**
368      * DOCUMENT ME!
369      *
370      * @return DOCUMENT ME!
371      */
372     public int getWidth()
373     {
374         int maxLength = -1;
375
376         for (int i = 0; i < sequences.size(); i++)
377         {
378             if (getSequenceAt(i).getLength() > maxLength)
379             {
380                 maxLength = getSequenceAt(i).getLength();
381             }
382         }
383
384         return maxLength;
385     }
386
387
388     /**
389      * DOCUMENT ME!
390      *
391      * @param gc DOCUMENT ME!
392      */
393     public void setGapCharacter(char gc)
394     {
395         gapCharacter = gc;
396
397         for (int i = 0; i < sequences.size(); i++)
398         {
399             Sequence seq = (Sequence) sequences.elementAt(i);
400             seq.setSequence( seq.getSequenceAsString().replace('.', gc) );
401             seq.setSequence( seq.getSequenceAsString().replace('-', gc) );
402             seq.setSequence( seq.getSequenceAsString().replace(' ', gc) );
403         }
404     }
405
406     /**
407      * DOCUMENT ME!
408      *
409      * @return DOCUMENT ME!
410      */
411     public char getGapCharacter()
412     {
413         return gapCharacter;
414     }
415
416
417     /**
418      * DOCUMENT ME!
419      *
420      * @return DOCUMENT ME!
421      */
422     public boolean isAligned()
423     {
424         int width = getWidth();
425
426         for (int i = 0; i < sequences.size(); i++)
427         {
428             if (getSequenceAt(i).getLength() != width)
429             {
430                 return false;
431             }
432         }
433
434         return true;
435     }
436
437     /**
438      * DOCUMENT ME!
439      *
440      * @param aa DOCUMENT ME!
441      */
442     public void deleteAnnotation(AlignmentAnnotation aa)
443     {
444         int aSize = 1;
445
446         if (annotations != null)
447         {
448             aSize = annotations.length;
449         }
450
451         if(aSize<1)
452           return;
453
454         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
455
456         int tIndex = 0;
457
458         for (int i = 0; i < aSize; i++)
459         {
460             if (annotations[i] == aa)
461             {
462                 continue;
463             }
464
465             temp[tIndex] = annotations[i];
466             tIndex++;
467         }
468
469         annotations = temp;
470     }
471
472
473     public void adjustSequenceAnnotations(int position,
474                                           int number,
475                                           boolean insert)
476     {
477       if(annotations!=null)
478       {
479         int aSize, tSize;
480         Annotation [] temp;
481         for (int a = 0; a < annotations.length; a++)
482         {
483           if(annotations[a].autoCalculated)
484           {
485             System.out.println("ignore "+annotations[a].label);
486             continue;
487           }
488           aSize = annotations[a].annotations.length;
489           if(insert)
490             tSize = aSize + number;
491           else
492             tSize = aSize - number;
493
494           temp = new Annotation[tSize];
495
496           if(insert)
497           {
498             System.arraycopy(annotations[a].annotations,
499                 0, temp, 0, position);
500
501             System.arraycopy(annotations[a].annotations,
502                 position, temp, position+number, aSize - position);
503           }
504           else
505           {
506               System.arraycopy(annotations[a].annotations,
507                                0, temp, 0, position);
508
509               System.arraycopy(annotations[a].annotations,
510                                position + number, temp, position,
511                                aSize - position - number);
512           }
513
514           annotations[a].annotations = temp;
515
516
517         //  if (annotations[a].sequenceRef != null)
518           {
519         //    annotations[a].adjustForAlignment();
520           }
521         }
522       }
523     }
524
525     /**
526      * DOCUMENT ME!
527      *
528      * @param aa DOCUMENT ME!
529      */
530     public void addAnnotation(AlignmentAnnotation aa)
531     {
532         int aSize = 1;
533         if (annotations != null)
534         {
535             aSize = annotations.length + 1;
536         }
537
538         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
539
540         temp[aSize-1] = aa;
541
542         int i = 0;
543
544         if (aSize > 1)
545         {
546             for (i = 0; i < (aSize-1); i++)
547             {
548                 temp[i] = annotations[i];
549             }
550         }
551
552         annotations = temp;
553     }
554
555     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
556     {
557       if(aa==null || annotations==null || annotations.length-1<index)
558         return;
559
560       int aSize = annotations.length;
561       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
562
563       temp[index] = aa;
564
565       for (int i = 0; i < aSize; i++)
566       {
567         if(i==index)
568           continue;
569
570         if(i<index)
571           temp[i] = annotations[i];
572         else
573           temp[i] = annotations[i-1];
574       }
575
576         annotations = temp;
577     }
578
579     /**
580      * DOCUMENT ME!
581      *
582      * @return DOCUMENT ME!
583      */
584     public AlignmentAnnotation[] getAlignmentAnnotation()
585     {
586         return annotations;
587     }
588
589     public void setNucleotide(boolean b)
590     {
591       if(b)
592         type = NUCLEOTIDE;
593       else
594         type = PROTEIN;
595     }
596
597     public boolean isNucleotide()
598     {
599       if(type==NUCLEOTIDE)
600         return true;
601       else
602         return false;
603     }
604
605     public void setDataset(Alignment data)
606     {
607       if(dataset==null && data==null)
608       {
609         // Create a new dataset for this alignment.
610         // Can only be done once, if dataset is not null
611         // This will not be performed
612         Sequence[] seqs = new Sequence[getHeight()];
613         SequenceI currentSeq;
614         for (int i = 0; i < getHeight(); i++)
615         {
616           currentSeq = getSequenceAt(i);
617           if(currentSeq.getDatasetSequence()!=null)
618           {
619             seqs[i] = (Sequence)currentSeq.getDatasetSequence();
620           }
621           else
622           {
623             seqs[i] = new Sequence(currentSeq.getName(),
624                                    AlignSeq.extractGaps(
625                                        jalview.util.Comparison.GapChars,
626                                        currentSeq.getSequenceAsString()
627                                    ),
628                                    currentSeq.getStart(),
629                                    currentSeq.getEnd());
630             seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
631             seqs[i].setDescription(currentSeq.getDescription());
632             getSequenceAt(i).setSequenceFeatures(null);
633             getSequenceAt(i).setDatasetSequence(seqs[i]);
634           }
635         }
636
637         dataset = new Alignment(seqs);
638       }
639       else if(dataset==null && data!=null)
640       {
641         dataset = data;
642       }
643     }
644
645     public Alignment getDataset()
646     {
647       return dataset;
648     }
649
650     public boolean padGaps()
651     {
652       boolean modified=false;
653
654       //Remove excess gaps from the end of alignment
655       int maxLength = -1;
656
657       SequenceI current;
658       for (int i = 0; i < sequences.size(); i++)
659       {
660         current = getSequenceAt(i);
661         for (int j = current.getLength(); j > maxLength; j--)
662         {
663           if (j > maxLength && !jalview.util.Comparison.isGap(
664               current.getCharAt(j)))
665           {
666             maxLength = j;
667             break;
668           }
669         }
670       }
671
672       maxLength++;
673
674       int cLength;
675       for (int i = 0; i < sequences.size();
676            i++)
677       {
678         current = getSequenceAt(i);
679         cLength = current.getLength();
680
681         if (cLength < maxLength)
682         {
683           current.insertCharAt(cLength,
684                               maxLength-cLength, gapCharacter);
685           modified=true;
686         }
687         else if(current.getLength() > maxLength)
688         {
689           current.deleteChars(maxLength, current.getLength());
690         }
691       }
692       return modified;
693     }
694
695     public HiddenSequences getHiddenSequences()
696     {
697       return hiddenSequences;
698     }
699
700   public CigarArray getCompactAlignment()
701   {
702     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
703     for (int i=0; i<sequences.size(); i++) {
704       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
705     }
706     CigarArray cal = new CigarArray(alseqs);
707     cal.addOperation(CigarArray.M, getWidth());
708     return cal;
709   }
710
711 }