get annotation by label string
[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 with new features, DBRefEntries, AlignmentAnnotations, and PDBIds
134    * but inherits any existing dataset sequence reference.
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     if (seq.getSequenceFeatures()!=null) {
145       SequenceFeature[] sf = seq.getSequenceFeatures();
146       for (int i=0; i<sf.length; i++) {
147         addSequenceFeature(new SequenceFeature(sf[i]));
148       }
149     }
150     if (seq.getDBRef()!=null) {
151       DBRefEntry[] dbr = seq.getDBRef();
152       for (int i=0; i<dbr.length; i++) {
153         addDBRef(new DBRefEntry(dbr[i]));
154       }
155     }
156     setDatasetSequence(seq.getDatasetSequence());
157     if (seq.getAnnotation()!=null) {
158       AlignmentAnnotation[] sqann = seq.getAnnotation();
159       for (int i=0;i<sqann.length; i++)
160       {
161         AlignmentAnnotation newann = new AlignmentAnnotation(sqann[i]);
162         addAlignmentAnnotation(newann);
163       }
164     }
165     if (seq.getPDBId()!=null) {
166       Vector ids = seq.getPDBId();
167       Enumeration e = ids.elements();
168       while (e.hasMoreElements()) {
169         this.addPDBId(new PDBEntry((PDBEntry) e.nextElement()));
170       }
171     }
172   }
173
174
175   /**
176    * DOCUMENT ME!
177    *
178    * @param v DOCUMENT ME!
179    */
180   public void setSequenceFeatures(SequenceFeature[] features)
181   {
182     sequenceFeatures = features;
183   }
184
185   public synchronized void addSequenceFeature(SequenceFeature sf)
186   {
187     if (sequenceFeatures == null)
188     {
189       sequenceFeatures = new SequenceFeature[0];
190     }
191
192     for (int i = 0; i < sequenceFeatures.length; i++)
193     {
194       if (sequenceFeatures[i].equals(sf))
195       {
196         return;
197       }
198     }
199
200     SequenceFeature[] temp = new SequenceFeature[sequenceFeatures.length + 1];
201     System.arraycopy(sequenceFeatures, 0, temp, 0, sequenceFeatures.length);
202     temp[sequenceFeatures.length] = sf;
203
204     sequenceFeatures = temp;
205   }
206
207   public void deleteFeature(SequenceFeature sf)
208   {
209     if(sequenceFeatures==null)
210     {
211       return;
212     }
213
214     int index=0;
215     for (index = 0; index < sequenceFeatures.length; index++)
216     {
217       if (sequenceFeatures[index].equals(sf))
218       {
219         break;
220       }
221     }
222
223
224     if(index==sequenceFeatures.length)
225     {
226       return;
227     }
228
229     int sfLength = sequenceFeatures.length;
230     if(sfLength<2)
231     {
232       sequenceFeatures = null;
233     }
234     else
235     {
236       SequenceFeature[] temp = new SequenceFeature[sfLength-1];
237       System.arraycopy(sequenceFeatures, 0, temp, 0, index);
238
239       if(index<sfLength)
240       {
241         System.arraycopy(sequenceFeatures,
242                          index + 1,
243                          temp,
244                          index, sequenceFeatures.length - index -1);
245       }
246
247       sequenceFeatures = temp;
248     }
249   }
250
251   /**
252    * DOCUMENT ME!
253    *
254    * @return DOCUMENT ME!
255    */
256   public SequenceFeature[] getSequenceFeatures()
257   {
258     return sequenceFeatures;
259   }
260
261   public void addPDBId(PDBEntry entry)
262   {
263     if (pdbIds == null)
264     {
265       pdbIds = new Vector();
266     }
267
268     pdbIds.addElement(entry);
269   }
270
271   /**
272    * DOCUMENT ME!
273    *
274    * @param id DOCUMENT ME!
275    */
276   public void setPDBId(Vector id)
277   {
278     pdbIds = id;
279   }
280
281   /**
282    * DOCUMENT ME!
283    *
284    * @return DOCUMENT ME!
285    */
286   public Vector getPDBId()
287   {
288     return pdbIds;
289   }
290
291   /**
292    * DOCUMENT ME!
293    *
294    * @return DOCUMENT ME!
295    */
296   public String getDisplayId(boolean jvsuffix)
297   {
298     StringBuffer result = new StringBuffer(name);
299     if (jvsuffix)
300     {
301       result.append("/" + start + "-" + end);
302     }
303
304     return result.toString();
305   }
306
307   /**
308    * DOCUMENT ME!
309    *
310    * @param name DOCUMENT ME!
311    */
312   public void setName(String name)
313   {
314     this.name = name;
315     this.parseId();
316   }
317
318   /**
319    * DOCUMENT ME!
320    *
321    * @return DOCUMENT ME!
322    */
323   public String getName()
324   {
325     return this.name;
326   }
327
328   /**
329    * DOCUMENT ME!
330    *
331    * @param start DOCUMENT ME!
332    */
333   public void setStart(int start)
334   {
335     this.start = start;
336   }
337
338   /**
339    * DOCUMENT ME!
340    *
341    * @return DOCUMENT ME!
342    */
343   public int getStart()
344   {
345     return this.start;
346   }
347
348   /**
349    * DOCUMENT ME!
350    *
351    * @param end DOCUMENT ME!
352    */
353   public void setEnd(int end)
354   {
355     this.end = end;
356   }
357
358   /**
359    * DOCUMENT ME!
360    *
361    * @return DOCUMENT ME!
362    */
363   public int getEnd()
364   {
365     return this.end;
366   }
367
368   /**
369    * DOCUMENT ME!
370    *
371    * @return DOCUMENT ME!
372    */
373   public int getLength()
374   {
375     return this.sequence.length;
376   }
377
378   /**
379    * DOCUMENT ME!
380    *
381    * @param seq DOCUMENT ME!
382    */
383   public void setSequence(String seq)
384   {
385     this.sequence = seq.toCharArray();
386     checkValidRange();
387   }
388
389
390   public String getSequenceAsString()
391   {
392     return new String(sequence);
393   }
394
395   public String getSequenceAsString(int start, int end)
396   {
397     return new String(getSequence(start, end));
398   }
399
400
401   public char [] getSequence()
402   {
403     return sequence;
404   }
405
406   /**
407    * DOCUMENT ME!
408    *
409    * @param start DOCUMENT ME!
410    * @param end DOCUMENT ME!
411    *
412    * @return DOCUMENT ME!
413    */
414   public char [] getSequence(int start, int end)
415   {
416     // JBPNote - left to user to pad the result here (TODO:Decide on this policy)
417     if (start >= sequence.length)
418     {
419       return new char[0];
420     }
421
422     if (end >= sequence.length)
423     {
424       end = sequence.length;
425     }
426
427     char [] reply = new char[end-start];
428     System.arraycopy(sequence, start, reply, 0, end-start);
429
430     return reply;
431   }
432
433
434   /**
435    * make a new Sequence object from start to end (including gaps) over this seqeunce
436    * @param start int
437    * @param end int
438    * @return SequenceI
439    */
440   public SequenceI getSubSequence(int start, int end)
441   {
442     if (start < 0)
443     {
444       start = 0;
445     }
446     char [] seq = getSequence(start, end);
447     if (seq.length == 0)
448     {
449       return null;
450     }
451     int nstart = findPosition(start);
452     int nend = findPosition(end) - 1;
453     // JBPNote - this is an incomplete copy.
454     SequenceI nseq = new Sequence(this.getName(), seq, nstart, nend);
455     nseq.setDescription(description);
456     if (datasetSequence!=null)
457     {
458         nseq.setDatasetSequence(datasetSequence);
459     }
460     else
461     {
462         nseq.setDatasetSequence(this);
463     }
464     return nseq;
465   }
466
467   /**
468    * DOCUMENT ME!
469    *
470    * @param i DOCUMENT ME!
471    *
472    * @return DOCUMENT ME!
473    */
474   public char getCharAt(int i)
475   {
476     if (i < sequence.length)
477     {
478       return sequence[i];
479     }
480     else
481     {
482       return ' ';
483     }
484   }
485
486   /**
487    * DOCUMENT ME!
488    *
489    * @param desc DOCUMENT ME!
490    */
491   public void setDescription(String desc)
492   {
493     this.description = desc;
494   }
495
496   /**
497    * DOCUMENT ME!
498    *
499    * @return DOCUMENT ME!
500    */
501   public String getDescription()
502   {
503     return this.description;
504   }
505
506   /**
507    * Return the alignment position for a sequence position
508    *
509    * @param pos lying from start to end
510    *
511    * @return aligned position of residue pos
512    */
513   public int findIndex(int pos)
514   {
515     // returns the alignment position for a residue
516     int j = start;
517     int i = 0;
518
519     while ( (i < sequence.length) && (j <= end) && (j <= pos))
520     {
521       if (!jalview.util.Comparison.isGap(sequence[i]))
522       {
523         j++;
524       }
525
526       i++;
527     }
528
529     if ( (j == end) && (j < pos))
530     {
531       return end + 1;
532     }
533     else
534     {
535       return i;
536     }
537   }
538
539   /**
540    * Returns the sequence position for an alignment position
541    *
542    * @param i column index in alignment (from 1)
543    *
544    * @return residue number for residue (left of and) nearest ith column
545    */
546   public int findPosition(int i)
547   {
548     int j = 0;
549     int pos = start;
550     int seqlen = sequence.length;
551     while ( (j < i) && (j < seqlen))
552     {
553       if (!jalview.util.Comparison.isGap( sequence[j] ))
554       {
555         pos++;
556       }
557
558       j++;
559     }
560
561     return pos;
562   }
563
564   /**
565    * Returns an int array where indices correspond to each residue in the sequence and the element value gives its position in the alignment
566    *
567    * @return int[SequenceI.getEnd()-SequenceI.getStart()+1] or null if no residues in SequenceI object
568    */
569   public int[] gapMap()
570   {
571     String seq = jalview.analysis.AlignSeq.extractGaps(jalview.util.Comparison.
572         GapChars, new String(sequence));
573     int[] map = new int[seq.length()];
574     int j = 0;
575     int p = 0;
576
577     while (j < sequence.length)
578     {
579       if (!jalview.util.Comparison.isGap(sequence[j]))
580       {
581         map[p++] = j;
582       }
583
584       j++;
585     }
586
587     return map;
588   }
589
590   /**
591    * DOCUMENT ME!
592    *
593    * @param i DOCUMENT ME!
594    * @param j DOCUMENT ME!
595    */
596   public void deleteChars(int i, int j)
597   {
598     if (i >= sequence.length)
599     {
600       return;
601     }
602
603     char [] tmp;
604
605     if (j >= sequence.length)
606     {
607       tmp = new char[i];
608       System.arraycopy(sequence,0,tmp,0,i);
609     }
610     else
611     {
612       tmp = new char[sequence.length-j+i];
613       System.arraycopy(sequence,0,tmp,0,i);
614       System.arraycopy(sequence,j,tmp,i,sequence.length-j);
615     }
616
617     if (this.datasetSequence != null)
618     {
619       for (int s = i; s < j; s++)
620       {
621         if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
622         {
623
624           Sequence ds = new Sequence(name,
625                                      AlignSeq.extractGaps(
626                                          jalview.util.Comparison.GapChars,
627                                          this.getSequenceAsString()
628                                      ),
629                                      start,
630                                      end);
631           ds.setDescription(description);
632         }
633         break;
634       }
635     }
636
637     sequence = tmp;
638
639   }
640
641
642   /**
643    * DOCUMENT ME!
644    *
645    * @param i DOCUMENT ME!
646    * @param c DOCUMENT ME!
647    * @param chop DOCUMENT ME!
648    */
649   public void insertCharAt(int i, int length, char c)
650   {
651     char [] tmp = new char[sequence.length+length];
652
653     if (i >= sequence.length)
654     {
655       System.arraycopy(sequence, 0, tmp, 0, sequence.length);
656       i = sequence.length;
657     }
658     else
659    {
660       System.arraycopy(sequence, 0, tmp, 0, i);
661    }
662
663
664     int index = i;
665     while (length > 0)
666     {
667       tmp[ index++ ] = c;
668       length--;
669     }
670
671     if (i < sequence.length)
672     {
673       System.arraycopy(sequence, i, tmp, index, sequence.length-i );
674     }
675
676     sequence = tmp;
677   }
678
679   public void insertCharAt(int i, char c)
680   {
681     insertCharAt(i, 1, c);
682   }
683
684   public String getVamsasId()
685   {
686     return vamsasId;
687   }
688
689   public void setVamsasId(String id)
690   {
691     vamsasId = id;
692   }
693
694   public void setDBRef(DBRefEntry[] dbref)
695   {
696     dbrefs = dbref;
697   }
698
699   public DBRefEntry[] getDBRef()
700   {
701     return dbrefs;
702   }
703
704   public void addDBRef(DBRefEntry entry)
705   {
706     if (dbrefs == null)
707     {
708       dbrefs = new DBRefEntry[0];
709     }
710
711     int i, iSize = dbrefs.length;
712
713     for(i=0; i<iSize; i++)
714     {
715       if(dbrefs[i].equals(entry))
716       {
717         return;
718       }
719     }
720
721     DBRefEntry[] temp = new DBRefEntry[iSize + 1];
722     System.arraycopy(dbrefs, 0, temp, 0, iSize);
723     temp[temp.length - 1] = entry;
724
725     dbrefs = temp;
726   }
727
728   public void setDatasetSequence(SequenceI seq)
729   {
730     datasetSequence = seq;
731   }
732
733   public SequenceI getDatasetSequence()
734   {
735     return datasetSequence;
736   }
737
738   public AlignmentAnnotation[] getAnnotation()
739   {
740     if (annotation == null)
741     {
742       return null;
743     }
744
745     AlignmentAnnotation[] ret = new AlignmentAnnotation[annotation.size()];
746     for (int r = 0; r < ret.length; r++)
747     {
748       ret[r] = (AlignmentAnnotation) annotation.elementAt(r);
749     }
750
751     return ret;
752   }
753
754   public void addAlignmentAnnotation(AlignmentAnnotation annotation)
755   {
756     if (this.annotation == null)
757     {
758       this.annotation = new Vector();
759     }
760
761     this.annotation.addElement(annotation);
762     annotation.setSequenceRef(this);
763   }
764
765   public void removeAlignmentAnnotation(AlignmentAnnotation annotation)
766   {
767     if(this.annotation!=null)
768     {
769       this.annotation.removeElement(annotation);
770       if(this.annotation.size()==0)
771         this.annotation = null;
772     }
773   }
774
775
776   /**
777    * test if this is a valid candidate for another
778    * sequence's dataset sequence.
779    *
780    */
781   private boolean isValidDatasetSequence()
782   {
783     if (datasetSequence!=null)
784     {
785           return false;
786     }
787       for (int i=0;i<sequence.length; i++)
788     {
789           if (jalview.util.Comparison.isGap(sequence[i]))
790       {
791               return false;
792       }
793     }
794       return true;
795   }
796   /* (non-Javadoc)
797    * @see jalview.datamodel.SequenceI#deriveSequence()
798    */
799   public SequenceI deriveSequence()
800   {
801     SequenceI seq=new Sequence(this);
802     if (datasetSequence != null)
803     {
804       // duplicate current sequence with same dataset
805       seq.setDatasetSequence(datasetSequence);
806     }
807     else
808     {
809       if (isValidDatasetSequence())
810       {
811         // Use this as dataset sequence
812         seq.setDatasetSequence(this);
813       } else {
814         // Create a new, valid dataset sequence
815         SequenceI ds = seq;
816         ds.setSequence(AlignSeq.extractGaps(jalview.util.Comparison.GapChars, new String(sequence)));
817         setDatasetSequence(ds);
818         seq = this; // and return this sequence as the derived sequence.
819       }
820     }
821     return seq;
822   }
823   /* (non-Javadoc)
824    * @see jalview.datamodel.SequenceI#setAlignmentAnnotation(AlignmmentAnnotation[] annotations)
825    */
826   public void setAlignmentAnnotation(AlignmentAnnotation[] annotations)
827   {
828     if (annotation!=null) {
829       annotation.removeAllElements();
830     }
831     if (annotations!=null) {
832       for (int i=0; i<annotations.length; i++)
833       {
834         if (annotations[i]!=null)
835           addAlignmentAnnotation(annotations[i]);
836       }
837     }
838   }
839
840   /* (non-Javadoc)
841    * @see jalview.datamodel.SequenceI#getAnnotation(java.lang.String)
842    */
843   public AlignmentAnnotation[] getAnnotation(String label)
844   {
845     if (annotation==null || annotation.size()==0)
846     {
847       return null;
848     }
849     
850     Vector subset = new Vector();
851     Enumeration e = annotation.elements();
852     while (e.hasMoreElements())
853     {
854       AlignmentAnnotation ann = (AlignmentAnnotation) e.nextElement();
855       if (ann.label!=null && ann.label.equals(label))
856       {
857         subset.addElement(ann);
858       }
859     }
860     if (subset.size()==0)
861     {
862       return null;
863     }
864     AlignmentAnnotation[] anns = new AlignmentAnnotation[subset.size()];
865     int i=0;
866     e = subset.elements();
867     while (e.hasMoreElements())
868     {
869       anns[i++] = (AlignmentAnnotation) e.nextElement();
870     }
871     subset.removeAllElements();
872     return anns;
873   }
874
875 }
876
877