Send gapChar to print original Data
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2005 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
74       SequenceI[] seqs = new SequenceI[alseqs.length];
75       for (int i=0; i<alseqs.length; i++) {
76         seqs[i] = alseqs[i].getSeq(this.gapCharacter);
77       }
78       initAlignment(seqs);
79     }
80     /**
81      * Make a new alignment from an CigarArray
82      * JBPNote - can only do this when compactAlignment does not contain hidden regions.
83      * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
84      * @param compactAlignment CigarArray
85      */
86     public Alignment(CigarArray compactAlignment) {
87       throw new Error("Alignment(CigarArray) not yet implemented");
88       // this(compactAlignment.refCigars);
89     }
90
91     /**
92      * DOCUMENT ME!
93      *
94      * @return DOCUMENT ME!
95      */
96     public Vector getSequences()
97     {
98         return sequences;
99     }
100
101     public SequenceI [] getSequencesArray()
102     {
103       SequenceI [] reply = new SequenceI[sequences.size()];
104       for(int i=0; i<sequences.size(); i++)
105       {
106         reply[i] = (SequenceI)sequences.elementAt(i);
107       }
108       return reply;
109     }
110
111     /**
112      * DOCUMENT ME!
113      *
114      * @param i DOCUMENT ME!
115      *
116      * @return DOCUMENT ME!
117      */
118     public SequenceI getSequenceAt(int i)
119     {
120         if (i < sequences.size())
121         {
122             return (SequenceI) sequences.elementAt(i);
123         }
124
125         return null;
126     }
127
128     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
129      *
130      * @param snew
131      */
132     public void addSequence(SequenceI snew)
133     {
134       if(dataset!=null)
135       {
136         if(snew.getDatasetSequence()!=null)
137         {
138           System.out.println(snew.getName());
139           getDataset().addSequence(snew.getDatasetSequence());
140         }
141         else
142         {
143           Sequence ds = new Sequence(snew.getName(),
144                                      AlignSeq.extractGaps("-. ",
145               snew.getSequence()),
146                                      snew.getStart(),
147                                      snew.getEnd());
148
149           snew.setDatasetSequence(ds);
150           getDataset().addSequence(ds);
151         }
152       }
153
154       sequences.addElement(snew);
155     }
156
157
158     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
159      *
160      * @param snew
161      */
162     public void setSequenceAt(int i, SequenceI snew)
163     {
164         SequenceI oldseq = getSequenceAt(i);
165         deleteSequence(oldseq);
166
167         sequences.setElementAt(snew, i);
168     }
169
170     /**
171      * DOCUMENT ME!
172      *
173      * @return DOCUMENT ME!
174      */
175     public Vector getGroups()
176     {
177         return groups;
178     }
179
180     /** Takes out columns consisting entirely of gaps (-,.," ")
181      */
182     public void removeGaps()
183     {
184         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
185         int j, jSize = seqs.length;
186
187         int width = 0;
188         for (int i = 0; i < jSize; i++)
189         {
190           if (seqs[i].getLength() > width)
191           {
192             width = seqs[i].getLength();
193           }
194         }
195
196         int startCol = -1, endCol = -1;
197         boolean delete = true;
198         for (int i = 0; i < width; i++)
199         {
200             delete = true;
201
202             for (j = 0; j < jSize; j++)
203             {
204                 if (seqs[j].getLength() > i)
205                 {
206                     if (!jalview.util.Comparison.isGap(seqs[j].getCharAt(i)))
207                     {
208                         if(delete)
209                           endCol = i;
210
211                         delete = false;
212                         break;
213                     }
214                 }
215             }
216
217             if(delete && startCol==-1)
218             {
219               startCol = i;
220             }
221
222
223             if (!delete && startCol > -1)
224             {
225               deleteColumns(seqs, startCol, endCol);
226               width -= (endCol - startCol);
227               i -= (endCol - startCol);
228               startCol = -1;
229               endCol = -1;
230             }
231         }
232
233         if (delete && startCol > -1)
234         {
235           deleteColumns(seqs, startCol, endCol);
236         }
237     }
238
239     /** Removes a range of columns (start to end inclusive).
240      *
241      * @param seqs Sequences to remove columns from
242      * @param start Start column in the alignment
243      * @param end End column in the alignment
244      */
245     public void deleteColumns(SequenceI [] seqs, int start, int end)
246     {
247       for(int i=0; i<seqs.length; i++)
248         seqs[i].deleteChars(start, end);
249     }
250
251
252     /**
253      * DOCUMENT ME!
254      *
255      * @param i DOCUMENT ME!
256      */
257     public void trimLeft(int i)
258     {
259         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
260         int j, jSize = seqs.length;
261         for (j = 0; j < jSize; j++)
262         {
263             int newstart = seqs[j].findPosition(i);
264
265             if(i>seqs[j].getLength())
266             {
267               sequences.removeElement(seqs[j]);
268               j--;
269               jSize--;
270             }
271             else
272             {
273               seqs[j].setStart(newstart);
274               seqs[j].setSequence(seqs[j].getSequence().substring(i));
275             }
276         }
277     }
278
279     /**
280      * DOCUMENT ME!
281      *
282      * @param i DOCUMENT ME!
283      */
284     public void trimRight(int i)
285     {
286         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
287         int j, jSize = seqs.length;
288         for (j = 0; j < jSize; j++)
289         {
290             int newend = seqs[j].findPosition(i);
291
292             seqs[j].setEnd(newend);
293             if(seqs[j].getLength()>i)
294               seqs[j].setSequence(seqs[j].getSequence().substring(0, i + 1));
295         }
296     }
297
298     /**
299      * DOCUMENT ME!
300      *
301      * @param s DOCUMENT ME!
302      */
303     public void deleteSequence(SequenceI s)
304     {
305         for (int i = 0; i < getHeight(); i++)
306         {
307             if (getSequenceAt(i) == s)
308             {
309                 deleteSequence(i);
310             }
311         }
312     }
313
314     /**
315      * DOCUMENT ME!
316      *
317      * @param i DOCUMENT ME!
318      */
319     public void deleteSequence(int i)
320     {
321         sequences.removeElementAt(i);
322     }
323
324
325     /**    */
326     public SequenceGroup findGroup(SequenceI s)
327     {
328         for (int i = 0; i < this.groups.size(); i++)
329         {
330             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
331
332             if (sg.getSequences(false).contains(s))
333             {
334                 return sg;
335             }
336         }
337
338         return null;
339     }
340
341     /**
342      * DOCUMENT ME!
343      *
344      * @param s DOCUMENT ME!
345      *
346      * @return DOCUMENT ME!
347      */
348     public SequenceGroup[] findAllGroups(SequenceI s)
349     {
350         Vector temp = new Vector();
351
352         int gSize = groups.size();
353         for (int i = 0; i < gSize; i++)
354         {
355             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
356             if(sg==null || sg.getSequences(false)==null)
357             {
358               this.deleteGroup(sg);
359               gSize--;
360               continue;
361             }
362
363             if (sg.getSequences(false).contains(s))
364             {
365                 temp.addElement(sg);
366             }
367         }
368
369         SequenceGroup[] ret = new SequenceGroup[temp.size()];
370
371         for (int i = 0; i < temp.size(); i++)
372         {
373             ret[i] = (SequenceGroup) temp.elementAt(i);
374         }
375
376         return ret;
377     }
378
379
380
381     /**    */
382     public void addGroup(SequenceGroup sg)
383     {
384         if (!groups.contains(sg))
385         {
386             groups.addElement(sg);
387         }
388     }
389
390     /**
391      * DOCUMENT ME!
392      */
393     public void deleteAllGroups()
394     {
395         groups.removeAllElements();
396
397         int i = 0;
398
399         while (i < sequences.size())
400         {
401             SequenceI s = getSequenceAt(i);
402             s.setColor(java.awt.Color.white);
403             i++;
404         }
405     }
406
407     /**    */
408     public void deleteGroup(SequenceGroup g)
409     {
410         if (groups.contains(g))
411         {
412             groups.removeElement(g);
413         }
414     }
415
416     /**    */
417     public SequenceI findName(String name)
418     {
419         int i = 0;
420
421         while (i < sequences.size())
422         {
423             if (getSequenceAt(i).getName().equals(name))
424             {
425                 return getSequenceAt(i);
426             }
427
428             i++;
429         }
430
431         return null;
432     }
433
434
435     /**    */
436     public int findIndex(SequenceI s)
437     {
438         int i = 0;
439
440         while (i < sequences.size())
441         {
442             if (s == getSequenceAt(i))
443             {
444                 return i;
445             }
446
447             i++;
448         }
449
450         return -1;
451     }
452
453     /**
454      * DOCUMENT ME!
455      *
456      * @return DOCUMENT ME!
457      */
458     public int getHeight()
459     {
460         return sequences.size();
461     }
462
463     /**
464      * DOCUMENT ME!
465      *
466      * @return DOCUMENT ME!
467      */
468     public int getWidth()
469     {
470         int maxLength = -1;
471
472         for (int i = 0; i < sequences.size(); i++)
473         {
474             if (getSequenceAt(i).getLength() > maxLength)
475             {
476                 maxLength = getSequenceAt(i).getLength();
477             }
478         }
479
480         return maxLength;
481     }
482
483     /**
484      * DOCUMENT ME!
485      *
486      * @return DOCUMENT ME!
487      */
488     public int getMaxIdLength()
489     {
490         int max = 0;
491         int i = 0;
492
493         while (i < sequences.size())
494         {
495             SequenceI seq = getSequenceAt(i);
496             String tmp = seq.getName() + "/" + seq.getStart() + "-" +
497                 seq.getEnd();
498
499             if (tmp.length() > max)
500             {
501                 max = tmp.length();
502             }
503
504             i++;
505         }
506
507         return max;
508     }
509
510     /**
511      * DOCUMENT ME!
512      *
513      * @param gc DOCUMENT ME!
514      */
515     public void setGapCharacter(char gc)
516     {
517         gapCharacter = gc;
518
519         for (int i = 0; i < sequences.size(); i++)
520         {
521             Sequence seq = (Sequence) sequences.elementAt(i);
522             seq.setSequence( seq.getSequence().replace('.', gc) );
523             seq.setSequence( seq.getSequence().replace('-', gc) );
524             seq.setSequence( seq.getSequence().replace(' ', gc) );
525         }
526     }
527
528     /**
529      * DOCUMENT ME!
530      *
531      * @return DOCUMENT ME!
532      */
533     public char getGapCharacter()
534     {
535         return gapCharacter;
536     }
537
538     /**
539      * DOCUMENT ME!
540      *
541      * @return DOCUMENT ME!
542      */
543     public Vector getAAFrequency()
544     {
545         return AAFrequency.calculate(sequences, 0, getWidth());
546     }
547
548     /**
549      * DOCUMENT ME!
550      *
551      * @return DOCUMENT ME!
552      */
553     public boolean isAligned()
554     {
555         int width = getWidth();
556
557         for (int i = 0; i < sequences.size(); i++)
558         {
559             if (getSequenceAt(i).getLength() != width)
560             {
561                 return false;
562             }
563         }
564
565         return true;
566     }
567
568     /**
569      * DOCUMENT ME!
570      *
571      * @param aa DOCUMENT ME!
572      */
573     public void deleteAnnotation(AlignmentAnnotation aa)
574     {
575         int aSize = 1;
576
577         if (annotations != null)
578         {
579             aSize = annotations.length;
580         }
581
582         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
583
584         int tIndex = 0;
585
586         for (int i = 0; i < aSize; i++)
587         {
588             if (annotations[i] == aa)
589             {
590                 continue;
591             }
592
593             temp[tIndex] = annotations[i];
594             tIndex++;
595         }
596
597         annotations = temp;
598     }
599
600
601     public void adjustSequenceAnnotations()
602     {
603       if(annotations!=null)
604       {
605         for (int a = 0; a < annotations.length; a++)
606         {
607           if (annotations[a].sequenceRef != null)
608           {
609             annotations[a].adjustForAlignment();
610           }
611         }
612       }
613     }
614
615     /**
616      * DOCUMENT ME!
617      *
618      * @param aa DOCUMENT ME!
619      */
620     public void addAnnotation(AlignmentAnnotation aa)
621     {
622         int aSize = 1;
623         if (annotations != null)
624         {
625             aSize = annotations.length + 1;
626         }
627
628         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
629
630         temp[aSize-1] = aa;
631
632         int i = 0;
633
634         if (aSize > 1)
635         {
636             for (i = 0; i < (aSize-1); i++)
637             {
638                 temp[i] = annotations[i];
639             }
640         }
641
642         annotations = temp;
643     }
644
645     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
646     {
647       if(aa==null || annotations==null || annotations.length-1<index)
648         return;
649
650       int aSize = annotations.length;
651       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
652
653       temp[index] = aa;
654
655       for (int i = 0; i < aSize; i++)
656       {
657         if(i==index)
658           continue;
659
660         if(i<index)
661           temp[i] = annotations[i];
662         else
663           temp[i] = annotations[i-1];
664       }
665
666         annotations = temp;
667     }
668
669     /**
670      * DOCUMENT ME!
671      *
672      * @return DOCUMENT ME!
673      */
674     public AlignmentAnnotation[] getAlignmentAnnotation()
675     {
676         return annotations;
677     }
678
679     public void setNucleotide(boolean b)
680     {
681       if(b)
682         type = NUCLEOTIDE;
683       else
684         type = PROTEIN;
685     }
686
687     public boolean isNucleotide()
688     {
689       if(type==NUCLEOTIDE)
690         return true;
691       else
692         return false;
693     }
694
695     public void setDataset(Alignment data)
696     {
697       if(dataset==null && data==null)
698       {
699         // Create a new dataset for this alignment.
700         // Can only be done once, if dataset is not null
701         // This will not be performed
702         Sequence[] seqs = new Sequence[getHeight()];
703         for (int i = 0; i < getHeight(); i++)
704         {
705           if(getSequenceAt(i).getDatasetSequence()!=null)
706           {
707             seqs[i] = (Sequence)getSequenceAt(i).getDatasetSequence();
708           }
709           else
710           {
711             seqs[i] = new Sequence(getSequenceAt(i).getName(),
712                                    AlignSeq.extractGaps(
713                                        jalview.util.Comparison.GapChars,
714                                        getSequenceAt(i).getSequence()
715                                    ),
716                                    getSequenceAt(i).getStart(),
717                                    getSequenceAt(i).getEnd());
718
719             getSequenceAt(i).setDatasetSequence(seqs[i]);
720           }
721         }
722
723         dataset = new Alignment(seqs);
724       }
725       else if(dataset==null && data!=null)
726       {
727         dataset = data;
728       }
729     }
730
731     public Alignment getDataset()
732     {
733       return dataset;
734     }
735
736     public boolean padGaps() {
737       boolean modified=false;
738
739       //Remove excess gaps from the end of alignment
740       int maxLength = -1;
741
742       SequenceI current;
743       for (int i = 0; i < sequences.size(); i++)
744       {
745         current = getSequenceAt(i);
746         for (int j = current.getLength(); j > maxLength; j--)
747         {
748           if (j > maxLength && !jalview.util.Comparison.isGap(
749               current.getCharAt(j)))
750           {
751             maxLength = j;
752             break;
753           }
754         }
755       }
756
757       maxLength++;
758
759       for (int i = 0; i < sequences.size();
760            i++)
761       {
762         current = getSequenceAt(i);
763
764         if (current.getLength() < maxLength)
765         {
766           current.insertCharAt(maxLength - 1, gapCharacter);
767           modified=true;
768         }
769         else if(current.getLength() > maxLength)
770         {
771           current.deleteChars(maxLength, current.getLength());
772         }
773       }
774       return modified;
775     }
776
777     public HiddenSequences getHiddenSequences()
778     {
779       return hiddenSequences;
780     }
781     SequenceI [] getVisibleAndRepresentedSeqs()
782     {
783       if(hiddenSequences==null || hiddenSequences.getSize()<1)
784         return getSequencesArray();
785
786       Vector seqs = new Vector();
787       SequenceI seq;
788       SequenceGroup hidden;
789       for (int i = 0; i < sequences.size(); i++)
790       {
791         seq = (SequenceI) sequences.elementAt(i);
792         seqs.addElement(seq);
793         hidden = seq.getHiddenSequences();
794         if(hidden!=null)
795         {
796           for(int j=0; j<hidden.getSize(false); j++)
797           {
798             seqs.addElement(hidden.getSequenceAt(j));
799           }
800         }
801       }
802       SequenceI [] result = new SequenceI[seqs.size()];
803       for(int i=0; i<seqs.size(); i++)
804         result[i] = (SequenceI)seqs.elementAt(i);
805
806       return result;
807
808     }
809
810   public CigarArray getCompactAlignment()
811   {
812     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
813     for (int i=0; i<sequences.size(); i++) {
814       alseqs[i] = new SeqCigar((SequenceI) sequences.get(i));
815     }
816     return new CigarArray(alseqs);
817   }
818
819 }