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