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