Jalview 2.6 source licence
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, 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 java.awt.*;
23
24 import jalview.analysis.*;
25 import jalview.schemes.*;
26
27 /**
28  * Collects a set contiguous ranges on a set of sequences
29  * 
30  * @author $author$
31  * @version $Revision$
32  */
33 public class SequenceGroup
34 {
35   String groupName;
36
37   String description;
38
39   Conservation conserve;
40
41   Vector aaFrequency;
42
43   boolean displayBoxes = true;
44
45   boolean displayText = true;
46
47   boolean colourText = false;
48
49   /**
50    * after Olivier's non-conserved only character display
51    */
52   boolean showNonconserved = false;
53
54   /**
55    * group members
56    */
57   private Vector sequences = new Vector();
58
59   /**
60    * representative sequence for this group (if any)
61    */
62   private SequenceI seqrep = null;
63
64   int width = -1;
65
66   /**
67    * Colourscheme applied to group if any
68    */
69   public ColourSchemeI cs;
70
71   int startRes = 0;
72
73   int endRes = 0;
74
75   public Color outlineColour = Color.black;
76
77   public Color idColour = null;
78
79   public int thresholdTextColour = 0;
80
81   public Color textColour = Color.black;
82
83   public Color textColour2 = Color.white;
84
85   /**
86    * consensus calculation property
87    */
88   private boolean ignoreGapsInConsensus = true;
89
90   /**
91    * consensus calculation property
92    */
93   private boolean showSequenceLogo = false;
94
95   /**
96    * @return the includeAllConsSymbols
97    */
98   public boolean isShowSequenceLogo()
99   {
100     return showSequenceLogo;
101   }
102
103   /**
104    * Creates a new SequenceGroup object.
105    */
106   public SequenceGroup()
107   {
108     groupName = "JGroup:" + this.hashCode();
109   }
110
111   /**
112    * Creates a new SequenceGroup object.
113    * 
114    * @param sequences
115    * @param groupName
116    * @param scheme
117    * @param displayBoxes
118    * @param displayText
119    * @param colourText
120    * @param start
121    *          first column of group
122    * @param end
123    *          last column of group
124    */
125   public SequenceGroup(Vector sequences, String groupName,
126           ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
127           boolean colourText, int start, int end)
128   {
129     this.sequences = sequences;
130     this.groupName = groupName;
131     this.displayBoxes = displayBoxes;
132     this.displayText = displayText;
133     this.colourText = colourText;
134     this.cs = scheme;
135     startRes = start;
136     endRes = end;
137     recalcConservation();
138   }
139
140   /**
141    * copy constructor
142    * 
143    * @param seqsel
144    */
145   public SequenceGroup(SequenceGroup seqsel)
146   {
147     if (seqsel != null)
148     {
149       sequences = new Vector();
150       Enumeration sq = seqsel.sequences.elements();
151       while (sq.hasMoreElements())
152       {
153         sequences.addElement(sq.nextElement());
154       }
155       ;
156       if (seqsel.groupName != null)
157       {
158         groupName = new String(seqsel.groupName);
159       }
160       displayBoxes = seqsel.displayBoxes;
161       displayText = seqsel.displayText;
162       colourText = seqsel.colourText;
163       startRes = seqsel.startRes;
164       endRes = seqsel.endRes;
165       cs = seqsel.cs;
166       if (seqsel.description != null)
167         description = new String(seqsel.description);
168       hidecols = seqsel.hidecols;
169       hidereps = seqsel.hidereps;
170       idColour = seqsel.idColour;
171       outlineColour = seqsel.outlineColour;
172       seqrep = seqsel.seqrep;
173       textColour = seqsel.textColour;
174       textColour2 = seqsel.textColour2;
175       thresholdTextColour = seqsel.thresholdTextColour;
176       width = seqsel.width;
177       ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
178       if (seqsel.conserve != null)
179       {
180         recalcConservation(); // safer than
181         // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
182       }
183     }
184   }
185
186   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
187   {
188     int iSize = sequences.size();
189     SequenceI[] seqs = new SequenceI[iSize];
190     SequenceI[] inorder = getSequencesInOrder(align);
191
192     for (int i = 0, ipos = 0; i < inorder.length; i++)
193     {
194       SequenceI seq = inorder[i];
195
196       seqs[ipos] = seq.getSubSequence(startRes, endRes + 1);
197       if (seqs[ipos] != null)
198       {
199         seqs[ipos].setDescription(seq.getDescription());
200         seqs[ipos].setDBRef(seq.getDBRef());
201         seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
202         if (seq.getDatasetSequence() != null)
203         {
204           seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
205         }
206
207         if (seq.getAnnotation() != null)
208         {
209           AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
210           // Only copy annotation that is either a score or referenced by the
211           // alignment's annotation vector
212           for (int a = 0; a < seq.getAnnotation().length; a++)
213           {
214             AlignmentAnnotation tocopy = seq.getAnnotation()[a];
215             if (alann != null)
216             {
217               boolean found = false;
218               for (int pos = 0; pos < alann.length; pos++)
219               {
220                 if (alann[pos] == tocopy)
221                 {
222                   found = true;
223                   break;
224                 }
225               }
226               if (!found)
227                 continue;
228             }
229             AlignmentAnnotation newannot = new AlignmentAnnotation(seq
230                     .getAnnotation()[a]);
231             newannot.restrict(startRes, endRes);
232             newannot.setSequenceRef(seqs[ipos]);
233             newannot.adjustForAlignment();
234             seqs[ipos].addAlignmentAnnotation(newannot);
235           }
236         }
237         ipos++;
238       }
239       else
240       {
241         iSize--;
242       }
243     }
244     if (iSize != inorder.length)
245     {
246       SequenceI[] nseqs = new SequenceI[iSize];
247       System.arraycopy(seqs, 0, nseqs, 0, iSize);
248       seqs = nseqs;
249     }
250     return seqs;
251
252   }
253
254   /**
255    * If sequence ends in gaps, the end residue can be correctly calculated here
256    * 
257    * @param seq
258    *          SequenceI
259    * @return int
260    */
261   public int findEndRes(SequenceI seq)
262   {
263     int eres = 0;
264     char ch;
265
266     for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
267     {
268       ch = seq.getCharAt(j);
269       if (!jalview.util.Comparison.isGap((ch)))
270       {
271         eres++;
272       }
273     }
274
275     if (eres > 0)
276     {
277       eres += seq.getStart() - 1;
278     }
279
280     return eres;
281   }
282
283   public Vector getSequences(Hashtable hiddenReps)
284   {
285     if (hiddenReps == null)
286     {
287       return sequences;
288     }
289     else
290     {
291       Vector allSequences = new Vector();
292       SequenceI seq, seq2;
293       for (int i = 0; i < sequences.size(); i++)
294       {
295         seq = (SequenceI) sequences.elementAt(i);
296         allSequences.addElement(seq);
297         if (hiddenReps.containsKey(seq))
298         {
299           SequenceGroup hsg = (SequenceGroup) hiddenReps.get(seq);
300           for (int h = 0; h < hsg.getSize(); h++)
301           {
302             seq2 = hsg.getSequenceAt(h);
303             if (seq2 != seq && !allSequences.contains(seq2))
304             {
305               allSequences.addElement(seq2);
306             }
307           }
308         }
309       }
310
311       return allSequences;
312     }
313   }
314
315   public SequenceI[] getSequencesAsArray(Hashtable hiddenReps)
316   {
317     Vector tmp = getSequences(hiddenReps);
318     if (tmp == null)
319     {
320       return null;
321     }
322     SequenceI[] result = new SequenceI[tmp.size()];
323     for (int i = 0; i < result.length; i++)
324     {
325       result[i] = (SequenceI) tmp.elementAt(i);
326     }
327
328     return result;
329   }
330
331   /**
332    * DOCUMENT ME!
333    * 
334    * @param col
335    *          DOCUMENT ME!
336    * 
337    * @return DOCUMENT ME!
338    */
339   public boolean adjustForRemoveLeft(int col)
340   {
341     // return value is true if the group still exists
342     if (startRes >= col)
343     {
344       startRes = startRes - col;
345     }
346
347     if (endRes >= col)
348     {
349       endRes = endRes - col;
350
351       if (startRes > endRes)
352       {
353         startRes = 0;
354       }
355     }
356     else
357     {
358       // must delete this group!!
359       return false;
360     }
361
362     return true;
363   }
364
365   /**
366    * DOCUMENT ME!
367    * 
368    * @param col
369    *          DOCUMENT ME!
370    * 
371    * @return DOCUMENT ME!
372    */
373   public boolean adjustForRemoveRight(int col)
374   {
375     if (startRes > col)
376     {
377       // delete this group
378       return false;
379     }
380
381     if (endRes >= col)
382     {
383       endRes = col;
384     }
385
386     return true;
387   }
388
389   /**
390    * DOCUMENT ME!
391    * 
392    * @return DOCUMENT ME!
393    */
394   public String getName()
395   {
396     return groupName;
397   }
398
399   public String getDescription()
400   {
401     return description;
402   }
403
404   /**
405    * DOCUMENT ME!
406    * 
407    * @param name
408    *          DOCUMENT ME!
409    */
410   public void setName(String name)
411   {
412     groupName = name;
413     // TODO: URGENT: update dependent objects (annotation row)
414   }
415
416   public void setDescription(String desc)
417   {
418     description = desc;
419   }
420
421   /**
422    * DOCUMENT ME!
423    * 
424    * @return DOCUMENT ME!
425    */
426   public Conservation getConservation()
427   {
428     return conserve;
429   }
430
431   /**
432    * DOCUMENT ME!
433    * 
434    * @param c
435    *          DOCUMENT ME!
436    */
437   public void setConservation(Conservation c)
438   {
439     conserve = c;
440   }
441
442   /**
443    * Add s to this sequence group
444    * 
445    * @param s
446    *          alignment sequence to be added
447    * @param recalc
448    *          true means Group's conservation should be recalculated
449    */
450   public void addSequence(SequenceI s, boolean recalc)
451   {
452     if (s != null && !sequences.contains(s))
453     {
454       sequences.addElement(s);
455     }
456
457     if (recalc)
458     {
459       recalcConservation();
460     }
461   }
462
463   /**
464    * Max Gaps Threshold for performing a conservation calculation TODO: make
465    * this a configurable property - or global to an alignment view
466    */
467   private int consPercGaps = 25;
468
469   /**
470    * calculate residue conservation for group - but only if necessary.
471    */
472   public void recalcConservation()
473   {
474     if (cs == null && consensus == null && conservation == null)
475     {
476       return;
477     }
478
479     try
480     {
481       Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes,
482               endRes + 1, showSequenceLogo);
483       if (consensus != null)
484       {
485         _updateConsensusRow(cnsns);
486       }
487       if (cs != null)
488       {
489         cs.setConsensus(cnsns);
490
491         if (cs instanceof ClustalxColourScheme)
492         {
493           ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
494         }
495       }
496
497       if ((conservation != null)
498               || (cs != null && cs.conservationApplied()))
499       {
500         Conservation c = new Conservation(groupName,
501                 ResidueProperties.propHash, 3, sequences, startRes,
502                 endRes + 1);
503         c.calculate();
504         c.verdict(false, consPercGaps);
505         if (conservation != null)
506         {
507           _updateConservationRow(c);
508         }
509         if (cs != null)
510         {
511           cs.setConservation(c);
512
513           if (cs instanceof ClustalxColourScheme)
514           {
515             ((ClustalxColourScheme) cs)
516                     .resetClustalX(sequences, getWidth());
517           }
518         }
519       }
520     } catch (java.lang.OutOfMemoryError err)
521     {
522       // TODO: catch OOM
523       System.out.println("Out of memory loading groups: " + err);
524     }
525
526   }
527
528   private void _updateConservationRow(Conservation c)
529   {
530     if (conservation == null)
531     {
532       getConservation();
533     }
534     // update Labels
535     conservation.label = "Conservation for " + getName();
536     conservation.description = "Conservation for group " + getName()
537             + " less than " + consPercGaps + "% gaps";
538     // preserve width if already set
539     int aWidth = (conservation.annotations != null) ? (endRes < conservation.annotations.length ? conservation.annotations.length
540             : endRes + 1)
541             : endRes + 1;
542     conservation.annotations = null;
543     conservation.annotations = new Annotation[aWidth]; // should be alignment
544                                                        // width
545     c.completeAnnotations(conservation, null, startRes, endRes + 1);
546   }
547
548   public Hashtable[] consensusData = null;
549
550   private void _updateConsensusRow(Hashtable[] cnsns)
551   {
552     if (consensus == null)
553     {
554       getConsensus();
555     }
556     consensus.label = "Consensus for " + getName();
557     consensus.description = "Percent Identity";
558     consensusData = cnsns;
559     // preserve width if already set
560     int aWidth = (consensus.annotations != null) ? (endRes < consensus.annotations.length ? consensus.annotations.length
561             : endRes + 1)
562             : endRes + 1;
563     consensus.annotations = null;
564     consensus.annotations = new Annotation[aWidth]; // should be alignment width
565
566     AAFrequency.completeConsensus(consensus, cnsns, startRes, endRes + 1,
567             ignoreGapsInConsensus, showSequenceLogo); // TODO: setting container
568                                                       // for
569                                                       // ignoreGapsInConsensusCalculation);
570   }
571
572   /**
573    * @param s
574    *          sequence to either add or remove from group
575    * @param recalc
576    *          flag passed to delete/addSequence to indicate if group properties
577    *          should be recalculated
578    */
579   public void addOrRemove(SequenceI s, boolean recalc)
580   {
581     if (sequences.contains(s))
582     {
583       deleteSequence(s, recalc);
584     }
585     else
586     {
587       addSequence(s, recalc);
588     }
589   }
590
591   /**
592    * DOCUMENT ME!
593    * 
594    * @param s
595    *          DOCUMENT ME!
596    * @param recalc
597    *          DOCUMENT ME!
598    */
599   public void deleteSequence(SequenceI s, boolean recalc)
600   {
601     sequences.removeElement(s);
602
603     if (recalc)
604     {
605       recalcConservation();
606     }
607   }
608
609   /**
610    * DOCUMENT ME!
611    * 
612    * @return DOCUMENT ME!
613    */
614   public int getStartRes()
615   {
616     return startRes;
617   }
618
619   /**
620    * DOCUMENT ME!
621    * 
622    * @return DOCUMENT ME!
623    */
624   public int getEndRes()
625   {
626     return endRes;
627   }
628
629   /**
630    * DOCUMENT ME!
631    * 
632    * @param i
633    *          DOCUMENT ME!
634    */
635   public void setStartRes(int i)
636   {
637     startRes = i;
638   }
639
640   /**
641    * DOCUMENT ME!
642    * 
643    * @param i
644    *          DOCUMENT ME!
645    */
646   public void setEndRes(int i)
647   {
648     endRes = i;
649   }
650
651   /**
652    * DOCUMENT ME!
653    * 
654    * @return DOCUMENT ME!
655    */
656   public int getSize()
657   {
658     return sequences.size();
659   }
660
661   /**
662    * DOCUMENT ME!
663    * 
664    * @param i
665    *          DOCUMENT ME!
666    * 
667    * @return DOCUMENT ME!
668    */
669   public SequenceI getSequenceAt(int i)
670   {
671     return (SequenceI) sequences.elementAt(i);
672   }
673
674   /**
675    * DOCUMENT ME!
676    * 
677    * @param state
678    *          DOCUMENT ME!
679    */
680   public void setColourText(boolean state)
681   {
682     colourText = state;
683   }
684
685   /**
686    * DOCUMENT ME!
687    * 
688    * @return DOCUMENT ME!
689    */
690   public boolean getColourText()
691   {
692     return colourText;
693   }
694
695   /**
696    * DOCUMENT ME!
697    * 
698    * @param state
699    *          DOCUMENT ME!
700    */
701   public void setDisplayText(boolean state)
702   {
703     displayText = state;
704   }
705
706   /**
707    * DOCUMENT ME!
708    * 
709    * @return DOCUMENT ME!
710    */
711   public boolean getDisplayText()
712   {
713     return displayText;
714   }
715
716   /**
717    * DOCUMENT ME!
718    * 
719    * @param state
720    *          DOCUMENT ME!
721    */
722   public void setDisplayBoxes(boolean state)
723   {
724     displayBoxes = state;
725   }
726
727   /**
728    * DOCUMENT ME!
729    * 
730    * @return DOCUMENT ME!
731    */
732   public boolean getDisplayBoxes()
733   {
734     return displayBoxes;
735   }
736
737   /**
738    * DOCUMENT ME!
739    * 
740    * @return DOCUMENT ME!
741    */
742   public int getWidth()
743   {
744     // MC This needs to get reset when characters are inserted and deleted
745     if (sequences.size() > 0)
746     {
747       width = ((SequenceI) sequences.elementAt(0)).getLength();
748     }
749
750     for (int i = 1; i < sequences.size(); i++)
751     {
752       SequenceI seq = (SequenceI) sequences.elementAt(i);
753
754       if (seq.getLength() > width)
755       {
756         width = seq.getLength();
757       }
758     }
759
760     return width;
761   }
762
763   /**
764    * DOCUMENT ME!
765    * 
766    * @param c
767    *          DOCUMENT ME!
768    */
769   public void setOutlineColour(Color c)
770   {
771     outlineColour = c;
772   }
773
774   /**
775    * DOCUMENT ME!
776    * 
777    * @return DOCUMENT ME!
778    */
779   public Color getOutlineColour()
780   {
781     return outlineColour;
782   }
783
784   /**
785    * 
786    * returns the sequences in the group ordered by the ordering given by al.
787    * this used to return an array with null entries regardless, new behaviour is
788    * below. TODO: verify that this does not affect use in applet or application
789    * 
790    * @param al
791    *          Alignment
792    * @return SequenceI[] intersection of sequences in group with al, ordered by
793    *         al, or null if group does not intersect with al
794    */
795   public SequenceI[] getSequencesInOrder(AlignmentI al)
796   {
797     int sSize = sequences.size();
798     int alHeight = al.getHeight();
799
800     SequenceI[] seqs = new SequenceI[sSize];
801
802     int index = 0;
803     for (int i = 0; i < alHeight && index < sSize; i++)
804     {
805       if (sequences.contains(al.getSequenceAt(i)))
806       {
807         seqs[index++] = al.getSequenceAt(i);
808       }
809     }
810     if (index == 0)
811     {
812       return null;
813     }
814     if (index < seqs.length)
815     {
816       SequenceI[] dummy = seqs;
817       seqs = new SequenceI[index];
818       while (--index >= 0)
819       {
820         seqs[index] = dummy[index];
821         dummy[index] = null;
822       }
823     }
824     return seqs;
825   }
826
827   /**
828    * @return the idColour
829    */
830   public Color getIdColour()
831   {
832     return idColour;
833   }
834
835   /**
836    * @param idColour
837    *          the idColour to set
838    */
839   public void setIdColour(Color idColour)
840   {
841     this.idColour = idColour;
842   }
843
844   /**
845    * @return the representative sequence for this group
846    */
847   public SequenceI getSeqrep()
848   {
849     return seqrep;
850   }
851
852   /**
853    * set the representative sequence for this group. Note - this affects the
854    * interpretation of the Hidereps attribute.
855    * 
856    * @param seqrep
857    *          the seqrep to set (null means no sequence representative)
858    */
859   public void setSeqrep(SequenceI seqrep)
860   {
861     this.seqrep = seqrep;
862   }
863
864   /**
865    * 
866    * @return true if group has a sequence representative
867    */
868   public boolean hasSeqrep()
869   {
870     return seqrep != null;
871   }
872
873   /**
874    * visibility of rows or represented rows covered by group
875    */
876   private boolean hidereps = false;
877
878   /**
879    * set visibility of sequences covered by (if no sequence representative is
880    * defined) or represented by this group.
881    * 
882    * @param visibility
883    */
884   public void setHidereps(boolean visibility)
885   {
886     hidereps = visibility;
887   }
888
889   /**
890    * 
891    * @return true if sequences represented (or covered) by this group should be
892    *         hidden
893    */
894   public boolean isHidereps()
895   {
896     return hidereps;
897   }
898
899   /**
900    * visibility of columns intersecting this group
901    */
902   private boolean hidecols = false;
903
904   /**
905    * set intended visibility of columns covered by this group
906    * 
907    * @param visibility
908    */
909   public void setHideCols(boolean visibility)
910   {
911     hidecols = visibility;
912   }
913
914   /**
915    * 
916    * @return true if columns covered by group should be hidden
917    */
918   public boolean isHideCols()
919   {
920     return hidecols;
921   }
922
923   /**
924    * create a new sequence group from the intersection of this group with an
925    * alignment Hashtable of hidden representatives
926    * 
927    * @param alignment
928    *          (may not be null)
929    * @param hashtable
930    *          (may be null)
931    * @return new group containing sequences common to this group and alignment
932    */
933   public SequenceGroup intersect(AlignmentI alignment, Hashtable hashtable)
934   {
935     SequenceGroup sgroup = new SequenceGroup(this);
936     SequenceI[] insect = getSequencesInOrder(alignment);
937     sgroup.sequences = new Vector();
938     for (int s = 0; insect != null && s < insect.length; s++)
939     {
940       if (hashtable == null || hashtable.containsKey(insect[s]))
941       {
942         sgroup.sequences.addElement(insect[s]);
943       }
944     }
945     // Enumeration en =getSequences(hashtable).elements();
946     // while (en.hasMoreElements())
947     // {
948     // SequenceI elem = (SequenceI) en.nextElement();
949     // if (alignment.getSequences().contains(elem))
950     // {
951     // sgroup.addSequence(elem, false);
952     // }
953     // }
954     return sgroup;
955   }
956
957   /**
958    * @return the showUnconserved
959    */
960   public boolean getShowNonconserved()
961   {
962     return showNonconserved;
963   }
964
965   /**
966    * @param showNonconserved
967    *          the showUnconserved to set
968    */
969   public void setShowNonconserved(boolean displayNonconserved)
970   {
971     this.showNonconserved = displayNonconserved;
972   }
973
974   AlignmentAnnotation consensus = null, conservation = null;
975
976   /**
977    * flag indicating if consensus histogram should be rendered
978    */
979   private boolean showConsensusHistogram;
980
981   /**
982    * 
983    * @return automatically calculated consensus row
984    */
985   public AlignmentAnnotation getConsensus()
986   {
987     // TODO get or calculate and get consensus annotation row for this group
988     int aWidth = this.getWidth();
989     // pointer
990     // possibility
991     // here.
992     if (aWidth < 0)
993     {
994       return null;
995     }
996     if (consensus == null)
997     {
998       consensus = new AlignmentAnnotation("", "", new Annotation[1], 0f,
999               100f, AlignmentAnnotation.BAR_GRAPH);
1000     }
1001     consensus.hasText = true;
1002     consensus.autoCalculated = true;
1003     consensus.groupRef = this;
1004     consensus.label = "Consensus for " + getName();
1005     consensus.description = "Percent Identity";
1006     return consensus;
1007   }
1008
1009   /**
1010    * get the conservation annotation row for this group
1011    * 
1012    * @return autoCalculated annotation row
1013    */
1014   public AlignmentAnnotation getConservationRow()
1015   {
1016     if (conservation == null)
1017     {
1018       conservation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
1019               11f, AlignmentAnnotation.BAR_GRAPH);
1020     }
1021
1022     conservation.hasText = true;
1023     conservation.autoCalculated = true;
1024     conservation.groupRef = this;
1025     conservation.label = "Conservation for " + getName();
1026     conservation.description = "Conservation for group " + getName()
1027             + " less than " + consPercGaps + "% gaps";
1028     return conservation;
1029   }
1030
1031   /**
1032    * 
1033    * @return true if annotation rows have been instantiated for this group
1034    */
1035   public boolean hasAnnotationRows()
1036   {
1037     return consensus != null || conservation != null;
1038   }
1039
1040   public SequenceI getConsensusSeq()
1041   {
1042     getConsensus();
1043     StringBuffer seqs = new StringBuffer();
1044     for (int i = 0; i < consensus.annotations.length; i++)
1045     {
1046       if (consensus.annotations[i] != null)
1047       {
1048         if (consensus.annotations[i].description.charAt(0) == '[')
1049         {
1050           seqs.append(consensus.annotations[i].description.charAt(1));
1051         }
1052         else
1053         {
1054           seqs.append(consensus.annotations[i].displayCharacter);
1055         }
1056       }
1057     }
1058
1059     SequenceI sq = new Sequence("Group" + getName() + " Consensus", seqs
1060             .toString());
1061     sq.setDescription("Percentage Identity Consensus "
1062             + ((ignoreGapsInConsensus) ? " without gaps" : ""));
1063     return sq;
1064   }
1065
1066   public void setIgnoreGapsConsensus(boolean state)
1067   {
1068     if (this.ignoreGapsInConsensus != state && consensus != null)
1069     {
1070       ignoreGapsInConsensus = state;
1071       recalcConservation();
1072     }
1073     ignoreGapsInConsensus = state;
1074   }
1075
1076   public boolean getIgnoreGapsConsensus()
1077   {
1078     return ignoreGapsInConsensus;
1079   }
1080
1081   /**
1082    * @param showSequenceLogo
1083    *          indicates if a sequence logo is shown for consensus annotation
1084    */
1085   public void setshowSequenceLogo(boolean showSequenceLogo)
1086   {
1087     // TODO: decouple calculation from settings update
1088     if (this.showSequenceLogo != showSequenceLogo && consensus != null)
1089     {
1090       this.showSequenceLogo = showSequenceLogo;
1091       recalcConservation();
1092     }
1093     this.showSequenceLogo = showSequenceLogo;
1094   }
1095
1096   /**
1097    * 
1098    * @param showConsHist
1099    *          flag indicating if the consensus histogram for this group should
1100    *          be rendered
1101    */
1102   public void setShowConsensusHistogram(boolean showConsHist)
1103   {
1104
1105     if (showConsensusHistogram != showConsHist && consensus != null)
1106     {
1107       this.showConsensusHistogram = showConsHist;
1108       recalcConservation();
1109     }
1110     this.showConsensusHistogram = showConsHist;
1111   }
1112
1113   /**
1114    * @return the showConsensusHistogram
1115    */
1116   public boolean isShowConsensusHistogram()
1117   {
1118     return showConsensusHistogram;
1119   }
1120 }