b0eabff1ac366b68fd01182495de0110d96c7607
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 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 implements AnnotatedCollectionI
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(
230                     seq.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. If aligment sequence is already contained in
444    * group, it will not be added again, but recalculation may happen if the flag
445    * is set.
446    * 
447    * @param s
448    *          alignment sequence to be added
449    * @param recalc
450    *          true means Group's conservation should be recalculated
451    */
452   public void addSequence(SequenceI s, boolean recalc)
453   {
454     if (s != null && !sequences.contains(s))
455     {
456       sequences.addElement(s);
457     }
458
459     if (recalc)
460     {
461       recalcConservation();
462     }
463   }
464
465   /**
466    * Max Gaps Threshold for performing a conservation calculation TODO: make
467    * this a configurable property - or global to an alignment view
468    */
469   private int consPercGaps = 25;
470
471   /**
472    * calculate residue conservation for group - but only if necessary.
473    */
474   public void recalcConservation()
475   {
476     if (cs == null && consensus == null && conservation == null)
477     {
478       return;
479     }
480     if (cs!=null)
481     {
482       cs.alignmentChanged(this);
483     }
484     try
485     {
486       Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes,
487               endRes + 1, showSequenceLogo);
488       if (consensus != null)
489       {
490         _updateConsensusRow(cnsns);
491       }
492       if (cs != null)
493       {
494         cs.setConsensus(cnsns);
495
496         if (cs instanceof ClustalxColourScheme)
497         {
498           ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
499         }
500       }
501
502       if ((conservation != null)
503               || (cs != null && cs.conservationApplied()))
504       {
505         Conservation c = new Conservation(groupName,
506                 ResidueProperties.propHash, 3, sequences, startRes,
507                 endRes + 1);
508         c.calculate();
509         c.verdict(false, consPercGaps);
510         if (conservation != null)
511         {
512           _updateConservationRow(c);
513         }
514         if (cs != null)
515         {
516           if (cs.conservationApplied())
517           {
518             cs.setConservation(c);
519
520             if (cs instanceof ClustalxColourScheme)
521             {
522               ((ClustalxColourScheme) cs).resetClustalX(sequences,
523                       getWidth());
524             }
525           }
526         }
527       }
528     } catch (java.lang.OutOfMemoryError err)
529     {
530       // TODO: catch OOM
531       System.out.println("Out of memory loading groups: " + err);
532     }
533
534   }
535
536   private void _updateConservationRow(Conservation c)
537   {
538     if (conservation == null)
539     {
540       getConservation();
541     }
542     // update Labels
543     conservation.label = "Conservation for " + getName();
544     conservation.description = "Conservation for group " + getName()
545             + " less than " + consPercGaps + "% gaps";
546     // preserve width if already set
547     int aWidth = (conservation.annotations != null) ? (endRes < conservation.annotations.length ? conservation.annotations.length
548             : endRes + 1)
549             : endRes + 1;
550     conservation.annotations = null;
551     conservation.annotations = new Annotation[aWidth]; // should be alignment
552                                                        // width
553     c.completeAnnotations(conservation, null, startRes, endRes + 1);
554   }
555
556   public Hashtable[] consensusData = null;
557
558   private void _updateConsensusRow(Hashtable[] cnsns)
559   {
560     if (consensus == null)
561     {
562       getConsensus();
563     }
564     consensus.label = "Consensus for " + getName();
565     consensus.description = "Percent Identity";
566     consensusData = cnsns;
567     // preserve width if already set
568     int aWidth = (consensus.annotations != null) ? (endRes < consensus.annotations.length ? consensus.annotations.length
569             : endRes + 1)
570             : endRes + 1;
571     consensus.annotations = null;
572     consensus.annotations = new Annotation[aWidth]; // should be alignment width
573
574     AAFrequency.completeConsensus(consensus, cnsns, startRes, endRes + 1,
575             ignoreGapsInConsensus, showSequenceLogo); // TODO: setting container
576                                                       // for
577                                                       // ignoreGapsInConsensusCalculation);
578   }
579
580   /**
581    * @param s
582    *          sequence to either add or remove from group
583    * @param recalc
584    *          flag passed to delete/addSequence to indicate if group properties
585    *          should be recalculated
586    */
587   public void addOrRemove(SequenceI s, boolean recalc)
588   {
589     if (sequences.contains(s))
590     {
591       deleteSequence(s, recalc);
592     }
593     else
594     {
595       addSequence(s, recalc);
596     }
597   }
598
599   /**
600    * DOCUMENT ME!
601    * 
602    * @param s
603    *          DOCUMENT ME!
604    * @param recalc
605    *          DOCUMENT ME!
606    */
607   public void deleteSequence(SequenceI s, boolean recalc)
608   {
609     sequences.removeElement(s);
610
611     if (recalc)
612     {
613       recalcConservation();
614     }
615   }
616
617   /**
618    * DOCUMENT ME!
619    * 
620    * @return DOCUMENT ME!
621    */
622   public int getStartRes()
623   {
624     return startRes;
625   }
626
627   /**
628    * DOCUMENT ME!
629    * 
630    * @return DOCUMENT ME!
631    */
632   public int getEndRes()
633   {
634     return endRes;
635   }
636
637   /**
638    * Set the first column selected by this group. Runs from 0<=i<N_cols
639    * 
640    * @param i
641    */
642   public void setStartRes(int i)
643   {
644     startRes = i;
645   }
646
647   /**
648    * Set the groups last selected column. Runs from 0<=i<N_cols
649    * 
650    * @param i
651    */
652   public void setEndRes(int i)
653   {
654     endRes = i;
655   }
656
657   /**
658    * DOCUMENT ME!
659    * 
660    * @return DOCUMENT ME!
661    */
662   public int getSize()
663   {
664     return sequences.size();
665   }
666
667   /**
668    * DOCUMENT ME!
669    * 
670    * @param i
671    *          DOCUMENT ME!
672    * 
673    * @return DOCUMENT ME!
674    */
675   public SequenceI getSequenceAt(int i)
676   {
677     return (SequenceI) sequences.elementAt(i);
678   }
679
680   /**
681    * DOCUMENT ME!
682    * 
683    * @param state
684    *          DOCUMENT ME!
685    */
686   public void setColourText(boolean state)
687   {
688     colourText = state;
689   }
690
691   /**
692    * DOCUMENT ME!
693    * 
694    * @return DOCUMENT ME!
695    */
696   public boolean getColourText()
697   {
698     return colourText;
699   }
700
701   /**
702    * DOCUMENT ME!
703    * 
704    * @param state
705    *          DOCUMENT ME!
706    */
707   public void setDisplayText(boolean state)
708   {
709     displayText = state;
710   }
711
712   /**
713    * DOCUMENT ME!
714    * 
715    * @return DOCUMENT ME!
716    */
717   public boolean getDisplayText()
718   {
719     return displayText;
720   }
721
722   /**
723    * DOCUMENT ME!
724    * 
725    * @param state
726    *          DOCUMENT ME!
727    */
728   public void setDisplayBoxes(boolean state)
729   {
730     displayBoxes = state;
731   }
732
733   /**
734    * DOCUMENT ME!
735    * 
736    * @return DOCUMENT ME!
737    */
738   public boolean getDisplayBoxes()
739   {
740     return displayBoxes;
741   }
742
743   /**
744    * DOCUMENT ME!
745    * 
746    * @return DOCUMENT ME!
747    */
748   public int getWidth()
749   {
750     // MC This needs to get reset when characters are inserted and deleted
751     if (sequences.size() > 0)
752     {
753       width = ((SequenceI) sequences.elementAt(0)).getLength();
754     }
755
756     for (int i = 1; i < sequences.size(); i++)
757     {
758       SequenceI seq = (SequenceI) sequences.elementAt(i);
759
760       if (seq.getLength() > width)
761       {
762         width = seq.getLength();
763       }
764     }
765
766     return width;
767   }
768
769   /**
770    * DOCUMENT ME!
771    * 
772    * @param c
773    *          DOCUMENT ME!
774    */
775   public void setOutlineColour(Color c)
776   {
777     outlineColour = c;
778   }
779
780   /**
781    * DOCUMENT ME!
782    * 
783    * @return DOCUMENT ME!
784    */
785   public Color getOutlineColour()
786   {
787     return outlineColour;
788   }
789
790   /**
791    * 
792    * returns the sequences in the group ordered by the ordering given by al.
793    * this used to return an array with null entries regardless, new behaviour is
794    * below. TODO: verify that this does not affect use in applet or application
795    * 
796    * @param al
797    *          Alignment
798    * @return SequenceI[] intersection of sequences in group with al, ordered by
799    *         al, or null if group does not intersect with al
800    */
801   public SequenceI[] getSequencesInOrder(AlignmentI al)
802   {
803     return getSequencesInOrder(al, true);
804   }
805
806   /**
807    * return an array representing the intersection of the group with al,
808    * optionally returning an array the size of al.getHeight() where nulls mark
809    * the non-intersected sequences
810    * 
811    * @param al
812    * @param trim
813    * @return null or array
814    */
815   public SequenceI[] getSequencesInOrder(AlignmentI al, boolean trim)
816   {
817     int sSize = sequences.size();
818     int alHeight = al.getHeight();
819
820     SequenceI[] seqs = new SequenceI[(trim) ? sSize : alHeight];
821
822     int index = 0;
823     for (int i = 0; i < alHeight && index < sSize; i++)
824     {
825       if (sequences.contains(al.getSequenceAt(i)))
826       {
827         seqs[(trim) ? index : i] = al.getSequenceAt(i);
828         index++;
829       }
830     }
831     if (index == 0)
832     {
833       return null;
834     }
835     if (!trim)
836     {
837       return seqs;
838     }
839     if (index < seqs.length)
840     {
841       SequenceI[] dummy = seqs;
842       seqs = new SequenceI[index];
843       while (--index >= 0)
844       {
845         seqs[index] = dummy[index];
846         dummy[index] = null;
847       }
848     }
849     return seqs;
850   }
851
852   /**
853    * @return the idColour
854    */
855   public Color getIdColour()
856   {
857     return idColour;
858   }
859
860   /**
861    * @param idColour
862    *          the idColour to set
863    */
864   public void setIdColour(Color idColour)
865   {
866     this.idColour = idColour;
867   }
868
869   /**
870    * @return the representative sequence for this group
871    */
872   public SequenceI getSeqrep()
873   {
874     return seqrep;
875   }
876
877   /**
878    * set the representative sequence for this group. Note - this affects the
879    * interpretation of the Hidereps attribute.
880    * 
881    * @param seqrep
882    *          the seqrep to set (null means no sequence representative)
883    */
884   public void setSeqrep(SequenceI seqrep)
885   {
886     this.seqrep = seqrep;
887   }
888
889   /**
890    * 
891    * @return true if group has a sequence representative
892    */
893   public boolean hasSeqrep()
894   {
895     return seqrep != null;
896   }
897
898   /**
899    * visibility of rows or represented rows covered by group
900    */
901   private boolean hidereps = false;
902
903   /**
904    * set visibility of sequences covered by (if no sequence representative is
905    * defined) or represented by this group.
906    * 
907    * @param visibility
908    */
909   public void setHidereps(boolean visibility)
910   {
911     hidereps = visibility;
912   }
913
914   /**
915    * 
916    * @return true if sequences represented (or covered) by this group should be
917    *         hidden
918    */
919   public boolean isHidereps()
920   {
921     return hidereps;
922   }
923
924   /**
925    * visibility of columns intersecting this group
926    */
927   private boolean hidecols = false;
928
929   /**
930    * set intended visibility of columns covered by this group
931    * 
932    * @param visibility
933    */
934   public void setHideCols(boolean visibility)
935   {
936     hidecols = visibility;
937   }
938
939   /**
940    * 
941    * @return true if columns covered by group should be hidden
942    */
943   public boolean isHideCols()
944   {
945     return hidecols;
946   }
947
948   /**
949    * create a new sequence group from the intersection of this group with an
950    * alignment Hashtable of hidden representatives
951    * 
952    * @param alignment
953    *          (may not be null)
954    * @param hashtable
955    *          (may be null)
956    * @return new group containing sequences common to this group and alignment
957    */
958   public SequenceGroup intersect(AlignmentI alignment, Hashtable hashtable)
959   {
960     SequenceGroup sgroup = new SequenceGroup(this);
961     SequenceI[] insect = getSequencesInOrder(alignment);
962     sgroup.sequences = new Vector();
963     for (int s = 0; insect != null && s < insect.length; s++)
964     {
965       if (hashtable == null || hashtable.containsKey(insect[s]))
966       {
967         sgroup.sequences.addElement(insect[s]);
968       }
969     }
970     // Enumeration en =getSequences(hashtable).elements();
971     // while (en.hasMoreElements())
972     // {
973     // SequenceI elem = (SequenceI) en.nextElement();
974     // if (alignment.getSequences().contains(elem))
975     // {
976     // sgroup.addSequence(elem, false);
977     // }
978     // }
979     return sgroup;
980   }
981
982   /**
983    * @return the showUnconserved
984    */
985   public boolean getShowNonconserved()
986   {
987     return showNonconserved;
988   }
989
990   /**
991    * @param showNonconserved
992    *          the showUnconserved to set
993    */
994   public void setShowNonconserved(boolean displayNonconserved)
995   {
996     this.showNonconserved = displayNonconserved;
997   }
998
999   AlignmentAnnotation consensus = null, conservation = null;
1000
1001   /**
1002    * flag indicating if consensus histogram should be rendered
1003    */
1004   private boolean showConsensusHistogram;
1005
1006   /**
1007    * set this alignmentAnnotation object as the one used to render consensus
1008    * annotation
1009    * 
1010    * @param aan
1011    */
1012   public void setConsensus(AlignmentAnnotation aan)
1013   {
1014     if (consensus == null)
1015     {
1016       consensus = aan;
1017     }
1018   }
1019
1020   /**
1021    * 
1022    * @return automatically calculated consensus row
1023    */
1024   public AlignmentAnnotation getConsensus()
1025   {
1026     // TODO get or calculate and get consensus annotation row for this group
1027     int aWidth = this.getWidth();
1028     // pointer
1029     // possibility
1030     // here.
1031     if (aWidth < 0)
1032     {
1033       return null;
1034     }
1035     if (consensus == null)
1036     {
1037       consensus = new AlignmentAnnotation("", "", new Annotation[1], 0f,
1038               100f, AlignmentAnnotation.BAR_GRAPH);
1039     }
1040     consensus.hasText = true;
1041     consensus.autoCalculated = true;
1042     consensus.groupRef = this;
1043     consensus.label = "Consensus for " + getName();
1044     consensus.description = "Percent Identity";
1045     return consensus;
1046   }
1047
1048   /**
1049    * set this alignmentAnnotation object as the one used to render consensus
1050    * annotation
1051    * 
1052    * @param aan
1053    */
1054   public void setConservationRow(AlignmentAnnotation aan)
1055   {
1056     if (conservation == null)
1057     {
1058       conservation = aan;
1059     }
1060   }
1061
1062   /**
1063    * get the conservation annotation row for this group
1064    * 
1065    * @return autoCalculated annotation row
1066    */
1067   public AlignmentAnnotation getConservationRow()
1068   {
1069     if (conservation == null)
1070     {
1071       conservation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
1072               11f, AlignmentAnnotation.BAR_GRAPH);
1073     }
1074
1075     conservation.hasText = true;
1076     conservation.autoCalculated = true;
1077     conservation.groupRef = this;
1078     conservation.label = "Conservation for " + getName();
1079     conservation.description = "Conservation for group " + getName()
1080             + " less than " + consPercGaps + "% gaps";
1081     return conservation;
1082   }
1083
1084   /**
1085    * 
1086    * @return true if annotation rows have been instantiated for this group
1087    */
1088   public boolean hasAnnotationRows()
1089   {
1090     return consensus != null || conservation != null;
1091   }
1092
1093   public SequenceI getConsensusSeq()
1094   {
1095     getConsensus();
1096     StringBuffer seqs = new StringBuffer();
1097     for (int i = 0; i < consensus.annotations.length; i++)
1098     {
1099       if (consensus.annotations[i] != null)
1100       {
1101         if (consensus.annotations[i].description.charAt(0) == '[')
1102         {
1103           seqs.append(consensus.annotations[i].description.charAt(1));
1104         }
1105         else
1106         {
1107           seqs.append(consensus.annotations[i].displayCharacter);
1108         }
1109       }
1110     }
1111
1112     SequenceI sq = new Sequence("Group" + getName() + " Consensus",
1113             seqs.toString());
1114     sq.setDescription("Percentage Identity Consensus "
1115             + ((ignoreGapsInConsensus) ? " without gaps" : ""));
1116     return sq;
1117   }
1118
1119   public void setIgnoreGapsConsensus(boolean state)
1120   {
1121     if (this.ignoreGapsInConsensus != state && consensus != null)
1122     {
1123       ignoreGapsInConsensus = state;
1124       recalcConservation();
1125     }
1126     ignoreGapsInConsensus = state;
1127   }
1128
1129   public boolean getIgnoreGapsConsensus()
1130   {
1131     return ignoreGapsInConsensus;
1132   }
1133
1134   /**
1135    * @param showSequenceLogo
1136    *          indicates if a sequence logo is shown for consensus annotation
1137    */
1138   public void setshowSequenceLogo(boolean showSequenceLogo)
1139   {
1140     // TODO: decouple calculation from settings update
1141     if (this.showSequenceLogo != showSequenceLogo && consensus != null)
1142     {
1143       this.showSequenceLogo = showSequenceLogo;
1144       recalcConservation();
1145     }
1146     this.showSequenceLogo = showSequenceLogo;
1147   }
1148
1149   /**
1150    * 
1151    * @param showConsHist
1152    *          flag indicating if the consensus histogram for this group should
1153    *          be rendered
1154    */
1155   public void setShowConsensusHistogram(boolean showConsHist)
1156   {
1157
1158     if (showConsensusHistogram != showConsHist && consensus != null)
1159     {
1160       this.showConsensusHistogram = showConsHist;
1161       recalcConservation();
1162     }
1163     this.showConsensusHistogram = showConsHist;
1164   }
1165
1166   /**
1167    * @return the showConsensusHistogram
1168    */
1169   public boolean isShowConsensusHistogram()
1170   {
1171     return showConsensusHistogram;
1172   }
1173
1174   @Override
1175   /**
1176    * returns a new array with all annotation involving this group
1177    */
1178   public AlignmentAnnotation[] getAlignmentAnnotation()
1179   {
1180     // TODO add in other methods like 'getAlignmentAnnotation(String label), etc'
1181     ArrayList<AlignmentAnnotation> annot = new ArrayList<AlignmentAnnotation>();
1182     for (SequenceI seq:(Vector<SequenceI>)sequences)
1183     {
1184       for (AlignmentAnnotation al: seq.getAnnotation())
1185       {
1186         if (al.groupRef==this)
1187         {
1188           annot.add(al);
1189         }
1190       }
1191     }
1192     if (consensus!=null)
1193     {
1194       annot.add(consensus);
1195     }
1196     if (conservation!=null)
1197     {
1198       annot.add(conservation);
1199     }
1200     return annot.toArray(new AlignmentAnnotation[0]);
1201   }
1202 }