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