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