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