use new secstructure process algorithm
[jalview.git] / src / jalview / datamodel / AlignmentAnnotation.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.Rna;
21 import jalview.analysis.SecStrConsensus.SimpleBP;
22
23 import jalview.analysis.WUSSParseException;
24
25 import java.util.ArrayList;
26 import java.util.Enumeration;
27 import java.util.Hashtable;
28
29 import fr.orsay.lri.varna.models.rna.RNA;
30
31 /**
32  * DOCUMENT ME!
33  * 
34  * @author $author$
35  * @version $Revision$
36  */
37 public class AlignmentAnnotation
38 {
39   /**
40    * If true, this annotations is calculated every edit, eg consensus, quality
41    * or conservation graphs
42    */
43   public boolean autoCalculated = false;
44
45   public String annotationId;
46
47   public SequenceI sequenceRef;
48
49   /** DOCUMENT ME!! */
50   public String label;
51
52   /** DOCUMENT ME!! */
53   public String description;
54
55   /** DOCUMENT ME!! */
56   public Annotation[] annotations;
57   
58   
59
60   public ArrayList<SimpleBP> bps=null;
61   /**
62    * RNA secondary structure contact positions
63    */
64   public SequenceFeature[] _rnasecstr = null;
65
66   /**
67    * position of annotation resulting in invalid WUSS parsing or -1
68    */
69   private long invalidrnastruc = -1;
70
71   /**
72    * Updates the _rnasecstr field Determines the positions that base pair and
73    * the positions of helices based on secondary structure from a Stockholm file
74    * 
75    * @param RNAannot
76    */
77   private void _updateRnaSecStr(CharSequence RNAannot)
78   {
79     try {
80     _rnasecstr = Rna.GetBasePairs(RNAannot);
81     bps = Rna.GetModeleBP(RNAannot);
82     invalidrnastruc=-1;
83     }
84     catch (WUSSParseException px)
85     {
86       // DEBUG System.out.println(px);
87       invalidrnastruc=px.getProblemPos();
88     }
89     if (invalidrnastruc > -1)
90     {
91       return;
92     }
93     Rna.HelixMap(_rnasecstr);
94     // setRNAStruc(RNAannot);
95
96     if (_rnasecstr != null && _rnasecstr.length > 0)
97     {
98       // show all the RNA secondary structure annotation symbols.
99       isrna = true;
100       showAllColLabels = true;
101       scaleColLabel = true;
102     }
103     // System.out.println("featuregroup " + _rnasecstr[0].getFeatureGroup());
104   }
105
106   public java.util.Hashtable sequenceMapping;
107
108   /** DOCUMENT ME!! */
109   public float graphMin;
110
111   /** DOCUMENT ME!! */
112   public float graphMax;
113
114   /**
115    * Score associated with label and description.
116    */
117   public double score = Double.NaN;
118
119   /**
120    * flag indicating if annotation has a score.
121    */
122   public boolean hasScore = false;
123
124   public GraphLine threshold;
125
126   // Graphical hints and tips
127
128   /** Can this row be edited by the user ? */
129   public boolean editable = false;
130
131   /** Indicates if annotation has a graphical symbol track */
132   public boolean hasIcons; //
133
134   /** Indicates if annotation has a text character label */
135   public boolean hasText;
136
137   /** is the row visible */
138   public boolean visible = true;
139
140   public int graphGroup = -1;
141
142   /** Displayed height of row in pixels */
143   public int height = 0;
144
145   public int graph = 0;
146
147   public int graphHeight = 40;
148
149   public boolean padGaps = false;
150
151   public static final int NO_GRAPH = 0;
152
153   public static final int BAR_GRAPH = 1;
154
155   public static final int LINE_GRAPH = 2;
156
157   public boolean belowAlignment = true;
158
159   public SequenceGroup groupRef = null;
160
161   /**
162    * display every column label, even if there is a row of identical labels
163    */
164   public boolean showAllColLabels = false;
165
166   /**
167    * scale the column label to fit within the alignment column.
168    */
169   public boolean scaleColLabel = false;
170
171   /**
172    * centre the column labels relative to the alignment column
173    */
174   public boolean centreColLabels = false;
175
176   private boolean isrna;
177
178   /*
179    * (non-Javadoc)
180    * 
181    * @see java.lang.Object#finalize()
182    */
183   protected void finalize() throws Throwable
184   {
185     sequenceRef = null;
186     groupRef = null;
187     super.finalize();
188   }
189
190   public static int getGraphValueFromString(String string)
191   {
192     if (string.equalsIgnoreCase("BAR_GRAPH"))
193     {
194       return BAR_GRAPH;
195     }
196     else if (string.equalsIgnoreCase("LINE_GRAPH"))
197     {
198       return LINE_GRAPH;
199     }
200     else
201     {
202       return NO_GRAPH;
203     }
204   }
205
206   /**
207    * Creates a new AlignmentAnnotation object.
208    * 
209    * @param label
210    *          short label shown under sequence labels
211    * @param description
212    *          text displayed on mouseover
213    * @param annotations
214    *          set of positional annotation elements
215    */
216   public AlignmentAnnotation(String label, String description,
217           Annotation[] annotations)
218   {
219     // always editable?
220     editable = true;
221     this.label = label;
222     this.description = description;
223     this.annotations = annotations;
224
225     validateRangeAndDisplay();
226   }
227
228   /**
229    * Checks if annotation labels represent secondary structures
230    * 
231    */
232   void areLabelsSecondaryStructure()
233   {
234     boolean nonSSLabel = false;
235     isrna = false;
236     StringBuffer rnastring = new StringBuffer();
237
238     char firstChar = 0;
239     for (int i = 0; i < annotations.length; i++)
240     {
241       if (annotations[i] == null)
242       {
243         continue;
244       }
245       if (annotations[i].secondaryStructure == 'H'
246               || annotations[i].secondaryStructure == 'E')
247       {
248         hasIcons |= true;
249       }
250       else
251       // Check for RNA secondary structure
252       {
253           //System.out.println(annotations[i].secondaryStructure);
254         if (annotations[i].secondaryStructure == '('
255                         || annotations[i].secondaryStructure == '['
256                         || annotations[i].secondaryStructure == '<'
257                         || annotations[i].secondaryStructure == '{'
258                         || annotations[i].secondaryStructure == 'A'
259                         || annotations[i].secondaryStructure == 'B'
260                         || annotations[i].secondaryStructure == 'C'
261                         || annotations[i].secondaryStructure == 'D'
262                         || annotations[i].secondaryStructure == '1'
263                         || annotations[i].secondaryStructure == 'F'
264                         || annotations[i].secondaryStructure == 'G'
265                         || annotations[i].secondaryStructure == '2'
266                         || annotations[i].secondaryStructure == 'I'
267                         || annotations[i].secondaryStructure == 'J'
268                         || annotations[i].secondaryStructure == 'K'
269                         || annotations[i].secondaryStructure == 'L'
270                         || annotations[i].secondaryStructure == 'M'
271                         || annotations[i].secondaryStructure == 'N'
272                         || annotations[i].secondaryStructure == 'O'
273                         || annotations[i].secondaryStructure == 'P'
274                         || annotations[i].secondaryStructure == 'Q'
275                         || annotations[i].secondaryStructure == 'R'
276                         || annotations[i].secondaryStructure == 'S'
277                         || annotations[i].secondaryStructure == 'T'
278                         || annotations[i].secondaryStructure == 'U'
279                         || annotations[i].secondaryStructure == 'V'
280                         || annotations[i].secondaryStructure == 'W'
281                         || annotations[i].secondaryStructure == 'X'
282                         || annotations[i].secondaryStructure == 'Y'
283                         || annotations[i].secondaryStructure == 'Z')
284         {
285           hasIcons |= true;
286           isrna |= true;
287         }
288       }
289
290       // System.out.println("displaychar " + annotations[i].displayCharacter);
291
292       if (annotations[i].displayCharacter == null
293               || annotations[i].displayCharacter.length() == 0)
294       {
295         rnastring.append('.');
296         continue;
297       }
298       if (annotations[i].displayCharacter.length() == 1)
299       {
300         firstChar = annotations[i].displayCharacter.charAt(0);
301         // check to see if it looks like a sequence or is secondary structure
302         // labelling.
303         if (annotations[i].secondaryStructure != ' '
304                 && !hasIcons
305                 &&
306                 // Uncomment to only catch case where
307                 // displayCharacter==secondary
308                 // Structure
309                 // to correctly redisplay SS annotation imported from Stockholm,
310                 // exported to JalviewXML and read back in again.
311                 // &&
312                 // annotations[i].displayCharacter.charAt(0)==annotations[i].secondaryStructure
313                 firstChar != ' '
314                 && firstChar != 'H'
315                 && firstChar != 'E'
316                 && firstChar != '('
317                 && firstChar != '['
318                 && firstChar != '>'
319                 && firstChar != '{'
320                 && firstChar != 'A'
321                 && firstChar != 'B'
322                 && firstChar != 'C'
323                 && firstChar != 'D'
324                 && firstChar != '1'
325                 && firstChar != 'F'
326                 && firstChar != 'G'
327                 && firstChar != '2'
328                 && firstChar != 'I'
329                 && firstChar != 'J'
330                 && firstChar != 'K'
331                 && firstChar != 'L'
332                 && firstChar != 'M'
333                 && firstChar != 'N'
334                 && firstChar != 'O'
335                 && firstChar != 'P'
336                 && firstChar != 'Q'
337                 && firstChar != 'R'
338                 && firstChar != 'S'
339                 && firstChar != 'T'
340                 && firstChar != 'U'
341                 && firstChar != 'V'
342                 && firstChar != 'W'
343                 && firstChar != 'X'
344                 && firstChar != 'Y'
345                 && firstChar != 'Z'
346                 && firstChar != '-'
347                 && firstChar < jalview.schemes.ResidueProperties.aaIndex.length)
348         {
349           if (jalview.schemes.ResidueProperties.aaIndex[firstChar] < 23) // TODO:
350                                                                          // parameterise
351                                                                          // to
352                                                                          // gap
353                                                                          // symbol
354                                                                          // number
355           {
356             nonSSLabel = true;
357           }
358         }
359       }
360       else
361       {
362         rnastring.append(annotations[i].displayCharacter.charAt(1));
363       }
364
365       if (annotations[i].displayCharacter.length() > 0)
366       {
367         hasText = true;
368       }
369     }
370
371     if (nonSSLabel)
372     {
373       hasIcons = false;
374       for (int j = 0; j < annotations.length; j++)
375       {
376         if (annotations[j] != null
377                 && annotations[j].secondaryStructure != ' ')
378         {
379           annotations[j].displayCharacter = String
380                   .valueOf(annotations[j].secondaryStructure);
381           annotations[j].secondaryStructure = ' ';
382         }
383
384       }
385     }
386     else
387     {
388       if (isrna)
389       {
390         _updateRnaSecStr(new AnnotCharSequence());
391       }
392     }
393
394     annotationId = this.hashCode() + "";
395   }
396
397   /**
398    * flyweight access to positions in the alignment annotation row for RNA
399    * processing
400    * 
401    * @author jimp
402    * 
403    */
404   private class AnnotCharSequence implements CharSequence
405   {
406     int offset = 0;
407
408     int max = 0;
409
410     public AnnotCharSequence()
411     {
412       this(0, annotations.length);
413     }
414
415     public AnnotCharSequence(int start, int end)
416     {
417       offset = start;
418       max = end;
419     }
420
421     @Override
422     public CharSequence subSequence(int start, int end)
423     {
424       return new AnnotCharSequence(offset + start, offset + end);
425     }
426
427     @Override
428     public int length()
429     {
430       return max - offset;
431     }
432
433     @Override
434     public char charAt(int index)
435     {
436       String dc;
437       return ((index + offset < 0) || (index + offset) >= max
438               || annotations[index + offset] == null || (dc = annotations[index
439               + offset].displayCharacter.trim()).length() < 1) ? '.' : dc
440               .charAt(0);
441     }
442
443     public String toString()
444     {
445       char[] string = new char[max - offset];
446       int mx = annotations.length;
447
448       for (int i = offset; i < mx; i++)
449       {
450         String dc;
451         string[i] = (annotations[i] == null || (dc = annotations[i].displayCharacter
452                 .trim()).length() < 1) ? '.' : dc.charAt(0);
453       }
454       return new String(string);
455     }
456   };
457
458   private long _lastrnaannot = -1;
459
460   public String getRNAStruc()
461   {
462     if (isrna)
463     {
464       String rnastruc = new AnnotCharSequence().toString();
465       if (_lastrnaannot != rnastruc.hashCode())
466       {
467         // ensure rna structure contacts are up to date
468         _lastrnaannot = rnastruc.hashCode();
469         _updateRnaSecStr(rnastruc);
470       }
471       return rnastruc;
472     }
473     return null;
474   }
475
476   /**
477    * Creates a new AlignmentAnnotation object.
478    * 
479    * @param label
480    *          DOCUMENT ME!
481    * @param description
482    *          DOCUMENT ME!
483    * @param annotations
484    *          DOCUMENT ME!
485    * @param min
486    *          DOCUMENT ME!
487    * @param max
488    *          DOCUMENT ME!
489    * @param winLength
490    *          DOCUMENT ME!
491    */
492   public AlignmentAnnotation(String label, String description,
493           Annotation[] annotations, float min, float max, int graphType)
494   {
495     // graphs are not editable
496     editable = graphType == 0;
497
498     this.label = label;
499     this.description = description;
500     this.annotations = annotations;
501     graph = graphType;
502     graphMin = min;
503     graphMax = max;
504     validateRangeAndDisplay();
505   }
506
507   /**
508    * checks graphMin and graphMax, secondary structure symbols, sets graphType
509    * appropriately, sets null labels to the empty string if appropriate.
510    */
511   public void validateRangeAndDisplay()
512   {
513
514     if (annotations == null)
515     {
516       visible = false; // try to prevent renderer from displaying.
517       return; // this is a non-annotation row annotation - ie a sequence score.
518     }
519
520     int graphType = graph;
521     float min = graphMin;
522     float max = graphMax;
523     boolean drawValues = true;
524     _linecolour = null;
525     if (min == max)
526     {
527       min = 999999999;
528       for (int i = 0; i < annotations.length; i++)
529       {
530         if (annotations[i] == null)
531         {
532           continue;
533         }
534
535         if (drawValues && annotations[i].displayCharacter != null
536                 && annotations[i].displayCharacter.length() > 1)
537         {
538           drawValues = false;
539         }
540
541         if (annotations[i].value > max)
542         {
543           max = annotations[i].value;
544         }
545
546         if (annotations[i].value < min)
547         {
548           min = annotations[i].value;
549         }
550         if (_linecolour == null && annotations[i].colour != null)
551         {
552           _linecolour = annotations[i].colour;
553         }
554       }
555       // ensure zero is origin for min/max ranges on only one side of zero
556       if (min > 0)
557       {
558         min = 0;
559       }
560       else
561       {
562         if (max < 0)
563         {
564           max = 0;
565         }
566       }
567     }
568
569     graphMin = min;
570     graphMax = max;
571
572     areLabelsSecondaryStructure();
573
574     if (!drawValues && graphType != NO_GRAPH)
575     {
576       for (int i = 0; i < annotations.length; i++)
577       {
578         if (annotations[i] != null)
579         {
580           annotations[i].displayCharacter = "X";
581         }
582       }
583     }
584   }
585
586   /**
587    * Copy constructor creates a new independent annotation row with the same
588    * associated sequenceRef
589    * 
590    * @param annotation
591    */
592   public AlignmentAnnotation(AlignmentAnnotation annotation)
593   {
594     this.label = new String(annotation.label);
595     if (annotation.description != null)
596       this.description = new String(annotation.description);
597     this.graphMin = annotation.graphMin;
598     this.graphMax = annotation.graphMax;
599     this.graph = annotation.graph;
600     this.graphHeight = annotation.graphHeight;
601     this.graphGroup = annotation.graphGroup;
602     this.groupRef = annotation.groupRef;
603     this.editable = annotation.editable;
604     this.autoCalculated = annotation.autoCalculated;
605     this.hasIcons = annotation.hasIcons;
606     this.hasText = annotation.hasText;
607     this.height = annotation.height;
608     this.label = annotation.label;
609     this.padGaps = annotation.padGaps;
610     this.visible = annotation.visible;
611     this.centreColLabels = annotation.centreColLabels;
612     this.scaleColLabel = annotation.scaleColLabel;
613     this.showAllColLabels = annotation.showAllColLabels;
614     this.calcId = annotation.calcId;
615     if (this.hasScore = annotation.hasScore)
616     {
617       this.score = annotation.score;
618     }
619     if (annotation.threshold != null)
620     {
621       threshold = new GraphLine(annotation.threshold);
622     }
623     if (annotation.annotations != null)
624     {
625       Annotation[] ann = annotation.annotations;
626       this.annotations = new Annotation[ann.length];
627       for (int i = 0; i < ann.length; i++)
628       {
629         if (ann[i] != null)
630         {
631           annotations[i] = new Annotation(ann[i]);
632           if (_linecolour != null)
633           {
634             _linecolour = annotations[i].colour;
635           }
636         }
637       }
638       ;
639       if (annotation.sequenceRef != null)
640       {
641         this.sequenceRef = annotation.sequenceRef;
642         if (annotation.sequenceMapping != null)
643         {
644           Integer p = null;
645           sequenceMapping = new Hashtable();
646           Enumeration pos = annotation.sequenceMapping.keys();
647           while (pos.hasMoreElements())
648           {
649             // could optimise this!
650             p = (Integer) pos.nextElement();
651             Annotation a = (Annotation) annotation.sequenceMapping.get(p);
652             if (a == null)
653             {
654               continue;
655             }
656             for (int i = 0; i < ann.length; i++)
657             {
658               if (ann[i] == a)
659               {
660                 sequenceMapping.put(p, annotations[i]);
661               }
662             }
663           }
664         }
665         else
666         {
667           this.sequenceMapping = null;
668         }
669       }
670     }
671     // TODO: check if we need to do this: JAL-952
672     // if (this.isrna=annotation.isrna)
673     {
674       // _rnasecstr=new SequenceFeature[annotation._rnasecstr];
675     }
676     validateRangeAndDisplay(); // construct hashcodes, etc.
677   }
678
679   /**
680    * clip the annotation to the columns given by startRes and endRes (inclusive)
681    * and prune any existing sequenceMapping to just those columns.
682    * 
683    * @param startRes
684    * @param endRes
685    */
686   public void restrict(int startRes, int endRes)
687   {
688     if (annotations == null)
689     {
690       // non-positional
691       return;
692     }
693     if (startRes < 0)
694       startRes = 0;
695     if (startRes >= annotations.length)
696       startRes = annotations.length - 1;
697     if (endRes >= annotations.length)
698       endRes = annotations.length - 1;
699     if (annotations == null)
700       return;
701     Annotation[] temp = new Annotation[endRes - startRes + 1];
702     if (startRes < annotations.length)
703     {
704       System.arraycopy(annotations, startRes, temp, 0, endRes - startRes
705               + 1);
706     }
707     if (sequenceRef != null)
708     {
709       // Clip the mapping, if it exists.
710       int spos = sequenceRef.findPosition(startRes);
711       int epos = sequenceRef.findPosition(endRes);
712       if (sequenceMapping != null)
713       {
714         Hashtable newmapping = new Hashtable();
715         Enumeration e = sequenceMapping.keys();
716         while (e.hasMoreElements())
717         {
718           Integer pos = (Integer) e.nextElement();
719           if (pos.intValue() >= spos && pos.intValue() <= epos)
720           {
721             newmapping.put(pos, sequenceMapping.get(pos));
722           }
723         }
724         sequenceMapping.clear();
725         sequenceMapping = newmapping;
726       }
727     }
728     annotations = temp;
729   }
730
731   /**
732    * set the annotation row to be at least length Annotations
733    * 
734    * @param length
735    *          minimum number of columns required in the annotation row
736    * @return false if the annotation row is greater than length
737    */
738   public boolean padAnnotation(int length)
739   {
740     if (annotations == null)
741     {
742       return true; // annotation row is correct - null == not visible and
743       // undefined length
744     }
745     if (annotations.length < length)
746     {
747       Annotation[] na = new Annotation[length];
748       System.arraycopy(annotations, 0, na, 0, annotations.length);
749       annotations = na;
750       return true;
751     }
752     return annotations.length > length;
753
754   }
755
756   /**
757    * DOCUMENT ME!
758    * 
759    * @return DOCUMENT ME!
760    */
761   public String toString()
762   {
763     StringBuffer buffer = new StringBuffer();
764
765     for (int i = 0; i < annotations.length; i++)
766     {
767       if (annotations[i] != null)
768       {
769         if (graph != 0)
770         {
771           buffer.append(annotations[i].value);
772         }
773         else if (hasIcons)
774         {
775           buffer.append(annotations[i].secondaryStructure);
776         }
777         else
778         {
779           buffer.append(annotations[i].displayCharacter);
780         }
781       }
782
783       buffer.append(", ");
784     }
785     // TODO: remove disgusting hack for 'special' treatment of consensus line.
786     if (label.indexOf("Consensus") == 0)
787     {
788       buffer.append("\n");
789
790       for (int i = 0; i < annotations.length; i++)
791       {
792         if (annotations[i] != null)
793         {
794           buffer.append(annotations[i].description);
795         }
796
797         buffer.append(", ");
798       }
799     }
800
801     return buffer.toString();
802   }
803
804   public void setThreshold(GraphLine line)
805   {
806     threshold = line;
807   }
808
809   public GraphLine getThreshold()
810   {
811     return threshold;
812   }
813
814   /**
815    * Attach the annotation to seqRef, starting from startRes position. If
816    * alreadyMapped is true then the indices of the annotation[] array are
817    * sequence positions rather than alignment column positions.
818    * 
819    * @param seqRef
820    * @param startRes
821    * @param alreadyMapped
822    */
823   public void createSequenceMapping(SequenceI seqRef, int startRes,
824           boolean alreadyMapped)
825   {
826
827     if (seqRef == null)
828     {
829       return;
830     }
831     sequenceRef = seqRef;
832     if (annotations == null)
833     {
834       return;
835     }
836     sequenceMapping = new java.util.Hashtable();
837
838     int seqPos;
839
840     for (int i = 0; i < annotations.length; i++)
841     {
842       if (annotations[i] != null)
843       {
844         if (alreadyMapped)
845         {
846           seqPos = seqRef.findPosition(i);
847         }
848         else
849         {
850           seqPos = i + startRes;
851         }
852
853         sequenceMapping.put(new Integer(seqPos), annotations[i]);
854       }
855     }
856
857   }
858
859   public void adjustForAlignment()
860   {
861     if (sequenceRef == null)
862       return;
863
864     if (annotations == null)
865     {
866       return;
867     }
868
869     int a = 0, aSize = sequenceRef.getLength();
870
871     if (aSize == 0)
872     {
873       // Its been deleted
874       return;
875     }
876
877     int position;
878     Annotation[] temp = new Annotation[aSize];
879     Integer index;
880
881     for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
882     {
883       index = new Integer(a);
884       if (sequenceMapping.containsKey(index))
885       {
886         position = sequenceRef.findIndex(a) - 1;
887
888         temp[position] = (Annotation) sequenceMapping.get(index);
889       }
890     }
891
892     annotations = temp;
893   }
894
895   /**
896    * remove any null entries in annotation row and return the number of non-null
897    * annotation elements.
898    * 
899    * @return
900    */
901   public int compactAnnotationArray()
902   {
903     int i = 0, iSize = annotations.length;
904     while (i < iSize)
905     {
906       if (annotations[i] == null)
907       {
908         if (i + 1 < iSize)
909           System.arraycopy(annotations, i + 1, annotations, i, iSize - i
910                   - 1);
911         iSize--;
912       }
913       else
914       {
915         i++;
916       }
917     }
918     Annotation[] ann = annotations;
919     annotations = new Annotation[i];
920     System.arraycopy(ann, 0, annotations, 0, i);
921     ann = null;
922     return iSize;
923   }
924
925   /**
926    * Associate this annotion with the aligned residues of a particular sequence.
927    * sequenceMapping will be updated in the following way: null sequenceI -
928    * existing mapping will be discarded but annotations left in mapped
929    * positions. valid sequenceI not equal to current sequenceRef: mapping is
930    * discarded and rebuilt assuming 1:1 correspondence TODO: overload with
931    * parameter to specify correspondence between current and new sequenceRef
932    * 
933    * @param sequenceI
934    */
935   public void setSequenceRef(SequenceI sequenceI)
936   {
937     if (sequenceI != null)
938     {
939       if (sequenceRef != null)
940       {
941         if (sequenceRef != sequenceI
942                 && !sequenceRef.equals(sequenceI)
943                 && sequenceRef.getDatasetSequence() != sequenceI
944                         .getDatasetSequence())
945         {
946           // if sequenceRef isn't intersecting with sequenceI
947           // throw away old mapping and reconstruct.
948           sequenceRef = null;
949           if (sequenceMapping != null)
950           {
951             sequenceMapping = null;
952             // compactAnnotationArray();
953           }
954           createSequenceMapping(sequenceI, 1, true);
955           adjustForAlignment();
956         }
957         else
958         {
959           // Mapping carried over
960           sequenceRef = sequenceI;
961         }
962       }
963       else
964       {
965         // No mapping exists
966         createSequenceMapping(sequenceI, 1, true);
967         adjustForAlignment();
968       }
969     }
970     else
971     {
972       // throw away the mapping without compacting.
973       sequenceMapping = null;
974       sequenceRef = null;
975     }
976   }
977
978   /**
979    * @return the score
980    */
981   public double getScore()
982   {
983     return score;
984   }
985
986   /**
987    * @param score
988    *          the score to set
989    */
990   public void setScore(double score)
991   {
992     hasScore = true;
993     this.score = score;
994   }
995
996   /**
997    * 
998    * @return true if annotation has an associated score
999    */
1000   public boolean hasScore()
1001   {
1002     return hasScore || !Double.isNaN(score);
1003   }
1004
1005   /**
1006    * Score only annotation
1007    * 
1008    * @param label
1009    * @param description
1010    * @param score
1011    */
1012   public AlignmentAnnotation(String label, String description, double score)
1013   {
1014     this(label, description, null);
1015     setScore(score);
1016   }
1017
1018   /**
1019    * copy constructor with edit based on the hidden columns marked in colSel
1020    * 
1021    * @param alignmentAnnotation
1022    * @param colSel
1023    */
1024   public AlignmentAnnotation(AlignmentAnnotation alignmentAnnotation,
1025           ColumnSelection colSel)
1026   {
1027     this(alignmentAnnotation);
1028     if (annotations == null)
1029     {
1030       return;
1031     }
1032     colSel.makeVisibleAnnotation(this);
1033   }
1034
1035   public void setPadGaps(boolean padgaps, char gapchar)
1036   {
1037     this.padGaps = padgaps;
1038     if (padgaps)
1039     {
1040       hasText = true;
1041       for (int i = 0; i < annotations.length; i++)
1042       {
1043         if (annotations[i] == null)
1044           annotations[i] = new Annotation(String.valueOf(gapchar), null,
1045                   ' ', 0f,null);
1046         else if (annotations[i].displayCharacter == null
1047                 || annotations[i].displayCharacter.equals(" "))
1048           annotations[i].displayCharacter = String.valueOf(gapchar);
1049       }
1050     }
1051   }
1052
1053   /**
1054    * format description string for display
1055    * 
1056    * @param seqname
1057    * @return Get the annotation description string optionally prefixed by
1058    *         associated sequence name (if any)
1059    */
1060   public String getDescription(boolean seqname)
1061   {
1062     if (seqname && this.sequenceRef != null)
1063     {
1064       int i = description.toLowerCase().indexOf("<html>");
1065       if (i > -1)
1066       {
1067         // move the html tag to before the sequence reference.
1068         return "<html>" + sequenceRef.getName() + " : "
1069                 + description.substring(i + 6);
1070       }
1071       return sequenceRef.getName() + " : " + description;
1072     }
1073     return description;
1074   }
1075
1076   public boolean isValidStruc()
1077   {
1078     return invalidrnastruc == -1;
1079   }
1080
1081   public long getInvalidStrucPos()
1082   {
1083     return invalidrnastruc;
1084   }
1085
1086   /**
1087    * machine readable ID string indicating what generated this annotation
1088    */
1089   protected String calcId = "";
1090
1091   /**
1092    * base colour for line graphs. If null, will be set automatically by
1093    * searching the alignment annotation
1094    */
1095   public java.awt.Color _linecolour;
1096
1097   public String getCalcId()
1098   {
1099     return calcId;
1100   }
1101
1102   public void setCalcId(String calcId)
1103   {
1104     this.calcId = calcId;
1105   }
1106 }