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