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