JAL-653 refactor sequence interface to separate concrete sequence data methods from...
[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 java.util.ArrayList;
24 import java.util.Enumeration;
25 import java.util.List;
26 import java.util.Vector;
27
28 import fr.orsay.lri.varna.models.rna.RNA;
29
30 import jalview.analysis.AlignSeq;
31 import jalview.util.StringUtils;
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 extends ASequence 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<PDBEntry> 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 signature?
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<PDBEntry>();
357     }
358     if (pdbIds.contains(entry))
359     {
360       updatePDBEntry(pdbIds.get(pdbIds.indexOf(entry)), entry);
361     }
362     else
363     {
364       pdbIds.addElement(entry);
365     }
366   }
367
368   private static void updatePDBEntry(PDBEntry oldEntry, PDBEntry newEntry)
369   {
370     if (newEntry.getFile() != null)
371     {
372       oldEntry.setFile(newEntry.getFile());
373     }
374   }
375
376   /**
377    * DOCUMENT ME!
378    * 
379    * @param id
380    *          DOCUMENT ME!
381    */
382   @Override
383   public void setPDBId(Vector<PDBEntry> id)
384   {
385     pdbIds = id;
386   }
387
388   /**
389    * DOCUMENT ME!
390    * 
391    * @return DOCUMENT ME!
392    */
393   @Override
394   public Vector<PDBEntry> getPDBId()
395   {
396     return pdbIds;
397   }
398
399   /**
400    * DOCUMENT ME!
401    * 
402    * @return DOCUMENT ME!
403    */
404   public String getDisplayId(boolean jvsuffix)
405   {
406     StringBuffer result = new StringBuffer(name);
407     if (jvsuffix)
408     {
409       result.append("/" + start + "-" + end);
410     }
411
412     return result.toString();
413   }
414
415   /**
416    * DOCUMENT ME!
417    * 
418    * @param name
419    *          DOCUMENT ME!
420    */
421   public void setName(String name)
422   {
423     this.name = name;
424     this.parseId();
425   }
426
427   /**
428    * DOCUMENT ME!
429    * 
430    * @return DOCUMENT ME!
431    */
432   public String getName()
433   {
434     return this.name;
435   }
436
437   /**
438    * DOCUMENT ME!
439    * 
440    * @param start
441    *          DOCUMENT ME!
442    */
443   public void setStart(int start)
444   {
445     this.start = start;
446   }
447
448   /**
449    * DOCUMENT ME!
450    * 
451    * @return DOCUMENT ME!
452    */
453   public int getStart()
454   {
455     return this.start;
456   }
457
458   /**
459    * DOCUMENT ME!
460    * 
461    * @param end
462    *          DOCUMENT ME!
463    */
464   public void setEnd(int end)
465   {
466     this.end = end;
467   }
468
469   /**
470    * DOCUMENT ME!
471    * 
472    * @return DOCUMENT ME!
473    */
474   public int getEnd()
475   {
476     return this.end;
477   }
478
479   /**
480    * DOCUMENT ME!
481    * 
482    * @return DOCUMENT ME!
483    */
484   public int getLength()
485   {
486     return this.sequence.length;
487   }
488
489   /**
490    * DOCUMENT ME!
491    * 
492    * @param seq
493    *          DOCUMENT ME!
494    */
495   public void setSequence(String seq)
496   {
497     this.sequence = seq.toCharArray();
498     checkValidRange();
499   }
500
501   public String getSequenceAsString()
502   {
503     return new String(sequence);
504   }
505
506   public String getSequenceAsString(int start, int end)
507   {
508     return new String(getSequence(start, end));
509   }
510
511   public char[] getSequence()
512   {
513     return sequence;
514   }
515
516   /*
517    * (non-Javadoc)
518    * 
519    * @see jalview.datamodel.SequenceI#getSequence(int, int)
520    */
521   public char[] getSequence(int start, int end)
522   {
523     if (start < 0)
524     {
525       start = 0;
526     }
527     // JBPNote - left to user to pad the result here (TODO:Decide on this
528     // policy)
529     if (start >= sequence.length)
530     {
531       return new char[0];
532     }
533
534     if (end >= sequence.length)
535     {
536       end = sequence.length;
537     }
538
539     char[] reply = new char[end - start];
540     System.arraycopy(sequence, start, reply, 0, end - start);
541
542     return reply;
543   }
544
545   @Override
546   public SequenceI getSubSequence(int start, int end)
547   {
548     if (start < 0)
549     {
550       start = 0;
551     }
552     char[] seq = getSequence(start, end);
553     if (seq.length == 0)
554     {
555       return null;
556     }
557     int nstart = findPosition(start);
558     int nend = findPosition(end) - 1;
559     // JBPNote - this is an incomplete copy.
560     SequenceI nseq = new Sequence(this.getName(), seq, nstart, nend);
561     nseq.setDescription(description);
562     if (datasetSequence != null)
563     {
564       nseq.setDatasetSequence(datasetSequence);
565     }
566     else
567     {
568       nseq.setDatasetSequence(this);
569     }
570     return nseq;
571   }
572
573   /**
574    * DOCUMENT ME!
575    * 
576    * @param i
577    *          DOCUMENT ME!
578    * 
579    * @return DOCUMENT ME!
580    */
581   public char getCharAt(int i)
582   {
583     if (i < sequence.length)
584     {
585       return sequence[i];
586     }
587     else
588     {
589       return ' ';
590     }
591   }
592
593   /**
594    * DOCUMENT ME!
595    * 
596    * @param desc
597    *          DOCUMENT ME!
598    */
599   public void setDescription(String desc)
600   {
601     this.description = desc;
602   }
603
604   /**
605    * DOCUMENT ME!
606    * 
607    * @return DOCUMENT ME!
608    */
609   public String getDescription()
610   {
611     return this.description;
612   }
613
614   /*
615    * (non-Javadoc)
616    * 
617    * @see jalview.datamodel.SequenceI#findIndex(int)
618    */
619   public int findIndex(int pos)
620   {
621     // returns the alignment position for a residue
622     int j = start;
623     int i = 0;
624     // Rely on end being at least as long as the length of the sequence.
625     while ((i < sequence.length) && (j <= end) && (j <= pos))
626     {
627       if (!jalview.util.Comparison.isGap(sequence[i]))
628       {
629         j++;
630       }
631
632       i++;
633     }
634
635     if ((j == end) && (j < pos))
636     {
637       return end + 1;
638     }
639     else
640     {
641       return i;
642     }
643   }
644
645   @Override
646   public int findPosition(int i)
647   {
648     int j = 0;
649     int pos = start;
650     int seqlen = sequence.length;
651     while ((j < i) && (j < seqlen))
652     {
653       if (!jalview.util.Comparison.isGap(sequence[j]))
654       {
655         pos++;
656       }
657
658       j++;
659     }
660
661     return pos;
662   }
663
664   /**
665    * Returns an int array where indices correspond to each residue in the
666    * sequence and the element value gives its position in the alignment
667    * 
668    * @return int[SequenceI.getEnd()-SequenceI.getStart()+1] or null if no
669    *         residues in SequenceI object
670    */
671   public int[] gapMap()
672   {
673     String seq = jalview.analysis.AlignSeq.extractGaps(
674             jalview.util.Comparison.GapChars, new String(sequence));
675     int[] map = new int[seq.length()];
676     int j = 0;
677     int p = 0;
678
679     while (j < sequence.length)
680     {
681       if (!jalview.util.Comparison.isGap(sequence[j]))
682       {
683         map[p++] = j;
684       }
685
686       j++;
687     }
688
689     return map;
690   }
691
692   @Override
693   public int[] findPositionMap()
694   {
695     int map[] = new int[sequence.length];
696     int j = 0;
697     int pos = start;
698     int seqlen = sequence.length;
699     while ((j < seqlen))
700     {
701       map[j] = pos;
702       if (!jalview.util.Comparison.isGap(sequence[j]))
703       {
704         pos++;
705       }
706
707       j++;
708     }
709     return map;
710   }
711
712   @Override
713   public List<int[]> getInsertions()
714   {
715     ArrayList<int[]> map = new ArrayList<int[]>();
716     int lastj = -1, j = 0;
717     int pos = start;
718     int seqlen = sequence.length;
719     while ((j < seqlen))
720     {
721       if (jalview.util.Comparison.isGap(sequence[j]))
722       {
723         if (lastj == -1)
724         {
725           lastj = j;
726         }
727       }
728       else
729       {
730         if (lastj != -1)
731         {
732           map.add(new int[]
733           { lastj, j - 1 });
734           lastj = -1;
735         }
736       }
737       j++;
738     }
739     if (lastj != -1)
740     {
741       map.add(new int[]
742       { lastj, j - 1 });
743       lastj = -1;
744     }
745     return map;
746   }
747
748   @Override
749   public void deleteChars(int i, int j)
750   {
751     int newstart = start, newend = end;
752     if (i >= sequence.length)
753     {
754       return;
755     }
756
757     char[] tmp = StringUtils.deleteChars(sequence, i, j);
758     boolean createNewDs = false;
759     // TODO: take a (second look) at the dataset creation validation method for
760     // the very large sequence case
761     int eindex = -1, sindex = -1;
762     boolean ecalc = false, scalc = false;
763     for (int s = i; s < j; s++)
764     {
765       if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
766       {
767         if (createNewDs)
768         {
769           newend--;
770         }
771         else
772         {
773           if (!scalc)
774           {
775             sindex = findIndex(start) - 1;
776             scalc = true;
777           }
778           if (sindex == s)
779           {
780             // delete characters including start of sequence
781             newstart = findPosition(j);
782             break; // don't need to search for any more residue characters.
783           }
784           else
785           {
786             // delete characters after start.
787             if (!ecalc)
788             {
789               eindex = findIndex(end) - 1;
790               ecalc = true;
791             }
792             if (eindex < j)
793             {
794               // delete characters at end of sequence
795               newend = findPosition(i - 1);
796               break; // don't need to search for any more residue characters.
797             }
798             else
799             {
800               createNewDs = true;
801               newend--; // decrease end position by one for the deleted residue
802               // and search further
803             }
804           }
805         }
806       }
807     }
808     // deletion occured in the middle of the sequence
809     if (createNewDs && this.datasetSequence != null)
810     {
811       // construct a new sequence
812       Sequence ds = new Sequence(datasetSequence);
813       // TODO: remove any non-inheritable properties ?
814       // TODO: create a sequence mapping (since there is a relation here ?)
815       ds.deleteChars(i, j);
816       datasetSequence = ds;
817     }
818     start = newstart;
819     end = newend;
820     sequence = tmp;
821   }
822
823   @Override
824   public void insertCharAt(int i, int length, char c)
825   {
826     char[] tmp = new char[sequence.length + length];
827
828     if (i >= sequence.length)
829     {
830       System.arraycopy(sequence, 0, tmp, 0, sequence.length);
831       i = sequence.length;
832     }
833     else
834     {
835       System.arraycopy(sequence, 0, tmp, 0, i);
836     }
837
838     int index = i;
839     while (length > 0)
840     {
841       tmp[index++] = c;
842       length--;
843     }
844
845     if (i < sequence.length)
846     {
847       System.arraycopy(sequence, i, tmp, index, sequence.length - i);
848     }
849
850     sequence = tmp;
851   }
852
853   @Override
854   public void insertCharAt(int i, char c)
855   {
856     insertCharAt(i, 1, c);
857   }
858
859   @Override
860   public String getVamsasId()
861   {
862     return vamsasId;
863   }
864
865   @Override
866   public void setVamsasId(String id)
867   {
868     vamsasId = id;
869   }
870
871   @Override
872   public void setDBRef(DBRefEntry[] dbref)
873   {
874     dbrefs = dbref;
875   }
876
877   @Override
878   public DBRefEntry[] getDBRef()
879   {
880     if (dbrefs == null && datasetSequence != null
881             && this != datasetSequence)
882     {
883       return datasetSequence.getDBRef();
884     }
885     return dbrefs;
886   }
887
888   @Override
889   public void addDBRef(DBRefEntry entry)
890   {
891     if (dbrefs == null)
892     {
893       dbrefs = new DBRefEntry[0];
894     }
895
896     int i, iSize = dbrefs.length;
897
898     for (i = 0; i < iSize; i++)
899     {
900       if (dbrefs[i].equalRef(entry))
901       {
902         if (entry.getMap() != null)
903         {
904           if (dbrefs[i].getMap() == null)
905           {
906             // overwrite with 'superior' entry that contains a mapping.
907             dbrefs[i] = entry;
908           }
909         }
910         return;
911       }
912     }
913
914     DBRefEntry[] temp = new DBRefEntry[iSize + 1];
915     System.arraycopy(dbrefs, 0, temp, 0, iSize);
916     temp[temp.length - 1] = entry;
917
918     dbrefs = temp;
919   }
920
921   @Override
922   public void setDatasetSequence(SequenceI seq)
923   {
924     datasetSequence = seq;
925   }
926
927   @Override
928   public SequenceI getDatasetSequence()
929   {
930     return datasetSequence;
931   }
932
933   @Override
934   public AlignmentAnnotation[] getAnnotation()
935   {
936     return annotation == null ? null : annotation
937             .toArray(new AlignmentAnnotation[annotation.size()]);
938   }
939
940
941   @Override
942   public boolean hasAnnotation(AlignmentAnnotation ann)
943   {
944     return annotation == null ? false : annotation.contains(ann);
945   }
946
947   @Override
948   public void addAlignmentAnnotation(AlignmentAnnotation annotation)
949   {
950     if (this.annotation == null)
951     {
952       this.annotation = new Vector<AlignmentAnnotation>();
953     }
954     if (!this.annotation.contains(annotation))
955     {
956       this.annotation.addElement(annotation);
957     }
958     annotation.setSequenceRef(this);
959   }
960
961   public void removeAlignmentAnnotation(AlignmentAnnotation annotation)
962   {
963     if (this.annotation != null)
964     {
965       this.annotation.removeElement(annotation);
966       if (this.annotation.size() == 0)
967       {
968         this.annotation = null;
969       }
970     }
971   }
972
973   /**
974    * test if this is a valid candidate for another sequence's dataset sequence.
975    * 
976    */
977   private boolean isValidDatasetSequence()
978   {
979     if (datasetSequence != null)
980     {
981       return false;
982     }
983     for (int i = 0; i < sequence.length; i++)
984     {
985       if (jalview.util.Comparison.isGap(sequence[i]))
986       {
987         return false;
988       }
989     }
990     return true;
991   }
992
993   @Override
994   public SequenceI deriveSequence()
995   {
996     SequenceI seq = new Sequence(this);
997     if (datasetSequence != null)
998     {
999       // duplicate current sequence with same dataset
1000       seq.setDatasetSequence(datasetSequence);
1001     }
1002     else
1003     {
1004       if (isValidDatasetSequence())
1005       {
1006         // Use this as dataset sequence
1007         seq.setDatasetSequence(this);
1008       }
1009       else
1010       {
1011         // Create a new, valid dataset sequence
1012         SequenceI ds = seq;
1013         ds.setSequence(AlignSeq.extractGaps(
1014                 jalview.util.Comparison.GapChars, new String(sequence)));
1015         setDatasetSequence(ds);
1016         ds.setSequenceFeatures(getSequenceFeatures());
1017         seq = this; // and return this sequence as the derived sequence.
1018       }
1019     }
1020     return seq;
1021   }
1022
1023   /*
1024    * (non-Javadoc)
1025    * 
1026    * @see jalview.datamodel.SequenceI#createDatasetSequence()
1027    */
1028   public SequenceI createDatasetSequence()
1029   {
1030     if (datasetSequence == null)
1031     {
1032       datasetSequence = new Sequence(getName(), AlignSeq.extractGaps(
1033               jalview.util.Comparison.GapChars, getSequenceAsString()),
1034               getStart(), getEnd());
1035       datasetSequence.setSequenceFeatures(getSequenceFeatures());
1036       datasetSequence.setDescription(getDescription());
1037       setSequenceFeatures(null);
1038       // move database references onto dataset sequence
1039       datasetSequence.setDBRef(getDBRef());
1040       setDBRef(null);
1041       datasetSequence.setPDBId(getPDBId());
1042       setPDBId(null);
1043       datasetSequence.updatePDBIds();
1044       if (annotation != null)
1045       {
1046         for (AlignmentAnnotation aa : annotation)
1047         {
1048           AlignmentAnnotation _aa = new AlignmentAnnotation(aa);
1049           _aa.sequenceRef = datasetSequence;
1050           _aa.adjustForAlignment(); // uses annotation's own record of
1051                                     // sequence-column mapping
1052           datasetSequence.addAlignmentAnnotation(_aa);
1053         }
1054       }
1055     }
1056     return datasetSequence;
1057   }
1058
1059   /*
1060    * (non-Javadoc)
1061    * 
1062    * @see
1063    * jalview.datamodel.SequenceI#setAlignmentAnnotation(AlignmmentAnnotation[]
1064    * annotations)
1065    */
1066   public void setAlignmentAnnotation(AlignmentAnnotation[] annotations)
1067   {
1068     if (annotation != null)
1069     {
1070       annotation.removeAllElements();
1071     }
1072     if (annotations != null)
1073     {
1074       for (int i = 0; i < annotations.length; i++)
1075       {
1076         if (annotations[i] != null)
1077         {
1078           addAlignmentAnnotation(annotations[i]);
1079         }
1080       }
1081     }
1082   }
1083
1084   @Override
1085   public AlignmentAnnotation[] getAnnotation(String label)
1086   {
1087     if (annotation == null || annotation.size() == 0)
1088     {
1089       return null;
1090     }
1091
1092     Vector subset = new Vector();
1093     Enumeration e = annotation.elements();
1094     while (e.hasMoreElements())
1095     {
1096       AlignmentAnnotation ann = (AlignmentAnnotation) e.nextElement();
1097       if (ann.label != null && ann.label.equals(label))
1098       {
1099         subset.addElement(ann);
1100       }
1101     }
1102     if (subset.size() == 0)
1103     {
1104       return null;
1105     }
1106     AlignmentAnnotation[] anns = new AlignmentAnnotation[subset.size()];
1107     int i = 0;
1108     e = subset.elements();
1109     while (e.hasMoreElements())
1110     {
1111       anns[i++] = (AlignmentAnnotation) e.nextElement();
1112     }
1113     subset.removeAllElements();
1114     return anns;
1115   }
1116
1117   @Override
1118   public boolean updatePDBIds()
1119   {
1120     if (datasetSequence != null)
1121     {
1122       // TODO: could merge DBRefs
1123       return datasetSequence.updatePDBIds();
1124     }
1125     if (dbrefs == null || dbrefs.length == 0)
1126     {
1127       return false;
1128     }
1129     Vector newpdb = new Vector();
1130     for (int i = 0; i < dbrefs.length; i++)
1131     {
1132       if (DBRefSource.PDB.equals(dbrefs[i].getSource()))
1133       {
1134         PDBEntry pdbe = new PDBEntry();
1135         pdbe.setId(dbrefs[i].getAccessionId());
1136         if (pdbIds == null || pdbIds.size() == 0)
1137         {
1138           newpdb.addElement(pdbe);
1139         }
1140         else
1141         {
1142           Enumeration en = pdbIds.elements();
1143           boolean matched = false;
1144           while (!matched && en.hasMoreElements())
1145           {
1146             PDBEntry anentry = (PDBEntry) en.nextElement();
1147             if (anentry.getId().equals(pdbe.getId()))
1148             {
1149               matched = true;
1150             }
1151           }
1152           if (!matched)
1153           {
1154             newpdb.addElement(pdbe);
1155           }
1156         }
1157       }
1158     }
1159     if (newpdb.size() > 0)
1160     {
1161       Enumeration en = newpdb.elements();
1162       while (en.hasMoreElements())
1163       {
1164         addPDBId((PDBEntry) en.nextElement());
1165       }
1166       return true;
1167     }
1168     return false;
1169   }
1170
1171   @Override
1172   public void transferAnnotation(SequenceI entry, Mapping mp)
1173   {
1174     if (datasetSequence != null)
1175     {
1176       datasetSequence.transferAnnotation(entry, mp);
1177       return;
1178     }
1179     if (entry.getDatasetSequence() != null)
1180     {
1181       transferAnnotation(entry.getDatasetSequence(), mp);
1182       return;
1183     }
1184     // transfer any new features from entry onto sequence
1185     if (entry.getSequenceFeatures() != null)
1186     {
1187
1188       SequenceFeature[] sfs = entry.getSequenceFeatures();
1189       for (int si = 0; si < sfs.length; si++)
1190       {
1191         SequenceFeature sf[] = (mp != null) ? mp.locateFeature(sfs[si])
1192                 : new SequenceFeature[]
1193                 { new SequenceFeature(sfs[si]) };
1194         if (sf != null && sf.length > 0)
1195         {
1196           for (int sfi = 0; sfi < sf.length; sfi++)
1197           {
1198             addSequenceFeature(sf[sfi]);
1199           }
1200         }
1201       }
1202     }
1203
1204     // transfer PDB entries
1205     if (entry.getPDBId() != null)
1206     {
1207       Enumeration e = entry.getPDBId().elements();
1208       while (e.hasMoreElements())
1209       {
1210         PDBEntry pdb = (PDBEntry) e.nextElement();
1211         addPDBId(pdb);
1212       }
1213     }
1214     // transfer database references
1215     DBRefEntry[] entryRefs = entry.getDBRef();
1216     if (entryRefs != null)
1217     {
1218       for (int r = 0; r < entryRefs.length; r++)
1219       {
1220         DBRefEntry newref = new DBRefEntry(entryRefs[r]);
1221         if (newref.getMap() != null && mp != null)
1222         {
1223           // remap ref using our local mapping
1224         }
1225         // we also assume all version string setting is done by dbSourceProxy
1226         /*
1227          * if (!newref.getSource().equalsIgnoreCase(dbSource)) {
1228          * newref.setSource(dbSource); }
1229          */
1230         addDBRef(newref);
1231       }
1232     }
1233   }
1234
1235   /**
1236    * @return The index (zero-based) on this sequence in the MSA. It returns
1237    *         {@code -1} if this information is not available.
1238    */
1239   public int getIndex()
1240   {
1241     return index;
1242   }
1243
1244   /**
1245    * Defines the position of this sequence in the MSA. Use the value {@code -1}
1246    * if this information is undefined.
1247    * 
1248    * @param The
1249    *          position for this sequence. This value is zero-based (zero for
1250    *          this first sequence)
1251    */
1252   public void setIndex(int value)
1253   {
1254     index = value;
1255   }
1256
1257   public void setRNA(RNA r)
1258   {
1259     rna = r;
1260   }
1261
1262   public RNA getRNA()
1263   {
1264     return rna;
1265   }
1266
1267   @Override
1268   public List<AlignmentAnnotation> getAlignmentAnnotations(String calcId,
1269           String label)
1270   {
1271     List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
1272     if (this.annotation != null)
1273     {
1274       for (AlignmentAnnotation ann : annotation)
1275       {
1276         if (ann.calcId != null && ann.calcId.equals(calcId)
1277                 && ann.label != null && ann.label.equals(label))
1278         {
1279           result.add(ann);
1280         }
1281       }
1282     }
1283     return result;
1284   }
1285
1286 }