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