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