580c85021a8850e8f4b423bf954677e9154c785d
[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     }
711     else
712     {
713       tmp = new char[sequence.length - j + i];
714       System.arraycopy(sequence, 0, tmp, 0, i);
715       System.arraycopy(sequence, j, tmp, i, sequence.length - j);
716     }
717     boolean createNewDs = false;
718     for (int s = i; s < j; s++)
719     {
720       if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
721       {
722         if (createNewDs)
723         {
724           newend--;
725         }
726         else
727         {
728           int sindex = findIndex(start) - 1;
729           if (sindex == s)
730           {
731             // delete characters including start of sequence
732             newstart = findPosition(j);
733             break; // don't need to search for any more residue characters.
734           }
735           else
736           {
737             // delete characters after start.
738             int eindex = findIndex(end) - 1;
739             if (eindex < j)
740             {
741               // delete characters at end of sequence
742               newend = findPosition(i - 1);
743               break; // don't need to search for any more residue characters.
744             }
745             else
746             {
747               createNewDs = true;
748               newend--; // decrease end position by one for the deleted residue
749               // and search further
750             }
751           }
752         }
753       }
754     }
755     // deletion occured in the middle of the sequence
756     if (createNewDs && this.datasetSequence != null)
757     {
758       // construct a new sequence
759       Sequence ds = new Sequence(datasetSequence);
760       // TODO: remove any non-inheritable properties ?
761       // TODO: create a sequence mapping (since there is a relation here ?)
762       ds.deleteChars(i, j);
763       datasetSequence = ds;
764     }
765     start = newstart;
766     end = newend;
767     sequence = tmp;
768   }
769
770   /**
771    * DOCUMENT ME!
772    * 
773    * @param i
774    *          DOCUMENT ME!
775    * @param c
776    *          DOCUMENT ME!
777    * @param chop
778    *          DOCUMENT ME!
779    */
780   public void insertCharAt(int i, int length, char c)
781   {
782     char[] tmp = new char[sequence.length + length];
783
784     if (i >= sequence.length)
785     {
786       System.arraycopy(sequence, 0, tmp, 0, sequence.length);
787       i = sequence.length;
788     }
789     else
790     {
791       System.arraycopy(sequence, 0, tmp, 0, i);
792     }
793
794     int index = i;
795     while (length > 0)
796     {
797       tmp[index++] = c;
798       length--;
799     }
800
801     if (i < sequence.length)
802     {
803       System.arraycopy(sequence, i, tmp, index, sequence.length - i);
804     }
805
806     sequence = tmp;
807   }
808
809   public void insertCharAt(int i, char c)
810   {
811     insertCharAt(i, 1, c);
812   }
813
814   public String getVamsasId()
815   {
816     return vamsasId;
817   }
818
819   public void setVamsasId(String id)
820   {
821     vamsasId = id;
822   }
823
824   public void setDBRef(DBRefEntry[] dbref)
825   {
826     dbrefs = dbref;
827   }
828
829   public DBRefEntry[] getDBRef()
830   {
831     if (dbrefs == null && datasetSequence != null
832             && this != datasetSequence)
833     {
834       return datasetSequence.getDBRef();
835     }
836     return dbrefs;
837   }
838
839   public void addDBRef(DBRefEntry entry)
840   {
841     if (dbrefs == null)
842     {
843       dbrefs = new DBRefEntry[0];
844     }
845
846     int i, iSize = dbrefs.length;
847
848     for (i = 0; i < iSize; i++)
849     {
850       if (dbrefs[i].equalRef(entry))
851       {
852         if (entry.getMap() != null)
853         {
854           if (dbrefs[i].getMap() == null)
855           {
856             // overwrite with 'superior' entry that contains a mapping.
857             dbrefs[i] = entry;
858           }
859         }
860         return;
861       }
862     }
863
864     DBRefEntry[] temp = new DBRefEntry[iSize + 1];
865     System.arraycopy(dbrefs, 0, temp, 0, iSize);
866     temp[temp.length - 1] = entry;
867
868     dbrefs = temp;
869   }
870
871   public void setDatasetSequence(SequenceI seq)
872   {
873     datasetSequence = seq;
874   }
875
876   public SequenceI getDatasetSequence()
877   {
878     return datasetSequence;
879   }
880
881   public AlignmentAnnotation[] getAnnotation()
882   {
883     if (annotation == null)
884     {
885       return null;
886     }
887
888     AlignmentAnnotation[] ret = new AlignmentAnnotation[annotation.size()];
889     for (int r = 0; r < ret.length; r++)
890     {
891       ret[r] = (AlignmentAnnotation) annotation.elementAt(r);
892     }
893
894     return ret;
895   }
896
897   public void addAlignmentAnnotation(AlignmentAnnotation annotation)
898   {
899     if (this.annotation == null)
900     {
901       this.annotation = new Vector();
902     }
903     if (!this.annotation.contains(annotation))
904     {
905       this.annotation.addElement(annotation);
906     }
907     annotation.setSequenceRef(this);
908   }
909
910   public void removeAlignmentAnnotation(AlignmentAnnotation annotation)
911   {
912     if (this.annotation != null)
913     {
914       this.annotation.removeElement(annotation);
915       if (this.annotation.size() == 0)
916         this.annotation = null;
917     }
918   }
919
920   /**
921    * test if this is a valid candidate for another sequence's dataset sequence.
922    * 
923    */
924   private boolean isValidDatasetSequence()
925   {
926     if (datasetSequence != null)
927     {
928       return false;
929     }
930     for (int i = 0; i < sequence.length; i++)
931     {
932       if (jalview.util.Comparison.isGap(sequence[i]))
933       {
934         return false;
935       }
936     }
937     return true;
938   }
939
940   /*
941    * (non-Javadoc)
942    * 
943    * @see jalview.datamodel.SequenceI#deriveSequence()
944    */
945   public SequenceI deriveSequence()
946   {
947     SequenceI seq = new Sequence(this);
948     if (datasetSequence != null)
949     {
950       // duplicate current sequence with same dataset
951       seq.setDatasetSequence(datasetSequence);
952     }
953     else
954     {
955       if (isValidDatasetSequence())
956       {
957         // Use this as dataset sequence
958         seq.setDatasetSequence(this);
959       }
960       else
961       {
962         // Create a new, valid dataset sequence
963         SequenceI ds = seq;
964         ds.setSequence(AlignSeq.extractGaps(
965                 jalview.util.Comparison.GapChars, new String(sequence)));
966         setDatasetSequence(ds);
967         ds.setSequenceFeatures(getSequenceFeatures());
968         seq = this; // and return this sequence as the derived sequence.
969       }
970     }
971     return seq;
972   }
973
974   /*
975    * (non-Javadoc)
976    * 
977    * @see jalview.datamodel.SequenceI#createDatasetSequence()
978    */
979   public SequenceI createDatasetSequence()
980   {
981     if (datasetSequence == null)
982     {
983       datasetSequence = new Sequence(getName(), AlignSeq.extractGaps(
984               jalview.util.Comparison.GapChars, getSequenceAsString()),
985               getStart(), getEnd());
986       datasetSequence.setSequenceFeatures(getSequenceFeatures());
987       datasetSequence.setDescription(getDescription());
988       setSequenceFeatures(null);
989       // move database references onto dataset sequence
990       datasetSequence.setDBRef(getDBRef());
991       setDBRef(null);
992       datasetSequence.setPDBId(getPDBId());
993       setPDBId(null);
994       datasetSequence.updatePDBIds();
995     }
996     return datasetSequence;
997   }
998
999   /*
1000    * (non-Javadoc)
1001    * 
1002    * @see
1003    * jalview.datamodel.SequenceI#setAlignmentAnnotation(AlignmmentAnnotation[]
1004    * annotations)
1005    */
1006   public void setAlignmentAnnotation(AlignmentAnnotation[] annotations)
1007   {
1008     if (annotation != null)
1009     {
1010       annotation.removeAllElements();
1011     }
1012     if (annotations != null)
1013     {
1014       for (int i = 0; i < annotations.length; i++)
1015       {
1016         if (annotations[i] != null)
1017           addAlignmentAnnotation(annotations[i]);
1018       }
1019     }
1020   }
1021
1022   /*
1023    * (non-Javadoc)
1024    * 
1025    * @see jalview.datamodel.SequenceI#getAnnotation(java.lang.String)
1026    */
1027   public AlignmentAnnotation[] getAnnotation(String label)
1028   {
1029     if (annotation == null || annotation.size() == 0)
1030     {
1031       return null;
1032     }
1033
1034     Vector subset = new Vector();
1035     Enumeration e = annotation.elements();
1036     while (e.hasMoreElements())
1037     {
1038       AlignmentAnnotation ann = (AlignmentAnnotation) e.nextElement();
1039       if (ann.label != null && ann.label.equals(label))
1040       {
1041         subset.addElement(ann);
1042       }
1043     }
1044     if (subset.size() == 0)
1045     {
1046       return null;
1047     }
1048     AlignmentAnnotation[] anns = new AlignmentAnnotation[subset.size()];
1049     int i = 0;
1050     e = subset.elements();
1051     while (e.hasMoreElements())
1052     {
1053       anns[i++] = (AlignmentAnnotation) e.nextElement();
1054     }
1055     subset.removeAllElements();
1056     return anns;
1057   }
1058
1059   public boolean updatePDBIds()
1060   {
1061     if (datasetSequence != null)
1062     {
1063       // TODO: could merge DBRefs
1064       return datasetSequence.updatePDBIds();
1065     }
1066     if (dbrefs == null || dbrefs.length == 0)
1067     {
1068       return false;
1069     }
1070     Vector newpdb = new Vector();
1071     for (int i = 0; i < dbrefs.length; i++)
1072     {
1073       if (DBRefSource.PDB.equals(dbrefs[i].getSource()))
1074       {
1075         PDBEntry pdbe = new PDBEntry();
1076         pdbe.setId(dbrefs[i].getAccessionId());
1077         if (pdbIds == null || pdbIds.size() == 0)
1078         {
1079           newpdb.addElement(pdbe);
1080         }
1081         else
1082         {
1083           Enumeration en = pdbIds.elements();
1084           boolean matched = false;
1085           while (!matched && en.hasMoreElements())
1086           {
1087             PDBEntry anentry = (PDBEntry) en.nextElement();
1088             if (anentry.getId().equals(pdbe.getId()))
1089             {
1090               matched = true;
1091             }
1092           }
1093           if (!matched)
1094           {
1095             newpdb.addElement(pdbe);
1096           }
1097         }
1098       }
1099     }
1100     if (newpdb.size() > 0)
1101     {
1102       Enumeration en = newpdb.elements();
1103       while (en.hasMoreElements())
1104       {
1105         addPDBId((PDBEntry) en.nextElement());
1106       }
1107       return true;
1108     }
1109     return false;
1110   }
1111
1112   /*
1113    * (non-Javadoc)
1114    * 
1115    * @see
1116    * jalview.datamodel.SequenceI#transferAnnotation(jalview.datamodel.SequenceI,
1117    * jalview.datamodel.Mapping)
1118    */
1119   public void transferAnnotation(SequenceI entry, Mapping mp)
1120   {
1121     if (datasetSequence != null)
1122     {
1123       datasetSequence.transferAnnotation(entry, mp);
1124       return;
1125     }
1126     if (entry.getDatasetSequence() != null)
1127     {
1128       transferAnnotation(entry.getDatasetSequence(), mp);
1129       return;
1130     }
1131     // transfer any new features from entry onto sequence
1132     if (entry.getSequenceFeatures() != null)
1133     {
1134
1135       SequenceFeature[] sfs = entry.getSequenceFeatures();
1136       for (int si = 0; si < sfs.length; si++)
1137       {
1138         SequenceFeature sf[] = (mp != null) ? mp.locateFeature(sfs[si])
1139                 : new SequenceFeature[]
1140                 { new SequenceFeature(sfs[si]) };
1141         if (sf != null && sf.length > 0)
1142         {
1143           for (int sfi = 0; sfi < sf.length; sfi++)
1144           {
1145             addSequenceFeature(sf[sfi]);
1146           }
1147         }
1148       }
1149     }
1150
1151     // transfer PDB entries
1152     if (entry.getPDBId() != null)
1153     {
1154       Enumeration e = entry.getPDBId().elements();
1155       while (e.hasMoreElements())
1156       {
1157         PDBEntry pdb = (PDBEntry) e.nextElement();
1158         addPDBId(pdb);
1159       }
1160     }
1161     // transfer database references
1162     DBRefEntry[] entryRefs = entry.getDBRef();
1163     if (entryRefs != null)
1164     {
1165       for (int r = 0; r < entryRefs.length; r++)
1166       {
1167         DBRefEntry newref = new DBRefEntry(entryRefs[r]);
1168         if (newref.getMap() != null && mp != null)
1169         {
1170           // remap ref using our local mapping
1171         }
1172         // we also assume all version string setting is done by dbSourceProxy
1173         /*
1174          * if (!newref.getSource().equalsIgnoreCase(dbSource)) {
1175          * newref.setSource(dbSource); }
1176          */
1177         addDBRef(newref);
1178       }
1179     }
1180   }
1181
1182   /**
1183    * @return The index (zero-based) on this sequence in the MSA. It returns
1184    *         {@code -1} if this information is not available.
1185    */
1186   public int getIndex()
1187   {
1188     return index;
1189   }
1190
1191   /**
1192    * Defines the position of this sequence in the MSA. Use the value {@code -1}
1193    * if this information is undefined.
1194    * 
1195    * @param The
1196    *          position for this sequence. This value is zero-based (zero for
1197    *          this first sequence)
1198    */
1199   public void setIndex(int value)
1200   {
1201     index = value;
1202   }
1203 }