Merge branch 'develop' into dev_from_2_8_1
[jalview.git] / src / jalview / datamodel / Sequence.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.datamodel;
19
20 import jalview.analysis.AlignSeq;
21
22 import java.util.Enumeration;
23 import java.util.Vector;
24
25 import fr.orsay.lri.varna.models.rna.RNA;
26
27 /**
28  * 
29  * Implements the SequenceI interface for a char[] based sequence object.
30  * 
31  * @author $author$
32  * @version $Revision$
33  */
34 public class Sequence implements SequenceI
35 {
36   SequenceI datasetSequence;
37
38   String name;
39
40   private char[] sequence;
41
42   String description;
43
44   int start;
45
46   int end;
47
48   Vector pdbIds;
49
50   String vamsasId;
51
52   DBRefEntry[] dbrefs;
53   
54   RNA rna;
55
56   /**
57    * This annotation is displayed below the alignment but the positions are tied
58    * to the residues of this sequence
59    */
60   Vector annotation;
61
62   /**
63    * The index of the sequence in a MSA
64    */
65   int index = -1;
66
67   /** array of sequence features - may not be null for a valid sequence object */
68   public SequenceFeature[] sequenceFeatures;
69
70   /**
71    * Creates a new Sequence object.
72    * 
73    * @param name
74    *          display name string
75    * @param sequence
76    *          string to form a possibly gapped sequence out of
77    * @param start
78    *          first position of non-gap residue in the sequence
79    * @param end
80    *          last position of ungapped residues (nearly always only used for
81    *          display purposes)
82    */
83   public Sequence(String name, String sequence, int start, int end)
84   {
85     this.name = name;
86     this.sequence = sequence.toCharArray();
87     this.start = start;
88     this.end = end;
89     parseId();
90     checkValidRange();
91   }
92
93   public Sequence(String name, char[] sequence, int start, int end)
94   {
95     this.name = name;
96     this.sequence = sequence;
97     this.start = start;
98     this.end = end;
99     parseId();
100     checkValidRange();
101   }
102
103   com.stevesoft.pat.Regex limitrx = new com.stevesoft.pat.Regex(
104           "[/][0-9]{1,}[-][0-9]{1,}$");
105
106   com.stevesoft.pat.Regex endrx = new com.stevesoft.pat.Regex("[0-9]{1,}$");
107
108   void parseId()
109   {
110     if (name == null)
111     {
112       System.err
113               .println("POSSIBLE IMPLEMENTATION ERROR: null sequence name passed to constructor.");
114       name = "";
115     }
116     // Does sequence have the /start-end signiature?
117     if (limitrx.search(name))
118     {
119       name = limitrx.left();
120       endrx.search(limitrx.stringMatched());
121       setStart(Integer.parseInt(limitrx.stringMatched().substring(1,
122               endrx.matchedFrom() - 1)));
123       setEnd(Integer.parseInt(endrx.stringMatched()));
124     }
125   }
126
127   void checkValidRange()
128   {
129     // Note: JAL-774 :
130     // http://issues.jalview.org/browse/JAL-774?focusedCommentId=11239&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-11239
131     {
132       int endRes = 0;
133       for (int j = 0; j < sequence.length; j++)
134       {
135         if (!jalview.util.Comparison.isGap(sequence[j]))
136         {
137           endRes++;
138         }
139       }
140       if (endRes > 0)
141       {
142         endRes += start - 1;
143       }
144
145       if (end < endRes)
146       {
147         end = endRes;
148       }
149     }
150
151   }
152
153   /**
154    * Creates a new Sequence object.
155    * 
156    * @param name
157    *          DOCUMENT ME!
158    * @param sequence
159    *          DOCUMENT ME!
160    */
161   public Sequence(String name, String sequence)
162   {
163     this(name, sequence, 1, -1);
164   }
165
166   /**
167    * Creates a new Sequence object with new features, DBRefEntries,
168    * AlignmentAnnotations, and PDBIds but inherits any existing dataset sequence
169    * reference.
170    * 
171    * @param seq
172    *          DOCUMENT ME!
173    */
174   public Sequence(SequenceI seq)
175   {
176     this(seq, seq.getAnnotation());
177   }
178
179   /**
180    * Create a new sequence object with new features, DBRefEntries, and PDBIds
181    * but inherits any existing dataset sequence reference, and duplicate of any
182    * annotation that is present in the given annotation array.
183    * 
184    * @param seq
185    *          the sequence to be copied
186    * @param alAnnotation
187    *          an array of annotation including some associated with seq
188    */
189   public Sequence(SequenceI seq, AlignmentAnnotation[] alAnnotation)
190   {
191     this(seq.getName(), seq.getSequence(), seq.getStart(), seq.getEnd());
192     description = seq.getDescription();
193     if (seq.getSequenceFeatures() != null)
194     {
195       SequenceFeature[] sf = seq.getSequenceFeatures();
196       for (int i = 0; i < sf.length; i++)
197       {
198         addSequenceFeature(new SequenceFeature(sf[i]));
199       }
200     }
201     setDatasetSequence(seq.getDatasetSequence());
202     if (datasetSequence == null && seq.getDBRef() != null)
203     {
204       // only copy DBRefs if we really are a dataset sequence
205       DBRefEntry[] dbr = seq.getDBRef();
206       for (int i = 0; i < dbr.length; i++)
207       {
208         addDBRef(new DBRefEntry(dbr[i]));
209       }
210     }
211     if (seq.getAnnotation() != null)
212     {
213       AlignmentAnnotation[] sqann = seq.getAnnotation();
214       for (int i = 0; i < sqann.length; i++)
215       {
216         if (sqann[i] == null)
217         {
218           continue;
219         }
220         boolean found = (alAnnotation == null);
221         if (!found)
222         {
223           for (int apos = 0; !found && apos < alAnnotation.length; apos++)
224           {
225             found = (alAnnotation[apos] == sqann[i]);
226           }
227         }
228         if (found)
229         {
230           // only copy the given annotation
231           AlignmentAnnotation newann = new AlignmentAnnotation(sqann[i]);
232           addAlignmentAnnotation(newann);
233         }
234       }
235     }
236     if (seq.getPDBId() != null)
237     {
238       Vector ids = seq.getPDBId();
239       Enumeration e = ids.elements();
240       while (e.hasMoreElements())
241       {
242         this.addPDBId(new PDBEntry((PDBEntry) e.nextElement()));
243       }
244     }
245   }
246
247   /**
248    * DOCUMENT ME!
249    * 
250    * @param v
251    *          DOCUMENT ME!
252    */
253   public void setSequenceFeatures(SequenceFeature[] features)
254   {
255     sequenceFeatures = features;
256   }
257
258   public synchronized void addSequenceFeature(SequenceFeature sf)
259   {
260     if (sequenceFeatures == null)
261     {
262       sequenceFeatures = new SequenceFeature[0];
263     }
264
265     for (int i = 0; i < sequenceFeatures.length; i++)
266     {
267       if (sequenceFeatures[i].equals(sf))
268       {
269         return;
270       }
271     }
272
273     SequenceFeature[] temp = new SequenceFeature[sequenceFeatures.length + 1];
274     System.arraycopy(sequenceFeatures, 0, temp, 0, sequenceFeatures.length);
275     temp[sequenceFeatures.length] = sf;
276
277     sequenceFeatures = temp;
278   }
279
280   public void deleteFeature(SequenceFeature sf)
281   {
282     if (sequenceFeatures == null)
283     {
284       return;
285     }
286
287     int index = 0;
288     for (index = 0; index < sequenceFeatures.length; index++)
289     {
290       if (sequenceFeatures[index].equals(sf))
291       {
292         break;
293       }
294     }
295
296     if (index == sequenceFeatures.length)
297     {
298       return;
299     }
300
301     int sfLength = sequenceFeatures.length;
302     if (sfLength < 2)
303     {
304       sequenceFeatures = null;
305     }
306     else
307     {
308       SequenceFeature[] temp = new SequenceFeature[sfLength - 1];
309       System.arraycopy(sequenceFeatures, 0, temp, 0, index);
310
311       if (index < sfLength)
312       {
313         System.arraycopy(sequenceFeatures, index + 1, temp, index,
314                 sequenceFeatures.length - index - 1);
315       }
316
317       sequenceFeatures = temp;
318     }
319   }
320
321   /**
322    * DOCUMENT ME!
323    * 
324    * @return DOCUMENT ME!
325    */
326   public SequenceFeature[] getSequenceFeatures()
327   {
328     return sequenceFeatures;
329   }
330
331   public void addPDBId(PDBEntry entry)
332   {
333     if (pdbIds == null)
334     {
335       pdbIds = new Vector();
336     }
337     if (!pdbIds.contains(entry))
338     {
339       pdbIds.addElement(entry);
340     }
341   }
342
343   /**
344    * DOCUMENT ME!
345    * 
346    * @param id
347    *          DOCUMENT ME!
348    */
349   public void setPDBId(Vector id)
350   {
351     pdbIds = id;
352   }
353
354   /**
355    * DOCUMENT ME!
356    * 
357    * @return DOCUMENT ME!
358    */
359   public Vector getPDBId()
360   {
361     return pdbIds;
362   }
363
364   /**
365    * DOCUMENT ME!
366    * 
367    * @return DOCUMENT ME!
368    */
369   public String getDisplayId(boolean jvsuffix)
370   {
371     StringBuffer result = new StringBuffer(name);
372     if (jvsuffix)
373     {
374       result.append("/" + start + "-" + end);
375     }
376
377     return result.toString();
378   }
379
380   /**
381    * DOCUMENT ME!
382    * 
383    * @param name
384    *          DOCUMENT ME!
385    */
386   public void setName(String name)
387   {
388     this.name = name;
389     this.parseId();
390   }
391
392   /**
393    * DOCUMENT ME!
394    * 
395    * @return DOCUMENT ME!
396    */
397   public String getName()
398   {
399     return this.name;
400   }
401
402   /**
403    * DOCUMENT ME!
404    * 
405    * @param start
406    *          DOCUMENT ME!
407    */
408   public void setStart(int start)
409   {
410     this.start = start;
411   }
412
413   /**
414    * DOCUMENT ME!
415    * 
416    * @return DOCUMENT ME!
417    */
418   public int getStart()
419   {
420     return this.start;
421   }
422
423   /**
424    * DOCUMENT ME!
425    * 
426    * @param end
427    *          DOCUMENT ME!
428    */
429   public void setEnd(int end)
430   {
431     this.end = end;
432   }
433
434   /**
435    * DOCUMENT ME!
436    * 
437    * @return DOCUMENT ME!
438    */
439   public int getEnd()
440   {
441     return this.end;
442   }
443
444   /**
445    * DOCUMENT ME!
446    * 
447    * @return DOCUMENT ME!
448    */
449   public int getLength()
450   {
451     return this.sequence.length;
452   }
453
454   /**
455    * DOCUMENT ME!
456    * 
457    * @param seq
458    *          DOCUMENT ME!
459    */
460   public void setSequence(String seq)
461   {
462     this.sequence = seq.toCharArray();
463     checkValidRange();
464   }
465
466   public String getSequenceAsString()
467   {
468     return new String(sequence);
469   }
470
471   public String getSequenceAsString(int start, int end)
472   {
473     return new String(getSequence(start, end));
474   }
475
476   public char[] getSequence()
477   {
478     return sequence;
479   }
480
481   /*
482    * (non-Javadoc)
483    * 
484    * @see jalview.datamodel.SequenceI#getSequence(int, int)
485    */
486   public char[] getSequence(int start, int end)
487   {
488     if (start < 0)
489       start = 0;
490     // JBPNote - left to user to pad the result here (TODO:Decide on this
491     // policy)
492     if (start >= sequence.length)
493     {
494       return new char[0];
495     }
496
497     if (end >= sequence.length)
498     {
499       end = sequence.length;
500     }
501
502     char[] reply = new char[end - start];
503     System.arraycopy(sequence, start, reply, 0, end - start);
504
505     return reply;
506   }
507
508   /**
509    * make a new Sequence object from start to end (including gaps) over this
510    * seqeunce
511    * 
512    * @param start
513    *          int
514    * @param end
515    *          int
516    * @return SequenceI
517    */
518   public SequenceI getSubSequence(int start, int end)
519   {
520     if (start < 0)
521     {
522       start = 0;
523     }
524     char[] seq = getSequence(start, end);
525     if (seq.length == 0)
526     {
527       return null;
528     }
529     int nstart = findPosition(start);
530     int nend = findPosition(end) - 1;
531     // JBPNote - this is an incomplete copy.
532     SequenceI nseq = new Sequence(this.getName(), seq, nstart, nend);
533     nseq.setDescription(description);
534     if (datasetSequence != null)
535     {
536       nseq.setDatasetSequence(datasetSequence);
537     }
538     else
539     {
540       nseq.setDatasetSequence(this);
541     }
542     return nseq;
543   }
544
545   /**
546    * DOCUMENT ME!
547    * 
548    * @param i
549    *          DOCUMENT ME!
550    * 
551    * @return DOCUMENT ME!
552    */
553   public char getCharAt(int i)
554   {
555     if (i < sequence.length)
556     {
557       return sequence[i];
558     }
559     else
560     {
561       return ' ';
562     }
563   }
564
565   /**
566    * DOCUMENT ME!
567    * 
568    * @param desc
569    *          DOCUMENT ME!
570    */
571   public void setDescription(String desc)
572   {
573     this.description = desc;
574   }
575
576   /**
577    * DOCUMENT ME!
578    * 
579    * @return DOCUMENT ME!
580    */
581   public String getDescription()
582   {
583     return this.description;
584   }
585
586   /*
587    * (non-Javadoc)
588    * 
589    * @see jalview.datamodel.SequenceI#findIndex(int)
590    */
591   public int findIndex(int pos)
592   {
593     // returns the alignment position for a residue
594     int j = start;
595     int i = 0;
596     // Rely on end being at least as long as the length of the sequence.
597     while ((i < sequence.length) && (j <= end) && (j <= pos))
598     {
599       if (!jalview.util.Comparison.isGap(sequence[i]))
600       {
601         j++;
602       }
603
604       i++;
605     }
606
607     if ((j == end) && (j < pos))
608     {
609       return end + 1;
610     }
611     else
612     {
613       return i;
614     }
615   }
616
617   /**
618    * Returns the sequence position for an alignment position
619    * 
620    * @param i
621    *          column index in alignment (from 1)
622    * 
623    * @return residue number for residue (left of and) nearest ith column
624    */
625   public int findPosition(int i)
626   {
627     int j = 0;
628     int pos = start;
629     int seqlen = sequence.length;
630     while ((j < i) && (j < seqlen))
631     {
632       if (!jalview.util.Comparison.isGap(sequence[j]))
633       {
634         pos++;
635       }
636
637       j++;
638     }
639
640     return pos;
641   }
642
643   /**
644    * Returns an int array where indices correspond to each residue in the
645    * sequence and the element value gives its position in the alignment
646    * 
647    * @return int[SequenceI.getEnd()-SequenceI.getStart()+1] or null if no
648    *         residues in SequenceI object
649    */
650   public int[] gapMap()
651   {
652     String seq = jalview.analysis.AlignSeq.extractGaps(
653             jalview.util.Comparison.GapChars, new String(sequence));
654     int[] map = new int[seq.length()];
655     int j = 0;
656     int p = 0;
657
658     while (j < sequence.length)
659     {
660       if (!jalview.util.Comparison.isGap(sequence[j]))
661       {
662         map[p++] = j;
663       }
664
665       j++;
666     }
667
668     return map;
669   }
670
671   /*
672    * (non-Javadoc)
673    * 
674    * @see jalview.datamodel.SequenceI#findPositionMap()
675    */
676   public int[] findPositionMap()
677   {
678     int map[] = new int[sequence.length];
679     int j = 0;
680     int pos = start;
681     int seqlen = sequence.length;
682     while ((j < seqlen))
683     {
684       map[j] = pos;
685       if (!jalview.util.Comparison.isGap(sequence[j]))
686       {
687         pos++;
688       }
689
690       j++;
691     }
692     return map;
693   }
694
695   /*
696    * (non-Javadoc)
697    * 
698    * @see jalview.datamodel.SequenceI#deleteChars(int, int)
699    */
700   public void deleteChars(int i, int j)
701   {
702     int newstart = start, newend = end;
703     if (i >= sequence.length)
704     {
705       return;
706     }
707
708     char[] tmp;
709
710     if (j >= sequence.length)
711     {
712       tmp = new char[i];
713       System.arraycopy(sequence, 0, tmp, 0, i);
714       j=sequence.length;
715     }
716     else
717     {
718       tmp = new char[sequence.length - j + i];
719       System.arraycopy(sequence, 0, tmp, 0, i);
720       System.arraycopy(sequence, j, tmp, i, sequence.length - j);
721     }
722     boolean createNewDs = false;
723     // TODO: take a look at the new dataset creation validation method below -
724     // this could become time comsuming for large sequences - consider making it
725     // more efficient
726     for (int s = i; s < j; s++)
727     {
728       if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
729       {
730         if (createNewDs)
731         {
732           newend--;
733         }
734         else
735         {
736           int sindex = findIndex(start) - 1;
737           if (sindex == s)
738           {
739             // delete characters including start of sequence
740             newstart = findPosition(j);
741             break; // don't need to search for any more residue characters.
742           }
743           else
744           {
745             // delete characters after start.
746             int eindex = findIndex(end) - 1;
747             if (eindex < j)
748             {
749               // delete characters at end of sequence
750               newend = findPosition(i - 1);
751               break; // don't need to search for any more residue characters.
752             }
753             else
754             {
755               createNewDs = true;
756               newend--; // decrease end position by one for the deleted residue
757               // and search further
758             }
759           }
760         }
761       }
762     }
763     // deletion occured in the middle of the sequence
764     if (createNewDs && this.datasetSequence != null)
765     {
766       // construct a new sequence
767       Sequence ds = new Sequence(datasetSequence);
768       // TODO: remove any non-inheritable properties ?
769       // TODO: create a sequence mapping (since there is a relation here ?)
770       ds.deleteChars(i, j);
771       datasetSequence = ds;
772     }
773     start = newstart;
774     end = newend;
775     sequence = tmp;
776   }
777
778   /**
779    * DOCUMENT ME!
780    * 
781    * @param i
782    *          DOCUMENT ME!
783    * @param c
784    *          DOCUMENT ME!
785    * @param chop
786    *          DOCUMENT ME!
787    */
788   public void insertCharAt(int i, int length, char c)
789   {
790     char[] tmp = new char[sequence.length + length];
791
792     if (i >= sequence.length)
793     {
794       System.arraycopy(sequence, 0, tmp, 0, sequence.length);
795       i = sequence.length;
796     }
797     else
798     {
799       System.arraycopy(sequence, 0, tmp, 0, i);
800     }
801
802     int index = i;
803     while (length > 0)
804     {
805       tmp[index++] = c;
806       length--;
807     }
808
809     if (i < sequence.length)
810     {
811       System.arraycopy(sequence, i, tmp, index, sequence.length - i);
812     }
813
814     sequence = tmp;
815   }
816
817   public void insertCharAt(int i, char c)
818   {
819     insertCharAt(i, 1, c);
820   }
821
822   public String getVamsasId()
823   {
824     return vamsasId;
825   }
826
827   public void setVamsasId(String id)
828   {
829     vamsasId = id;
830   }
831
832   public void setDBRef(DBRefEntry[] dbref)
833   {
834     dbrefs = dbref;
835   }
836
837   public DBRefEntry[] getDBRef()
838   {
839     if (dbrefs == null && datasetSequence != null
840             && this != datasetSequence)
841     {
842       return datasetSequence.getDBRef();
843     }
844     return dbrefs;
845   }
846
847   public void addDBRef(DBRefEntry entry)
848   {
849     if (dbrefs == null)
850     {
851       dbrefs = new DBRefEntry[0];
852     }
853
854     int i, iSize = dbrefs.length;
855
856     for (i = 0; i < iSize; i++)
857     {
858       if (dbrefs[i].equalRef(entry))
859       {
860         if (entry.getMap() != null)
861         {
862           if (dbrefs[i].getMap() == null)
863           {
864             // overwrite with 'superior' entry that contains a mapping.
865             dbrefs[i] = entry;
866           }
867         }
868         return;
869       }
870     }
871
872     DBRefEntry[] temp = new DBRefEntry[iSize + 1];
873     System.arraycopy(dbrefs, 0, temp, 0, iSize);
874     temp[temp.length - 1] = entry;
875
876     dbrefs = temp;
877   }
878
879   public void setDatasetSequence(SequenceI seq)
880   {
881     datasetSequence = seq;
882   }
883
884   public SequenceI getDatasetSequence()
885   {
886     return datasetSequence;
887   }
888
889   public AlignmentAnnotation[] getAnnotation()
890   {
891     if (annotation == null)
892     {
893       return null;
894     }
895
896     AlignmentAnnotation[] ret = new AlignmentAnnotation[annotation.size()];
897     for (int r = 0; r < ret.length; r++)
898     {
899       ret[r] = (AlignmentAnnotation) annotation.elementAt(r);
900     }
901
902     return ret;
903   }
904
905   public void addAlignmentAnnotation(AlignmentAnnotation annotation)
906   {
907     if (this.annotation == null)
908     {
909       this.annotation = new Vector();
910     }
911     if (!this.annotation.contains(annotation))
912     {
913       this.annotation.addElement(annotation);
914     }
915     annotation.setSequenceRef(this);
916   }
917
918   public void removeAlignmentAnnotation(AlignmentAnnotation annotation)
919   {
920     if (this.annotation != null)
921     {
922       this.annotation.removeElement(annotation);
923       if (this.annotation.size() == 0)
924         this.annotation = null;
925     }
926   }
927
928   /**
929    * test if this is a valid candidate for another sequence's dataset sequence.
930    * 
931    */
932   private boolean isValidDatasetSequence()
933   {
934     if (datasetSequence != null)
935     {
936       return false;
937     }
938     for (int i = 0; i < sequence.length; i++)
939     {
940       if (jalview.util.Comparison.isGap(sequence[i]))
941       {
942         return false;
943       }
944     }
945     return true;
946   }
947
948   /*
949    * (non-Javadoc)
950    * 
951    * @see jalview.datamodel.SequenceI#deriveSequence()
952    */
953   public SequenceI deriveSequence()
954   {
955     SequenceI seq = new Sequence(this);
956     if (datasetSequence != null)
957     {
958       // duplicate current sequence with same dataset
959       seq.setDatasetSequence(datasetSequence);
960     }
961     else
962     {
963       if (isValidDatasetSequence())
964       {
965         // Use this as dataset sequence
966         seq.setDatasetSequence(this);
967       }
968       else
969       {
970         // Create a new, valid dataset sequence
971         SequenceI ds = seq;
972         ds.setSequence(AlignSeq.extractGaps(
973                 jalview.util.Comparison.GapChars, new String(sequence)));
974         setDatasetSequence(ds);
975         ds.setSequenceFeatures(getSequenceFeatures());
976         seq = this; // and return this sequence as the derived sequence.
977       }
978     }
979     return seq;
980   }
981
982   /*
983    * (non-Javadoc)
984    * 
985    * @see jalview.datamodel.SequenceI#createDatasetSequence()
986    */
987   public SequenceI createDatasetSequence()
988   {
989     if (datasetSequence == null)
990     {
991       datasetSequence = new Sequence(getName(), AlignSeq.extractGaps(
992               jalview.util.Comparison.GapChars, getSequenceAsString()),
993               getStart(), getEnd());
994       datasetSequence.setSequenceFeatures(getSequenceFeatures());
995       datasetSequence.setDescription(getDescription());
996       setSequenceFeatures(null);
997       // move database references onto dataset sequence
998       datasetSequence.setDBRef(getDBRef());
999       setDBRef(null);
1000       datasetSequence.setPDBId(getPDBId());
1001       setPDBId(null);
1002       datasetSequence.updatePDBIds();
1003     }
1004     return datasetSequence;
1005   }
1006
1007   /*
1008    * (non-Javadoc)
1009    * 
1010    * @see
1011    * jalview.datamodel.SequenceI#setAlignmentAnnotation(AlignmmentAnnotation[]
1012    * annotations)
1013    */
1014   public void setAlignmentAnnotation(AlignmentAnnotation[] annotations)
1015   {
1016     if (annotation != null)
1017     {
1018       annotation.removeAllElements();
1019     }
1020     if (annotations != null)
1021     {
1022       for (int i = 0; i < annotations.length; i++)
1023       {
1024         if (annotations[i] != null)
1025           addAlignmentAnnotation(annotations[i]);
1026       }
1027     }
1028   }
1029
1030   /*
1031    * (non-Javadoc)
1032    * 
1033    * @see jalview.datamodel.SequenceI#getAnnotation(java.lang.String)
1034    */
1035   public AlignmentAnnotation[] getAnnotation(String label)
1036   {
1037     if (annotation == null || annotation.size() == 0)
1038     {
1039       return null;
1040     }
1041
1042     Vector subset = new Vector();
1043     Enumeration e = annotation.elements();
1044     while (e.hasMoreElements())
1045     {
1046       AlignmentAnnotation ann = (AlignmentAnnotation) e.nextElement();
1047       if (ann.label != null && ann.label.equals(label))
1048       {
1049         subset.addElement(ann);
1050       }
1051     }
1052     if (subset.size() == 0)
1053     {
1054       return null;
1055     }
1056     AlignmentAnnotation[] anns = new AlignmentAnnotation[subset.size()];
1057     int i = 0;
1058     e = subset.elements();
1059     while (e.hasMoreElements())
1060     {
1061       anns[i++] = (AlignmentAnnotation) e.nextElement();
1062     }
1063     subset.removeAllElements();
1064     return anns;
1065   }
1066
1067   public boolean updatePDBIds()
1068   {
1069     if (datasetSequence != null)
1070     {
1071       // TODO: could merge DBRefs
1072       return datasetSequence.updatePDBIds();
1073     }
1074     if (dbrefs == null || dbrefs.length == 0)
1075     {
1076       return false;
1077     }
1078     Vector newpdb = new Vector();
1079     for (int i = 0; i < dbrefs.length; i++)
1080     {
1081       if (DBRefSource.PDB.equals(dbrefs[i].getSource()))
1082       {
1083         PDBEntry pdbe = new PDBEntry();
1084         pdbe.setId(dbrefs[i].getAccessionId());
1085         if (pdbIds == null || pdbIds.size() == 0)
1086         {
1087           newpdb.addElement(pdbe);
1088         }
1089         else
1090         {
1091           Enumeration en = pdbIds.elements();
1092           boolean matched = false;
1093           while (!matched && en.hasMoreElements())
1094           {
1095             PDBEntry anentry = (PDBEntry) en.nextElement();
1096             if (anentry.getId().equals(pdbe.getId()))
1097             {
1098               matched = true;
1099             }
1100           }
1101           if (!matched)
1102           {
1103             newpdb.addElement(pdbe);
1104           }
1105         }
1106       }
1107     }
1108     if (newpdb.size() > 0)
1109     {
1110       Enumeration en = newpdb.elements();
1111       while (en.hasMoreElements())
1112       {
1113         addPDBId((PDBEntry) en.nextElement());
1114       }
1115       return true;
1116     }
1117     return false;
1118   }
1119
1120   /*
1121    * (non-Javadoc)
1122    * 
1123    * @see
1124    * jalview.datamodel.SequenceI#transferAnnotation(jalview.datamodel.SequenceI,
1125    * jalview.datamodel.Mapping)
1126    */
1127   public void transferAnnotation(SequenceI entry, Mapping mp)
1128   {
1129     if (datasetSequence != null)
1130     {
1131       datasetSequence.transferAnnotation(entry, mp);
1132       return;
1133     }
1134     if (entry.getDatasetSequence() != null)
1135     {
1136       transferAnnotation(entry.getDatasetSequence(), mp);
1137       return;
1138     }
1139     // transfer any new features from entry onto sequence
1140     if (entry.getSequenceFeatures() != null)
1141     {
1142
1143       SequenceFeature[] sfs = entry.getSequenceFeatures();
1144       for (int si = 0; si < sfs.length; si++)
1145       {
1146         SequenceFeature sf[] = (mp != null) ? mp.locateFeature(sfs[si])
1147                 : new SequenceFeature[]
1148                 { new SequenceFeature(sfs[si]) };
1149         if (sf != null && sf.length > 0)
1150         {
1151           for (int sfi = 0; sfi < sf.length; sfi++)
1152           {
1153             addSequenceFeature(sf[sfi]);
1154           }
1155         }
1156       }
1157     }
1158
1159     // transfer PDB entries
1160     if (entry.getPDBId() != null)
1161     {
1162       Enumeration e = entry.getPDBId().elements();
1163       while (e.hasMoreElements())
1164       {
1165         PDBEntry pdb = (PDBEntry) e.nextElement();
1166         addPDBId(pdb);
1167       }
1168     }
1169     // transfer database references
1170     DBRefEntry[] entryRefs = entry.getDBRef();
1171     if (entryRefs != null)
1172     {
1173       for (int r = 0; r < entryRefs.length; r++)
1174       {
1175         DBRefEntry newref = new DBRefEntry(entryRefs[r]);
1176         if (newref.getMap() != null && mp != null)
1177         {
1178           // remap ref using our local mapping
1179         }
1180         // we also assume all version string setting is done by dbSourceProxy
1181         /*
1182          * if (!newref.getSource().equalsIgnoreCase(dbSource)) {
1183          * newref.setSource(dbSource); }
1184          */
1185         addDBRef(newref);
1186       }
1187     }
1188   }
1189
1190   /**
1191    * @return The index (zero-based) on this sequence in the MSA. It returns
1192    *         {@code -1} if this information is not available.
1193    */
1194   public int getIndex()
1195   {
1196     return index;
1197   }
1198
1199   /**
1200    * Defines the position of this sequence in the MSA. Use the value {@code -1}
1201    * if this information is undefined.
1202    * 
1203    * @param The
1204    *          position for this sequence. This value is zero-based (zero for
1205    *          this first sequence)
1206    */
1207   public void setIndex(int value)
1208   {
1209     index = value;
1210   }
1211   
1212   public void setRNA(RNA r){rna=r;}
1213   
1214   public RNA getRNA() { return rna; }
1215   
1216 }