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