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