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