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