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