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