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