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