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