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