remove gaps method from AlignFrame.remove_gaps_actionPerformed
[jalview.git] / src / jalview / datamodel / Sequence.java
1 /*
2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18 */
19 package jalview.datamodel;
20
21 import java.awt.*;
22
23 import java.util.*;
24
25
26 /**
27  * DOCUMENT ME!
28  *
29  * @author $author$
30  * @version $Revision$
31  */
32 public class Sequence implements SequenceI
33 {
34     SequenceI datasetSequence;
35     String name;
36     private String sequence;
37     String description;
38     int start;
39     int end;
40     Color color = Color.white;
41     Vector pdbIds;
42     String vamsasId;
43     DBRefEntry [] dbrefs;
44
45     /** This annotation is displayed below the alignment but the
46      * positions are tied to the residues of this sequence */
47     Vector annotation;
48
49     /** DOCUMENT ME!! */
50     public SequenceFeature [] sequenceFeatures;
51
52     /** This array holds hidden sequences
53      * of which this sequence is the representitive member of a group
54      */
55     SequenceGroup hiddenSequences;
56
57     /**
58      * Creates a new Sequence object.
59      *
60      * @param name DOCUMENT ME!
61      * @param sequence DOCUMENT ME!
62      * @param start DOCUMENT ME!
63      * @param end DOCUMENT ME!
64      */
65     public Sequence(String name, String sequence, int start, int end)
66     {
67       this.name = name;
68       this.sequence = sequence;
69       this.start = start;
70       this.end = end;
71
72       parseId();
73
74       checkValidRange();
75     }
76
77     com.stevesoft.pat.Regex limitrx = new com.stevesoft.pat.Regex(
78                         "[/][0-9]{1,}[-][0-9]{1,}$");
79     com.stevesoft.pat.Regex endrx = new com.stevesoft.pat.Regex(
80                         "[0-9]{1,}$");
81
82     void parseId()
83     {
84         // Does sequence have the /start-end signiature?
85          if(limitrx.search(name))
86          {
87             name = limitrx.left();
88             endrx.search(limitrx.stringMatched());
89             setStart( Integer.parseInt( limitrx.stringMatched().substring(1,endrx.matchedFrom()-1 )));
90             setEnd(   Integer.parseInt( endrx.stringMatched() ));
91          }
92     }
93
94     void checkValidRange()
95     {
96       if (end < 1)
97       {
98         int endRes = 0;
99         char ch;
100         for (int j = 0; j < sequence.length(); j++)
101         {
102           ch = sequence.charAt(j);
103           if (!jalview.util.Comparison.isGap( (ch)))
104           {
105             endRes++;
106           }
107         }
108         if (endRes > 0)
109         {
110           endRes += start - 1;
111         }
112
113         this.end = endRes;
114       }
115
116     }
117
118     /**
119      * Creates a new Sequence object.
120      *
121      * @param name DOCUMENT ME!
122      * @param sequence DOCUMENT ME!
123      */
124     public Sequence(String name, String sequence)
125     {
126         this(name, sequence, 1, -1);
127     }
128
129     /**
130      * Creates a new Sequence object.
131      *
132      * @param seq DOCUMENT ME!
133      */
134     public Sequence(SequenceI seq)
135     {
136         this(seq.getName(), seq.getSequence(), seq.getStart(), seq.getEnd());
137     }
138
139     /**
140      * DOCUMENT ME!
141      *
142      * @param v DOCUMENT ME!
143      */
144     public void setSequenceFeatures(SequenceFeature [] features)
145     {
146         sequenceFeatures = features;
147     }
148
149     public synchronized void addSequenceFeature(SequenceFeature sf)
150     {
151       if(sequenceFeatures==null)
152       {
153         sequenceFeatures = new SequenceFeature[0];
154       }
155
156       for(int i=0; i<sequenceFeatures.length; i++)
157       {
158         if(sequenceFeatures[i].equals(sf))
159         {
160           return;
161         }
162       }
163
164       SequenceFeature [] temp = new SequenceFeature[sequenceFeatures.length+1];
165       System.arraycopy(sequenceFeatures, 0, temp, 0, sequenceFeatures.length);
166       temp[sequenceFeatures.length] = sf;
167
168       sequenceFeatures = temp;
169     }
170
171
172     /**
173      * DOCUMENT ME!
174      *
175      * @return DOCUMENT ME!
176      */
177     public SequenceFeature [] getSequenceFeatures()
178     {
179         return sequenceFeatures;
180     }
181
182     public void addPDBId(PDBEntry entry)
183     {
184       if(pdbIds == null)
185         pdbIds = new Vector();
186
187       pdbIds.addElement(entry);
188     }
189
190     /**
191      * DOCUMENT ME!
192      *
193      * @param id DOCUMENT ME!
194      */
195     public void setPDBId(Vector id)
196     {
197         pdbIds = id;
198     }
199
200     /**
201      * DOCUMENT ME!
202      *
203      * @return DOCUMENT ME!
204      */
205     public Vector getPDBId()
206     {
207         return pdbIds;
208     }
209
210     /**
211      * DOCUMENT ME!
212      *
213      * @return DOCUMENT ME!
214      */
215     public String getDisplayId(boolean jvsuffix)
216     {
217       StringBuffer result = new StringBuffer(name);
218       if (jvsuffix)
219       {
220         result.append("/" + start + "-" + end);
221       }
222
223       return result.toString();
224     }
225
226     /**
227      * DOCUMENT ME!
228      *
229      * @param name DOCUMENT ME!
230      */
231     public void setName(String name)
232     {
233       this.name = name;
234       this.parseId();
235     }
236
237     /**
238      * DOCUMENT ME!
239      *
240      * @return DOCUMENT ME!
241      */
242     public String getName()
243     {
244        return this.name;
245     }
246
247     /**
248      * DOCUMENT ME!
249      *
250      * @param start DOCUMENT ME!
251      */
252     public void setStart(int start)
253     {
254         this.start = start;
255     }
256
257     /**
258      * DOCUMENT ME!
259      *
260      * @return DOCUMENT ME!
261      */
262     public int getStart()
263     {
264         return this.start;
265     }
266
267     /**
268      * DOCUMENT ME!
269      *
270      * @param end DOCUMENT ME!
271      */
272     public void setEnd(int end)
273     {
274         this.end = end;
275     }
276
277     /**
278      * DOCUMENT ME!
279      *
280      * @return DOCUMENT ME!
281      */
282     public int getEnd()
283     {
284         return this.end;
285     }
286
287     /**
288      * DOCUMENT ME!
289      *
290      * @return DOCUMENT ME!
291      */
292     public int getLength()
293     {
294         return this.sequence.length();
295     }
296
297     /**
298      * DOCUMENT ME!
299      *
300      * @param seq DOCUMENT ME!
301      */
302     public void setSequence(String seq)
303     {
304         this.sequence = seq;
305         checkValidRange();
306     }
307
308     /**
309      * DOCUMENT ME!
310      *
311      * @return DOCUMENT ME!
312      */
313     public String getSequence()
314     {
315         return this.sequence;
316     }
317
318     /**
319      * DOCUMENT ME!
320      *
321      * @param start DOCUMENT ME!
322      * @param end DOCUMENT ME!
323      *
324      * @return DOCUMENT ME!
325      */
326     public String getSequence(int start, int end)
327     {
328         // JBPNote - left to user to pad the result here (TODO:Decide on this policy)
329         if (start >= sequence.length())
330         {
331             return "";
332         }
333
334         if (end >= sequence.length())
335         {
336             end = sequence.length();
337         }
338
339         return this.sequence.substring(start, end);
340     }
341     /**
342      * make a new Sequence object from start to end (including gaps) over this seqeunce
343      * @param start int
344      * @param end int
345      * @return SequenceI
346      */
347     public SequenceI getSubSequence(int start, int end) {
348       if (start<0)
349         start = 0;
350       String seq = getSequence(start, end);
351       if (seq=="")
352         return null;
353       int nstart = findPosition(start);
354       int nend=findPosition(end)-1;
355       // JBPNote - this is an incomplete copy.
356       SequenceI nseq = new Sequence(this.getName(), seq, nstart, nend);
357       nseq.setDatasetSequence(getDatasetSequence());
358       return nseq;
359     }
360     /**
361      * DOCUMENT ME!
362      *
363      * @param i DOCUMENT ME!
364      *
365      * @return DOCUMENT ME!
366      */
367     public char getCharAt(int i)
368     {
369         if (i < sequence.length())
370         {
371             return sequence.charAt(i);
372         }
373         else
374         {
375             return ' ';
376         }
377     }
378
379     /**
380      * DOCUMENT ME!
381      *
382      * @param desc DOCUMENT ME!
383      */
384     public void setDescription(String desc)
385     {
386         this.description = desc;
387     }
388
389     /**
390      * DOCUMENT ME!
391      *
392      * @return DOCUMENT ME!
393      */
394     public String getDescription()
395     {
396         return this.description;
397     }
398
399     /**
400      * DOCUMENT ME!
401      *
402      * @param pos DOCUMENT ME!
403      *
404      * @return DOCUMENT ME!
405      */
406     public int findIndex(int pos)
407     {
408         // returns the alignment position for a residue
409         int j = start;
410         int i = 0;
411
412         while ((i < sequence.length()) && (j <= end) && (j <= pos))
413         {
414             if (!jalview.util.Comparison.isGap(sequence.charAt(i)))
415             {
416                 j++;
417             }
418
419             i++;
420         }
421
422         if ((j == end) && (j < pos))
423         {
424             return end + 1;
425         }
426         else
427         {
428             return i;
429         }
430     }
431
432     /**
433      * Returns the sequence position for an alignment position
434      *
435      * @param i column index in alignment (from 1)
436      *
437      * @return residue number for residue (left of and) nearest ith column
438      */
439     public int findPosition(int i)
440     {
441         int j = 0;
442         int pos = start;
443         int seqlen=sequence.length();
444         while ((j < i) && (j < seqlen))
445         {
446             if (!jalview.util.Comparison.isGap((sequence.charAt(j))))
447             {
448                 pos++;
449             }
450
451             j++;
452         }
453
454         return pos;
455     }
456
457     /**
458      * DOCUMENT ME!
459      *
460      * @return DOCUMENT ME!
461      */
462     public int[] gapMap()
463     {
464         // Returns an int array giving the position of each residue in the sequence in the alignment
465         String seq = jalview.analysis.AlignSeq.extractGaps(jalview.util.Comparison.GapChars, sequence);
466         int[] map = new int[seq.length()];
467         int j = 0;
468         int p = 0;
469
470         while (j < sequence.length())
471         {
472             if (!jalview.util.Comparison.isGap(sequence.charAt(j)))
473             {
474                 map[p++] = j;
475             }
476
477             j++;
478         }
479
480         return map;
481     }
482
483     /**
484      * DOCUMENT ME!
485      *
486      * @param i DOCUMENT ME!
487      */
488     public void deleteCharAt(int i)
489     {
490         if (i >= sequence.length())
491         {
492             return;
493         }
494
495         sequence = sequence.substring(0, i) + sequence.substring(i + 1);
496     }
497
498     /**
499      * DOCUMENT ME!
500      *
501      * @param i DOCUMENT ME!
502      * @param j DOCUMENT ME!
503      */
504     public void deleteChars(int i, int j)
505     {
506         if (i >= sequence.length())
507         {
508             return;
509         }
510
511         if (j >= sequence.length())
512         {
513             sequence = sequence.substring(0, i);
514         }
515         else
516         {
517             sequence = sequence.substring(0, i) + sequence.substring(j);
518         }
519     }
520
521
522     /**
523      * DOCUMENT ME!
524      *
525      * @param i DOCUMENT ME!
526      * @param c DOCUMENT ME!
527      * @param chop DOCUMENT ME!
528      */
529     public void insertCharAt(int i, char c)
530     {
531         String tmp = new String(sequence);
532
533         if (i < sequence.length())
534         {
535             sequence = tmp.substring(0, i) + String.valueOf(c) +
536                 tmp.substring(i);
537         }
538         else
539         {
540             // JBPNote : padding char at end of sequence. We'll not get away with this when we insert residues, I bet!
541             char[] ch = new char[(1 + i) - sequence.length()];
542
543             for (int j = 0, k = ch.length; j < k; j++)
544                 ch[j] = c;
545
546             sequence = tmp + String.valueOf(ch);
547         }
548     }
549
550     /**
551      * DOCUMENT ME!
552      *
553      * @param c DOCUMENT ME!
554      */
555     public void setColor(Color c)
556     {
557         this.color = c;
558     }
559
560     /**
561      * DOCUMENT ME!
562      *
563      * @return DOCUMENT ME!
564      */
565     public Color getColor()
566     {
567         return color;
568     }
569
570     public String getVamsasId()
571     {
572       return vamsasId;
573     }
574
575     public void setVamsasId(String id)
576     {
577       vamsasId = id;
578     }
579
580     public void setDBRef(DBRefEntry [] dbref)
581     {
582       dbrefs = dbref;
583     }
584
585     public DBRefEntry [] getDBRef()
586     {
587       return dbrefs;
588     }
589
590     public void addDBRef(DBRefEntry entry)
591     {
592       if(dbrefs == null)
593         dbrefs = new DBRefEntry[0];
594
595       DBRefEntry [] temp = new DBRefEntry[dbrefs.length+1];
596       System.arraycopy(dbrefs, 0, temp, 0, dbrefs.length);
597
598       temp[temp.length-1] = entry;
599
600       dbrefs = temp;
601     }
602
603     public void setDatasetSequence(SequenceI seq)
604     {
605       datasetSequence = seq;
606     }
607
608     public SequenceI getDatasetSequence()
609     {
610       return datasetSequence;
611     }
612
613     public AlignmentAnnotation [] getAnnotation()
614     {
615       if(annotation==null)
616         return null;
617
618       AlignmentAnnotation [] ret = new AlignmentAnnotation[annotation.size()];
619       for(int r = 0; r<ret.length; r++)
620         ret[r] = (AlignmentAnnotation)annotation.elementAt(r);
621
622       return ret;
623     }
624
625     public void addAlignmentAnnotation(AlignmentAnnotation annotation)
626     {
627       if(this.annotation==null)
628         this.annotation = new Vector();
629
630       this.annotation.addElement( annotation );
631     }
632
633     public SequenceGroup getHiddenSequences()
634     {
635       return hiddenSequences;
636     }
637
638     public void addHiddenSequence(SequenceI seq)
639     {
640       if(hiddenSequences==null)
641       {
642         hiddenSequences = new SequenceGroup();
643       }
644       hiddenSequences.addSequence(seq, false);
645     }
646
647     public void showHiddenSequence(SequenceI seq)
648     {
649       hiddenSequences.deleteSequence(seq, false);
650       if (hiddenSequences.getSize(false) < 1)
651       {
652         hiddenSequences = null;
653       }
654     }
655
656     public void changeCase(boolean toUpper, int start, int end)
657     {
658       StringBuffer newSeq = new StringBuffer();
659
660       if(end>sequence.length())
661         end = sequence.length();
662
663       if (start > 0)
664       {
665         newSeq.append(sequence.substring(0, start));
666       }
667
668       if (toUpper)
669         newSeq.append(sequence.substring(start, end).toUpperCase());
670       else
671         newSeq.append(sequence.substring(start, end).toLowerCase());
672
673       if (end < sequence.length())
674         newSeq.append(sequence.substring(end));
675
676       sequence = newSeq.toString();
677     }
678
679     public void toggleCase(int start, int end)
680     {
681       StringBuffer newSeq = new StringBuffer();
682
683      if(end>sequence.length())
684        end = sequence.length();
685
686      if (start > 0)
687      {
688        newSeq.append(sequence.substring(0, start));
689      }
690
691      char nextChar;
692      for(int c=start; c<end; c++)
693      {
694        nextChar = sequence.charAt(c);
695        if(Character.isLetter(nextChar))
696        {
697          if(Character.isUpperCase(nextChar))
698            nextChar = Character.toLowerCase(nextChar);
699          else
700            nextChar = Character.toUpperCase(nextChar);
701        }
702
703
704        newSeq.append(nextChar);
705      }
706
707      if (end < sequence.length())
708        newSeq.append(sequence.substring(end));
709
710      sequence = newSeq.toString();
711     }
712
713   public SequenceI getSubSequence(int start)
714   {
715     int e=getLength();
716     if (start>=e)
717       return null;
718     return getSubSequence(start, getLength());
719   }
720
721   public int removeGaps() {
722     if (sequence!=null)
723       return removeGaps(0, getLength());
724     return 0;
725   }
726
727   public int removeGaps(int start, int end) {
728     int jSize = getLength();
729     int oSize=jSize;
730     if (jSize<=start)
731       return 0;
732     if (end>jSize)
733       end = jSize;
734
735     // Removing a range is much quicker than removing gaps
736     // one by one for long sequences
737     int j = start;
738     int rangeStart=-1, rangeEnd=-1;
739
740     do
741     {
742       if (jalview.util.Comparison.isGap(getCharAt(j)))
743       {
744         if(rangeStart==-1)
745          {
746            rangeStart = j;
747            rangeEnd = j+1;
748          }
749          else
750          {
751            rangeEnd++;
752          }
753          j++;
754       }
755       else
756       {
757         if(rangeStart>-1)
758         {
759           deleteChars(rangeStart, rangeEnd);
760           j-=rangeEnd-rangeStart;
761           jSize-=rangeEnd-rangeStart;
762           rangeStart = -1;
763           rangeEnd = -1;
764         }
765         else
766           j++;
767       }
768     }
769     while (j < end && j < jSize);
770     if(rangeStart>-1)
771     {
772      deleteChars(rangeStart, rangeEnd);
773      jSize-=rangeEnd-rangeStart;
774     }
775     return oSize-jSize; // number of deleted characters.
776   }
777
778 }