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