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