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