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