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