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