Redundant Remove Redundancy Removed
[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(int i)\r
283     {\r
284         return findGroup(getSequenceAt(i));\r
285     }\r
286 \r
287     /**    */\r
288     public SequenceGroup findGroup(SequenceI s)\r
289     {\r
290         for (int i = 0; i < this.groups.size(); i++)\r
291         {\r
292             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);\r
293 \r
294             if (sg.sequences.contains(s))\r
295             {\r
296                 return sg;\r
297             }\r
298         }\r
299 \r
300         return null;\r
301     }\r
302 \r
303     /**\r
304      * DOCUMENT ME!\r
305      *\r
306      * @param s DOCUMENT ME!\r
307      *\r
308      * @return DOCUMENT ME!\r
309      */\r
310     public SequenceGroup[] findAllGroups(SequenceI s)\r
311     {\r
312         Vector temp = new Vector();\r
313 \r
314         int gSize = groups.size();\r
315         for (int i = 0; i < gSize; i++)\r
316         {\r
317             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);\r
318             if(sg==null || sg.sequences==null)\r
319             {\r
320               this.deleteGroup(sg);\r
321               gSize--;\r
322               continue;\r
323             }\r
324 \r
325             if (sg.sequences.contains(s))\r
326             {\r
327                 temp.addElement(sg);\r
328             }\r
329         }\r
330 \r
331         SequenceGroup[] ret = new SequenceGroup[temp.size()];\r
332 \r
333         for (int i = 0; i < temp.size(); i++)\r
334         {\r
335             ret[i] = (SequenceGroup) temp.elementAt(i);\r
336         }\r
337 \r
338         return ret;\r
339     }\r
340 \r
341 \r
342 \r
343     /**    */\r
344     public void addGroup(SequenceGroup sg)\r
345     {\r
346         if (!groups.contains(sg))\r
347         {\r
348             groups.addElement(sg);\r
349         }\r
350     }\r
351 \r
352     /**\r
353      * DOCUMENT ME!\r
354      */\r
355     public void deleteAllGroups()\r
356     {\r
357         groups.removeAllElements();\r
358 \r
359         int i = 0;\r
360 \r
361         while (i < sequences.size())\r
362         {\r
363             SequenceI s = getSequenceAt(i);\r
364             s.setColor(java.awt.Color.white);\r
365             i++;\r
366         }\r
367     }\r
368 \r
369     /**    */\r
370     public void deleteGroup(SequenceGroup g)\r
371     {\r
372         if (groups.contains(g))\r
373         {\r
374             groups.removeElement(g);\r
375         }\r
376     }\r
377 \r
378     /**    */\r
379     public SequenceI findName(String name)\r
380     {\r
381         int i = 0;\r
382 \r
383         while (i < sequences.size())\r
384         {\r
385             if (getSequenceAt(i).getName().equals(name))\r
386             {\r
387                 return getSequenceAt(i);\r
388             }\r
389 \r
390             i++;\r
391         }\r
392 \r
393         return null;\r
394     }\r
395 \r
396 \r
397     /**    */\r
398     public int findIndex(SequenceI s)\r
399     {\r
400         int i = 0;\r
401 \r
402         while (i < sequences.size())\r
403         {\r
404             if (s == getSequenceAt(i))\r
405             {\r
406                 return i;\r
407             }\r
408 \r
409             i++;\r
410         }\r
411 \r
412         return -1;\r
413     }\r
414 \r
415     /**\r
416      * DOCUMENT ME!\r
417      *\r
418      * @return DOCUMENT ME!\r
419      */\r
420     public int getHeight()\r
421     {\r
422         return sequences.size();\r
423     }\r
424 \r
425     /**\r
426      * DOCUMENT ME!\r
427      *\r
428      * @return DOCUMENT ME!\r
429      */\r
430     public int getWidth()\r
431     {\r
432         int maxLength = -1;\r
433 \r
434         for (int i = 0; i < sequences.size(); i++)\r
435         {\r
436             if (getSequenceAt(i).getLength() > maxLength)\r
437             {\r
438                 maxLength = getSequenceAt(i).getLength();\r
439             }\r
440         }\r
441 \r
442         return maxLength;\r
443     }\r
444 \r
445     /**\r
446      * DOCUMENT ME!\r
447      *\r
448      * @return DOCUMENT ME!\r
449      */\r
450     public int getMaxIdLength()\r
451     {\r
452         int max = 0;\r
453         int i = 0;\r
454 \r
455         while (i < sequences.size())\r
456         {\r
457             SequenceI seq = getSequenceAt(i);\r
458             String tmp = seq.getName() + "/" + seq.getStart() + "-" +\r
459                 seq.getEnd();\r
460 \r
461             if (tmp.length() > max)\r
462             {\r
463                 max = tmp.length();\r
464             }\r
465 \r
466             i++;\r
467         }\r
468 \r
469         return max;\r
470     }\r
471 \r
472     /**\r
473      * DOCUMENT ME!\r
474      *\r
475      * @param gc DOCUMENT ME!\r
476      */\r
477     public void setGapCharacter(char gc)\r
478     {\r
479         gapCharacter = gc;\r
480 \r
481         for (int i = 0; i < sequences.size(); i++)\r
482         {\r
483             Sequence seq = (Sequence) sequences.elementAt(i);\r
484             seq.sequence = seq.sequence.replace('.', gc);\r
485             seq.sequence = seq.sequence.replace('-', gc);\r
486             seq.sequence = seq.sequence.replace(' ', gc);\r
487         }\r
488     }\r
489 \r
490     /**\r
491      * DOCUMENT ME!\r
492      *\r
493      * @return DOCUMENT ME!\r
494      */\r
495     public char getGapCharacter()\r
496     {\r
497         return gapCharacter;\r
498     }\r
499 \r
500     /**\r
501      * DOCUMENT ME!\r
502      *\r
503      * @return DOCUMENT ME!\r
504      */\r
505     public Vector getAAFrequency()\r
506     {\r
507         return AAFrequency.calculate(sequences, 0, getWidth());\r
508     }\r
509 \r
510     /**\r
511      * DOCUMENT ME!\r
512      *\r
513      * @return DOCUMENT ME!\r
514      */\r
515     public boolean isAligned()\r
516     {\r
517         int width = getWidth();\r
518 \r
519         for (int i = 0; i < sequences.size(); i++)\r
520         {\r
521             if (getSequenceAt(i).getLength() != width)\r
522             {\r
523                 return false;\r
524             }\r
525         }\r
526 \r
527         return true;\r
528     }\r
529 \r
530     /**\r
531      * DOCUMENT ME!\r
532      *\r
533      * @param aa DOCUMENT ME!\r
534      */\r
535     public void deleteAnnotation(AlignmentAnnotation aa)\r
536     {\r
537         int aSize = 1;\r
538 \r
539         if (annotations != null)\r
540         {\r
541             aSize = annotations.length;\r
542         }\r
543 \r
544         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];\r
545 \r
546         int tIndex = 0;\r
547 \r
548         for (int i = 0; i < aSize; i++)\r
549         {\r
550             if (annotations[i] == aa)\r
551             {\r
552                 continue;\r
553             }\r
554 \r
555             temp[tIndex] = annotations[i];\r
556             tIndex++;\r
557         }\r
558 \r
559         annotations = temp;\r
560     }\r
561 \r
562 \r
563     public void adjustSequenceAnnotations()\r
564     {\r
565       if(annotations!=null)\r
566       {\r
567         for (int a = 0; a < annotations.length; a++)\r
568         {\r
569           if (annotations[a].sequenceRef != null)\r
570           {\r
571             annotations[a].adjustForAlignment();\r
572           }\r
573         }\r
574       }\r
575     }\r
576 \r
577     /**\r
578      * DOCUMENT ME!\r
579      *\r
580      * @param aa DOCUMENT ME!\r
581      */\r
582     public void addAnnotation(AlignmentAnnotation aa)\r
583     {\r
584         int aSize = 1;\r
585         if (annotations != null)\r
586         {\r
587             aSize = annotations.length + 1;\r
588         }\r
589 \r
590         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];\r
591 \r
592         temp[aSize-1] = aa;\r
593 \r
594         int i = 0;\r
595 \r
596         if (aSize > 1)\r
597         {\r
598             for (i = 0; i < (aSize-1); i++)\r
599             {\r
600                 temp[i] = annotations[i];\r
601             }\r
602         }\r
603 \r
604         annotations = temp;\r
605     }\r
606 \r
607     public void setAnnotationIndex(AlignmentAnnotation aa, int index)\r
608     {\r
609       if(aa==null || annotations==null || annotations.length-1<index)\r
610         return;\r
611 \r
612       int aSize = annotations.length;\r
613       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];\r
614 \r
615       temp[index] = aa;\r
616 \r
617       for (int i = 0; i < aSize; i++)\r
618       {\r
619         if(i==index)\r
620           continue;\r
621 \r
622         if(i<index)\r
623           temp[i] = annotations[i];\r
624         else\r
625           temp[i] = annotations[i-1];\r
626       }\r
627 \r
628         annotations = temp;\r
629     }\r
630 \r
631     /**\r
632      * DOCUMENT ME!\r
633      *\r
634      * @return DOCUMENT ME!\r
635      */\r
636     public AlignmentAnnotation[] getAlignmentAnnotation()\r
637     {\r
638         return annotations;\r
639     }\r
640 \r
641     public void setNucleotide(boolean b)\r
642     {\r
643       if(b)\r
644         type = NUCLEOTIDE;\r
645       else\r
646         type = PROTEIN;\r
647     }\r
648 \r
649     public boolean isNucleotide()\r
650     {\r
651       if(type==NUCLEOTIDE)\r
652         return true;\r
653       else\r
654         return false;\r
655     }\r
656 \r
657     public void setDataset(Alignment data)\r
658     {\r
659       if(dataset==null && data==null)\r
660       {\r
661         // Create a new dataset for this alignment.\r
662         // Can only be done once, if dataset is not null\r
663         // This will not be performed\r
664         Sequence[] seqs = new Sequence[getHeight()];\r
665         for (int i = 0; i < getHeight(); i++)\r
666         {\r
667 \r
668           seqs[i] = new Sequence(getSequenceAt(i).getName(),\r
669                                  AlignSeq.extractGaps(\r
670                                      jalview.util.Comparison.GapChars,\r
671                                      getSequenceAt(i).getSequence()\r
672                                  ),\r
673                                  getSequenceAt(i).getStart(),\r
674                                  getSequenceAt(i).getEnd());\r
675 \r
676           getSequenceAt(i).setDatasetSequence(seqs[i]);\r
677         }\r
678 \r
679         dataset = new Alignment(seqs);\r
680       }\r
681       else if(dataset==null && data!=null)\r
682       {\r
683         dataset = data;\r
684       }\r
685     }\r
686 \r
687     public Alignment getDataset()\r
688     {\r
689       return dataset;\r
690     }\r
691 \r
692     public boolean padGaps() {\r
693       boolean modified=false;\r
694 \r
695       //Remove excess gaps from the end of alignment\r
696       int maxLength = -1;\r
697 \r
698       SequenceI current;\r
699       for (int i = 0; i < sequences.size(); i++)\r
700       {\r
701         current = getSequenceAt(i);\r
702         for (int j = current.getLength(); j > maxLength; j--)\r
703         {\r
704           if (j > maxLength && !jalview.util.Comparison.isGap(\r
705               current.getCharAt(j)))\r
706           {\r
707             maxLength = j;\r
708             break;\r
709           }\r
710         }\r
711       }\r
712 \r
713       maxLength++;\r
714 \r
715       for (int i = 0; i < sequences.size();\r
716            i++)\r
717       {\r
718         current = getSequenceAt(i);\r
719 \r
720         if (current.getLength() < maxLength)\r
721         {\r
722           current.insertCharAt(maxLength - 1, gapCharacter);\r
723           modified=true;\r
724         }\r
725         else if(current.getLength() > maxLength)\r
726         {\r
727           current.deleteChars(maxLength, current.getLength());\r
728         }\r
729       }\r
730       return modified;\r
731     }\r
732 \r
733     public HiddenSequences getHiddenSequences()\r
734     {\r
735       return hiddenSequences;\r
736     }\r
737 \r
738 }\r