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