Clover icon

Coverage Report

  1. Project Clover database Wed Jan 7 2026 02:39:37 GMT
  2. Package jalview.datamodel

File SequenceGroup.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

216
507
123
1
2,074
1,299
255
0.5
4.12
123
2.07

Classes

Class Line # Actions
SequenceGroup 50 507 255
0.5484633454.8%
 

Contributing tests

This file is covered by 112 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ 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    import jalview.util.MessageManager;
23    import jalview.workers.InformationThread;
24   
25    import java.awt.Color;
26    import java.beans.PropertyChangeListener;
27    import java.beans.PropertyChangeSupport;
28    import java.util.ArrayList;
29    import java.util.Arrays;
30    import java.util.Collection;
31    import java.util.Collections;
32    import java.util.HashMap;
33    import java.util.List;
34    import java.util.Map;
35   
36    import jalview.analysis.AAFrequency;
37    import jalview.analysis.AlignmentUtils;
38    import jalview.analysis.Conservation;
39    import jalview.renderer.ResidueShader;
40    import jalview.renderer.ResidueShaderI;
41    import jalview.schemes.ColourSchemeI;
42    import jalview.util.Constants;
43   
44    /**
45    * Collects a set contiguous ranges on a set of sequences
46    *
47    * @author $author$
48    * @version $Revision$
49    */
 
50    public class SequenceGroup implements AnnotatedCollectionI
51    {
52    // TODO ideally this event notification functionality should be separated into
53    // a subclass of ViewportProperties similarly to ViewportRanges.
54    // Done here as a quick fix for JAL-2665
55    public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
56   
57    protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
58    this);
59   
 
60  0 toggle public void addPropertyChangeListener(PropertyChangeListener listener)
61    {
62  0 changeSupport.addPropertyChangeListener(listener);
63    }
64   
 
65  0 toggle public void removePropertyChangeListener(PropertyChangeListener listener)
66    {
67  0 changeSupport.removePropertyChangeListener(listener);
68    }
69    // end of event notification functionality initialisation
70   
71    String groupName;
72   
73    String description;
74   
75    Conservation conserve;
76   
77    Conservation conservationData;
78   
79    ProfilesI consensusProfiles;
80   
81    ProfilesI hmmProfiles;
82   
83    boolean displayBoxes = true;
84   
85    boolean displayText = true;
86   
87    boolean colourText = false;
88   
89    /*
90    * true if the group is defined as a group on the alignment, false if it is
91    * just a selection
92    */
93    boolean isDefined = false;
94   
95    /*
96    * after Olivier's non-conserved only character display
97    */
98    boolean showNonconserved = false;
99   
100    /*
101    * sequences in the group
102    */
103    private List<SequenceI> sequences;
104   
105    /*
106    * representative sequence for this group (if any)
107    */
108    private SequenceI seqrep = null;
109   
110    int width = -1;
111   
112    /*
113    * colour scheme applied to group if any
114    */
115    public ResidueShaderI cs;
116   
117    /**
118    * start column (base 0)
119    */
120    private int startRes = 0;
121   
122    /**
123    * end column (base 0)
124    */
125    private int endRes = 0;
126   
127    public Color outlineColour = Color.black;
128   
129    public Color idColour = null;
130   
131    public int thresholdTextColour = 0;
132   
133    public Color textColour = Color.black;
134   
135    public Color textColour2 = Color.white;
136   
137    /*
138    * properties for consensus annotation
139    */
140    private boolean ignoreGapsInConsensus = true;
141   
142    private boolean showSequenceLogo = false;
143   
144    private boolean showSequenceSSLogo = false;
145   
146    /**
147    * flag indicating if logo should be rendered normalised
148    */
149    private boolean normaliseSequenceLogo;
150   
151    /*
152    * properties for HMM information annotation
153    */
154    private boolean hmmIgnoreBelowBackground = true;
155   
156    private boolean hmmUseInfoLetterHeight;
157   
158    private boolean hmmShowSequenceLogo;
159   
160    private boolean hmmNormaliseSequenceLogo;
161   
162    private boolean hmmShowHistogram;
163   
164    /*
165    * visibility of rows or represented rows covered by group
166    */
167    private boolean hidereps = false;
168   
169    /*
170    * visibility of columns intersecting this group
171    */
172    private boolean hidecols;
173   
174    AlignmentAnnotation consensus = null;
175   
176    List<AlignmentAnnotation> ssConsensus = null;
177   
178    List<String> secondaryStructureSources = null;
179   
180    AlignmentAnnotation conservation = null;
181   
182    private AlignmentAnnotation hmmInformation;
183   
184    private boolean showConsensusHistogram;
185   
186    private boolean showSSConsensusHistogram;
187   
188    private AnnotatedCollectionI context;
189   
190    public Map<String, ProfilesI> hSSConsensusProfileMap;
191   
192    // Stores annotations of the nodes in the group if the group is created from
193    // annotation based tree
194    private List<AlignmentAnnotation> annotationsFromTree;
195   
196    /**
197    * Used to determine whether the group is tree based
198    */
199    private boolean treeBased = false;
200   
201   
202    /**
203    * Constructor, assigning a generated default name of "JGroup:" with object
204    * hashcode appended
205    */
 
206  1262 toggle public SequenceGroup()
207    {
208  1262 groupName = "JGroup:" + this.hashCode();
209  1262 cs = new ResidueShader();
210  1262 sequences = new ArrayList<>();
211  1262 annotationsFromTree = new ArrayList<>();
212    }
213   
214    /**
215    * Creates a new SequenceGroup object.
216    *
217    * @param sequences
218    * @param groupName
219    * @param scheme
220    * @param displayBoxes
221    * @param displayText
222    * @param colourText
223    * @param start
224    * first column of group
225    * @param end
226    * last column of group
227    */
 
228  93 toggle public SequenceGroup(List<SequenceI> sequences, String groupName,
229    ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
230    boolean colourText, int start, int end)
231    {
232  93 this();
233  93 this.sequences = sequences;
234  93 this.groupName = groupName;
235  93 this.displayBoxes = displayBoxes;
236  93 this.displayText = displayText;
237  93 this.colourText = colourText;
238  93 this.cs = new ResidueShader(scheme);
239  93 startRes = start;
240  93 endRes = end;
241  93 recalcConservation();
242    }
243   
244    /**
245    * copy constructor
246    *
247    * @param seqsel
248    * @param keepsequences
249    * if false do not add sequences from seqsel to new instance
250    */
 
251  85 toggle public SequenceGroup(SequenceGroup seqsel)
252    {
253  85 this();
254   
255  85 if (seqsel != null)
256    {
257  77 sequences = new ArrayList<>();
258  77 sequences.addAll(seqsel.sequences);
259  77 if (seqsel.groupName != null)
260    {
261  77 groupName = new String(seqsel.groupName);
262    }
263  77 annotationsFromTree = seqsel.annotationsFromTree;
264  77 displayBoxes = seqsel.displayBoxes;
265  77 displayText = seqsel.displayText;
266  77 colourText = seqsel.colourText;
267   
268  77 startRes = seqsel.startRes;
269  77 endRes = seqsel.endRes;
270  77 cs = new ResidueShader((ResidueShader) seqsel.cs);
271  77 if (seqsel.description != null)
272    {
273  1 description = new String(seqsel.description);
274    }
275  77 hidecols = seqsel.hidecols;
276  77 hidereps = seqsel.hidereps;
277  77 showNonconserved = seqsel.showNonconserved;
278  77 showSequenceLogo = seqsel.showSequenceLogo;
279  77 showSequenceSSLogo = seqsel.showSequenceSSLogo;
280  77 normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
281  77 showConsensusHistogram = seqsel.showConsensusHistogram;
282  77 showSSConsensusHistogram = seqsel.showSSConsensusHistogram;
283  77 hmmShowSequenceLogo = seqsel.hmmShowSequenceLogo;
284  77 hmmNormaliseSequenceLogo = seqsel.hmmNormaliseSequenceLogo;
285  77 hmmShowHistogram = seqsel.hmmShowHistogram;
286  77 idColour = seqsel.idColour;
287  77 outlineColour = seqsel.outlineColour;
288  77 seqrep = seqsel.seqrep;
289  77 textColour = seqsel.textColour;
290  77 textColour2 = seqsel.textColour2;
291  77 thresholdTextColour = seqsel.thresholdTextColour;
292  77 width = seqsel.width;
293  77 ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
294  77 hmmIgnoreBelowBackground = seqsel.hmmIgnoreBelowBackground;
295  77 hmmUseInfoLetterHeight = seqsel.hmmUseInfoLetterHeight;
296  77 if (seqsel.conserve != null)
297    {
298    // todo avoid doing this if we don't actually want derived calculations
299    // !
300  0 recalcConservation(); // safer than
301    // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
302    }
303    }
304    }
305   
306    /**
307    * Constructor that copies the given list of sequences
308    *
309    * @param seqs
310    */
 
311  15 toggle public SequenceGroup(List<SequenceI> seqs)
312    {
313  15 this();
314  15 this.sequences.addAll(seqs);
315    }
316   
 
317  114 toggle public boolean isShowSequenceLogo()
318    {
319  114 return showSequenceLogo;
320    }
321   
 
322  2 toggle public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
323    {
324  2 int iSize = sequences.size();
325  2 SequenceI[] seqs = new SequenceI[iSize];
326  2 SequenceI[] inorder = getSequencesInOrder(align);
327   
328  18 for (int i = 0, ipos = 0; i < inorder.length; i++)
329    {
330  16 SequenceI seq = inorder[i];
331  16 SequenceI seqipos = seqs[ipos] = seq.getSubSequence(startRes,
332    endRes + 1);
333  16 if (seqipos != null)
334    {
335  16 if (seq.getAnnotation() != null)
336    {
337  1 AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
338    // Only copy annotation that is either a score or referenced by the
339    // alignment's annotation vector
340  2 for (int a = 0; a < seq.getAnnotation().length; a++)
341    {
342  1 AlignmentAnnotation tocopy = seq.getAnnotation()[a];
343  1 if (alann != null)
344    {
345  1 boolean found = false;
346  1 for (int pos = 0, np = alann.length; pos < np; pos++)
347    {
348  1 if (alann[pos] == tocopy)
349    {
350  1 found = true;
351  1 break;
352    }
353    }
354  1 if (!found)
355    {
356  0 continue;
357    }
358    }
359  1 AlignmentAnnotation newannot = new AlignmentAnnotation(
360    seq.getAnnotation()[a]);
361  1 newannot.restrict(startRes, endRes);
362  1 newannot.setSequenceRef(seqs[ipos]);
363  1 newannot.adjustForAlignment();
364  1 ContactMatrixI cm = seq
365    .getContactMatrixFor(seq.getAnnotation()[a]);
366  1 if (cm != null)
367    {
368  1 seqs[ipos].addContactListFor(newannot, cm);
369    }
370  1 seqipos.addAlignmentAnnotation(newannot);
371    }
372    }
373  16 ipos++;
374    }
375    else
376    {
377  0 iSize--;
378    }
379    }
380  2 if (iSize != inorder.length)
381    {
382  0 SequenceI[] nseqs = new SequenceI[iSize];
383  0 System.arraycopy(seqs, 0, nseqs, 0, iSize);
384  0 seqs = nseqs;
385    }
386  2 return seqs;
387   
388    }
389   
390    /**
391    * If sequence ends in gaps, the end residue can be correctly calculated here
392    *
393    * @param seq
394    * SequenceI
395    * @return int
396    */
 
397  0 toggle public int findEndRes(SequenceI seq)
398    {
399  0 int eres = 0;
400  0 char ch;
401   
402  0 for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
403    {
404  0 ch = seq.getCharAt(j);
405  0 if (!jalview.util.Comparison.isGap((ch)))
406    {
407  0 eres++;
408    }
409    }
410   
411  0 if (eres > 0)
412    {
413  0 eres += seq.getStart() - 1;
414    }
415   
416  0 return eres;
417    }
418   
 
419  17153 toggle @Override
420    public List<SequenceI> getSequences()
421    {
422  17153 return sequences;
423    }
424   
 
425  1040 toggle @Override
426    public List<SequenceI> getSequences(
427    Map<SequenceI, SequenceCollectionI> hiddenReps)
428    {
429  1040 if (hiddenReps == null)
430    {
431    // TODO: need a synchronizedCollection here ?
432  1040 return sequences;
433    }
434    else
435    {
436  0 List<SequenceI> allSequences = new ArrayList<>();
437  0 for (SequenceI seq : sequences)
438    {
439  0 allSequences.add(seq);
440  0 if (hiddenReps.containsKey(seq))
441    {
442  0 SequenceCollectionI hsg = hiddenReps.get(seq);
443  0 for (SequenceI seq2 : hsg.getSequences())
444    {
445  0 if (seq2 != seq && !allSequences.contains(seq2))
446    {
447  0 allSequences.add(seq2);
448    }
449    }
450    }
451    }
452   
453  0 return allSequences;
454    }
455    }
456   
 
457  5 toggle public SequenceI[] getSequencesAsArray(
458    Map<SequenceI, SequenceCollectionI> map)
459    {
460  5 List<SequenceI> tmp = getSequences(map);
461  5 if (tmp == null)
462    {
463  0 return null;
464    }
465  5 return tmp.toArray(new SequenceI[tmp.size()]);
466    }
467   
 
468  26 toggle public List<String> getSecondaryStructureSources()
469    {
470  26 return secondaryStructureSources;
471    }
472   
 
473  59 toggle public void setSecondaryStructureSources(
474    List<String> secondaryStructureSources)
475    {
476  59 this.secondaryStructureSources = secondaryStructureSources;
477    }
478   
479    /**
480    * DOCUMENT ME!
481    *
482    * @param col
483    * DOCUMENT ME!
484    *
485    * @return DOCUMENT ME!
486    */
 
487  0 toggle public boolean adjustForRemoveLeft(int col)
488    {
489    // return value is true if the group still exists
490  0 if (startRes >= col)
491    {
492  0 startRes = startRes - col;
493    }
494   
495  0 if (endRes >= col)
496    {
497  0 endRes = endRes - col;
498   
499  0 if (startRes > endRes)
500    {
501  0 startRes = 0;
502    }
503    }
504    else
505    {
506    // must delete this group!!
507  0 return false;
508    }
509   
510  0 return true;
511    }
512   
513    /**
514    * DOCUMENT ME!
515    *
516    * @param col
517    * DOCUMENT ME!
518    *
519    * @return DOCUMENT ME!
520    */
 
521  0 toggle public boolean adjustForRemoveRight(int col)
522    {
523  0 if (startRes > col)
524    {
525    // delete this group
526  0 return false;
527    }
528   
529  0 if (endRes >= col)
530    {
531  0 endRes = col;
532    }
533   
534  0 return true;
535    }
536   
537    /**
538    * DOCUMENT ME!
539    *
540    * @return DOCUMENT ME!
541    */
 
542  934 toggle public String getName()
543    {
544  934 return groupName;
545    }
546   
 
547  22 toggle public String getDescription()
548    {
549  22 return description;
550    }
551   
552    /**
553    * DOCUMENT ME!
554    *
555    * @param name
556    * DOCUMENT ME!
557    */
 
558  87 toggle public void setName(String name)
559    {
560  87 groupName = name;
561    // TODO: URGENT: update dependent objects (annotation row)
562    }
563   
 
564  24 toggle public void setDescription(String desc)
565    {
566  24 description = desc;
567    }
568   
569    /**
570    * DOCUMENT ME!
571    *
572    * @return DOCUMENT ME!
573    */
 
574  0 toggle public Conservation getConservation()
575    {
576  0 return conserve;
577    }
578   
579    /**
580    * DOCUMENT ME!
581    *
582    * @param c
583    * DOCUMENT ME!
584    */
 
585  0 toggle public void setConservation(Conservation c)
586    {
587  0 conserve = c;
588    }
589   
590    /**
591    * Add s to this sequence group. If aligment sequence is already contained in
592    * group, it will not be added again, but recalculation may happen if the flag
593    * is set.
594    *
595    * @param s
596    * alignment sequence to be added
597    * @param recalc
598    * true means Group's conservation should be recalculated
599    */
 
600  1540 toggle public void addSequence(SequenceI s, boolean recalc)
601    {
602  1540 synchronized (sequences)
603    {
604  1540 if (s != null && !sequences.contains(s))
605    {
606  1523 sequences.add(s);
607  1523 changeSupport.firePropertyChange(SEQ_GROUP_CHANGED,
608    sequences.size() - 1, sequences.size());
609    }
610   
611  1540 if (recalc)
612    {
613  1 recalcConservation();
614    }
615    }
616    }
617   
618    /**
619    * Max Gaps Threshold (percent) for performing a conservation calculation
620    */
621    private int consPercGaps = 25;
622   
623    /**
624    * @return Max Gaps Threshold for performing a conservation calculation
625    */
 
626  0 toggle public int getConsPercGaps()
627    {
628  0 return consPercGaps;
629    }
630   
631    /**
632    * set Max Gaps Threshold (percent) for performing a conservation calculation
633    *
634    * @param consPercGaps
635    */
 
636  0 toggle public void setConsPercGaps(int consPercGaps)
637    {
638  0 this.consPercGaps = consPercGaps;
639    }
640   
641    /**
642    * calculate residue conservation and colourschemes for group - but only if
643    * necessary. returns true if the calculation resulted in a visible change to
644    * group
645    */
 
646  509 toggle public boolean recalcConservation()
647    {
648  509 return recalcConservation(false);
649    }
650   
651    /**
652    * Recalculates column consensus, conservation, and HMM annotation for the
653    * group (as applicable). Returns true if the calculation resulted in a
654    * visible change to group.
655    *
656    * @param defer
657    * when set, colourschemes for this group are not refreshed after
658    * recalculation
659    */
 
660  509 toggle public boolean recalcConservation(boolean defer)
661    {
662  509 if (cs == null && consensus == null && conservation == null
663    && hmmInformation == null)
664    {
665  0 return false;
666    }
667    // TODO: try harder to detect changes in state in order to minimise
668    // recalculation effort
669  509 boolean upd = false;
670  509 try
671    {
672  509 ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
673    endRes + 1, showSequenceLogo);
674  509 if (hmmInformation != null)
675    {
676  0 HiddenMarkovModel hmm = hmmInformation.sequenceRef.getHMM();
677   
678  0 ProfilesI info = AAFrequency.calculateHMMProfiles(hmm,
679    (endRes + 1) - startRes, startRes, endRes + 1,
680    hmmIgnoreBelowBackground, hmmUseInfoLetterHeight);
681  0 _updateInformationRow(info);
682  0 upd = true;
683    }
684  509 if (consensus != null)
685    {
686  144 _updateConsensusRow(cnsns, sequences.size());
687  144 upd = true;
688    }
689  509 if (cs != null)
690    {
691  509 cs.setConsensus(cnsns);
692  509 upd = true;
693    }
694   
695  509 hSSConsensusProfileMap = new HashMap<String, ProfilesI>();
696  509 List<String> ssSources = new ArrayList<String>();
697  509 AnnotatedCollectionI aa = this.getContext();
698   
699  509 if (aa != null)
700    {
701  416 ssSources = AlignmentUtils.extractSSSourceInAlignmentAnnotation(
702    aa.getAlignmentAnnotation());
703    }
704  509 if (ssSources != null)
705    {
706  509 ssSources.add(Constants.SS_ALL_PROVIDERS);
707   
708  509 for (String ssSource : ssSources)
709    {
710  549 ProfilesI hSSConsensus = AAFrequency.calculateSS(sequences,
711    startRes, endRes + 1, showSequenceLogo, ssSource,this);
712  549 hSSConsensusProfileMap.put(ssSource, hSSConsensus);
713    }
714    }
715   
716  509 if (ssConsensus != null)
717    {
718  0 _updateSSConsensusRow(hSSConsensusProfileMap, sequences.size());
719  0 upd = true;
720    }
721   
722  509 if (cs != null)
723    {
724  509 cs.setSSConsensusProfileMap(hSSConsensusProfileMap);
725  509 upd = true;
726    }
727   
728  509 if ((conservation != null)
729    || (cs != null && cs.conservationApplied()))
730    {
731  3 Conservation c = new Conservation(groupName, sequences, startRes,
732    endRes + 1);
733  3 c.calculate();
734  3 c.verdict(false, consPercGaps);
735  3 if (conservation != null)
736    {
737  0 _updateConservationRow(c);
738    }
739  3 if (cs != null)
740    {
741  3 if (cs.conservationApplied())
742    {
743  3 cs.setConservation(c);
744    }
745    }
746    // eager update - will cause a refresh of overview regardless
747  3 upd = true;
748    }
749  509 if (cs != null && !defer)
750    {
751    // TODO: JAL-2034 should cs.alignmentChanged modify return state
752  509 cs.alignmentChanged(context != null ? context : this, null);
753  509 return true;
754    }
755    else
756    {
757  0 return upd;
758    }
759    } catch (java.lang.OutOfMemoryError err)
760    {
761    // TODO: catch OOM
762  0 jalview.bin.Console
763    .outPrintln("Out of memory loading groups: " + err);
764    }
765  0 return upd;
766    }
767   
 
768  0 toggle private void _updateConservationRow(Conservation c)
769    {
770  0 if (conservation == null)
771    {
772  0 getConservation();
773    }
774    // update Labels
775  0 conservation.label = "Conservation for " + getName();
776  0 conservation.description = "Conservation for group " + getName()
777    + " less than " + consPercGaps + "% gaps";
778    // preserve width if already set
779  0 int aWidth = (conservation.annotations != null)
780  0 ? (endRes < conservation.annotations.length
781    ? conservation.annotations.length
782    : endRes + 1)
783    : endRes + 1;
784  0 conservation.annotations = null;
785  0 conservation.annotations = new Annotation[aWidth]; // should be alignment
786    // width
787  0 c.completeAnnotations(conservation, null, startRes, endRes + 1);
788    }
789   
790    public ProfilesI consensusData = null;
791   
 
792  144 toggle private void _updateConsensusRow(ProfilesI cnsns, long nseq)
793    {
794  144 if (consensus == null)
795    {
796  0 getConsensus();
797    }
798  144 consensus.label = "Consensus for " + getName();
799  144 consensus.description = "Percent Identity";
800  144 consensusData = cnsns;
801    // preserve width if already set
802  144 int aWidth = (consensus.annotations != null)
803  144 ? (endRes < consensus.annotations.length
804    ? consensus.annotations.length
805    : endRes + 1)
806    : endRes + 1;
807  144 consensus.annotations = null;
808  144 consensus.annotations = new Annotation[aWidth]; // should be alignment width
809   
810  144 AAFrequency.completeConsensus(consensus, cnsns, startRes, endRes + 1,
811    ignoreGapsInConsensus, showSequenceLogo, nseq); // TODO: setting
812    // container
813    // for
814    // ignoreGapsInConsensusCalculation);
815    }
816   
817    /**
818    * Updates the secondary structure consensus row based on the provided
819    * profiles map and the number of sequences.
820    *
821    * @param hSSConsensusProfileMap
822    * A map containing secondary structure consensus profiles for each
823    * providers.
824    * @param nseq
825    * The number of sequences.
826    */
 
827  0 toggle private void _updateSSConsensusRow(
828    Map<String, ProfilesI> hSSConsensusProfileMap, long nseq)
829    {
830    // Get a list of secondary structure sources from the profile map keys
831  0 List<String> ssSources = new ArrayList<>(
832    hSSConsensusProfileMap.keySet());
833  0 secondaryStructureSources = new ArrayList<String>();
834   
835    // Sort the secondary structure sources alphabetically
836  0 Collections.sort(ssSources);
837   
838    // Initialize ssConsensus if it is null
839  0 if (ssConsensus == null)
840    {
841  0 getSSConsensus(ssSources);
842    }
843   
844    // Iterate through each alignment annotation in the ssConsensus list
845  0 for (AlignmentAnnotation aa : ssConsensus)
846    {
847  0 ProfilesI profile = null;
848  0 String ssSource = null;
849   
850    // Find the matching profile for the current annotation based on its
851    // description
852  0 for (String source : ssSources)
853    {
854  0 if (aa.description.startsWith(source))
855    {
856  0 profile = hSSConsensusProfileMap.get(source);
857  0 ssSource = source;
858    }
859    }
860   
861    // If no matching profile is found, continue to the next annotation
862  0 if (profile == null)
863    {
864  0 continue;
865    }
866   
867    // Update the label and description of the annotation with the
868    // source/provider
869  0 aa.label = Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL + " "
870    + ssSource + " " + getName();
871  0 aa.description = ssSource + " "
872    + Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL + " for "
873    + getName();
874   
875    // Get the width of the annotations array
876  0 int aWidth = (aa.annotations != null)
877  0 ? (endRes < aa.annotations.length ? aa.annotations.length
878    : endRes + 1)
879    : endRes + 1;
880  0 aa.annotations = new Annotation[aWidth];
881   
882    // Complete the secondary structure consensus
883  0 AAFrequency.completeSSConsensus(aa, profile, startRes, endRes + 1,
884    ignoreGapsInConsensus, showSequenceLogo, nseq);
885   
886    // Add the provider to the list if the no of sequences
887    // contributed to the secondary structure consensus is
888    // more than 0.
889  0 if (aa.getNoOfSequencesIncluded() > 0
890    && !Constants.SS_ALL_PROVIDERS.equals(ssSource))
891    {
892    // Remove "All" from the hidden types list{
893  0 secondaryStructureSources.add(ssSource);
894    }
895    }
896    }
897   
898    /**
899    * Recalculates the information content on the HMM annotation
900    *
901    * @param cnsns
902    */
 
903  0 toggle private void _updateInformationRow(ProfilesI cnsns)
904    {
905  0 if (hmmInformation == null)
906    {
907  0 createInformationAnnotation();
908    }
909  0 hmmInformation.description = MessageManager
910    .getString("label.information_description");
911  0 setHmmProfiles(cnsns);
912    // preserve width if already set
913  0 int aWidth = (hmmInformation.annotations != null)
914  0 ? (endRes < hmmInformation.annotations.length
915    ? hmmInformation.annotations.length : endRes + 1)
916    : endRes + 1;
917  0 hmmInformation.annotations = null;
918  0 hmmInformation.annotations = new Annotation[aWidth]; // should be alignment
919    // width
920  0 hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
921  0 AAFrequency.completeInformation(hmmInformation, cnsns, startRes,
922    endRes + 1);
923    }
924   
925    /**
926    * @param s
927    * sequence to either add or remove from group
928    * @param recalc
929    * flag passed to delete/addSequence to indicate if group properties
930    * should be recalculated
931    */
 
932  235 toggle public void addOrRemove(SequenceI s, boolean recalc)
933    {
934  235 synchronized (sequences)
935    {
936  235 if (sequences.contains(s))
937    {
938  1 deleteSequence(s, recalc);
939    }
940    else
941    {
942  234 addSequence(s, recalc);
943    }
944    }
945    }
946   
947    /**
948    * TODO @RENIA - this doesn't actually do what someone might think - NEEDS
949    * DOCUMENTING AND A TEST
950    *
951    * @param alignmentAnnotation
952    * @return
953    */
 
954  0 toggle public boolean addOrRemoveAnnotation(
955    AlignmentAnnotation alignmentAnnotation)
956    {
957  0 synchronized (annotationsFromTree)
958    {
959  0 if (annotationsFromTree.contains(alignmentAnnotation))
960    {
961  0 annotationsFromTree.remove(alignmentAnnotation);
962   
963  0 for(AlignmentAnnotation annot : annotationsFromTree) {
964  0 if(annot.sequenceRef == alignmentAnnotation.sequenceRef) {
965  0 return true;
966    }
967    }
968   
969  0 return false;
970    }
971    else
972    {
973  0 annotationsFromTree.add(alignmentAnnotation);
974  0 return true;
975    }
976    }
977    }
978   
 
979  41 toggle public void addAnnotationFromTree(AlignmentAnnotation alignmentAnnotation)
980    {
981  41 if(annotationsFromTree == null)
982    {
983  0 annotationsFromTree = new ArrayList<AlignmentAnnotation>();
984    }
985   
986  41 synchronized (annotationsFromTree)
987    {
988  41 if (!annotationsFromTree.contains(alignmentAnnotation))
989    {
990   
991  41 annotationsFromTree.add(alignmentAnnotation);
992   
993    }
994    }
995    }
996   
997    /**
998    * remove
999    *
1000    * @param s
1001    * to be removed
1002    * @param recalc
1003    * true means recalculate conservation
1004    */
 
1005  35 toggle public void deleteSequence(SequenceI s, boolean recalc)
1006    {
1007  35 synchronized (sequences)
1008    {
1009  35 sequences.remove(s);
1010  35 changeSupport.firePropertyChange(SEQ_GROUP_CHANGED,
1011    sequences.size() + 1, sequences.size());
1012   
1013  35 if (recalc)
1014    {
1015  0 recalcConservation();
1016    }
1017    }
1018    }
1019   
1020    /**
1021    *
1022    *
1023    * @return the first column selected by this group. Runs from 0<=i<N_cols
1024    */
 
1025  335270 toggle @Override
1026    public int getStartRes()
1027    {
1028  335277 return startRes;
1029    }
1030   
1031    /**
1032    *
1033    * @return the groups last selected column. Runs from 0<=i<N_cols
1034    */
 
1035  256099 toggle @Override
1036    public int getEndRes()
1037    {
1038  256111 return endRes;
1039    }
1040   
1041    /**
1042    * Set the first column selected by this group. Runs from 0<=i<N_cols
1043    *
1044    * @param newStart
1045    */
 
1046  398 toggle public void setStartRes(int newStart)
1047    {
1048  398 int before = startRes;
1049  398 startRes = Math.max(0, newStart); // sanity check for negative start column
1050    // positions
1051  398 changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, startRes);
1052   
1053    }
1054   
1055    /**
1056    * Set the groups last selected column. Runs from 0<=i<N_cols
1057    *
1058    * @param i
1059    */
 
1060  419 toggle public void setEndRes(int i)
1061    {
1062  419 int before = endRes;
1063  419 endRes = i;
1064  419 changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, endRes);
1065    }
1066   
1067    /**
1068    * @return number of sequences in group
1069    */
 
1070  844 toggle public int getSize()
1071    {
1072  844 return sequences.size();
1073    }
1074   
1075    /**
1076    * @param i
1077    * @return the ith sequence
1078    */
 
1079  96 toggle public SequenceI getSequenceAt(int i)
1080    {
1081  96 return sequences.get(i);
1082    }
1083   
1084    /**
1085    * @param state
1086    * colourText
1087    */
 
1088  24 toggle public void setColourText(boolean state)
1089    {
1090  24 colourText = state;
1091    }
1092   
1093    /**
1094    * DOCUMENT ME!
1095    *
1096    * @return DOCUMENT ME!
1097    */
 
1098  31177 toggle public boolean getColourText()
1099    {
1100  31177 return colourText;
1101    }
1102   
1103    /**
1104    * DOCUMENT ME!
1105    *
1106    * @param state
1107    * DOCUMENT ME!
1108    */
 
1109  20 toggle public void setDisplayText(boolean state)
1110    {
1111  20 displayText = state;
1112    }
1113   
1114    /**
1115    * DOCUMENT ME!
1116    *
1117    * @return DOCUMENT ME!
1118    */
 
1119  31162 toggle public boolean getDisplayText()
1120    {
1121  31162 return displayText;
1122    }
1123   
1124    /**
1125    * DOCUMENT ME!
1126    *
1127    * @param state
1128    * DOCUMENT ME!
1129    */
 
1130  20 toggle public void setDisplayBoxes(boolean state)
1131    {
1132  20 displayBoxes = state;
1133    }
1134   
1135    /**
1136    * DOCUMENT ME!
1137    *
1138    * @return DOCUMENT ME!
1139    */
 
1140  31171 toggle public boolean getDisplayBoxes()
1141    {
1142  31171 return displayBoxes;
1143    }
1144   
1145    /**
1146    * computes the width of current set of sequences and returns it
1147    *
1148    * @return DOCUMENT ME!
1149    */
 
1150  120 toggle @Override
1151    public int getWidth()
1152    {
1153  120 synchronized (sequences)
1154    {
1155    // MC This needs to get reset when characters are inserted and deleted
1156  120 boolean first = true;
1157  120 for (SequenceI seq : sequences)
1158    {
1159  390 if (first || seq.getLength() > width)
1160    {
1161  120 width = seq.getLength();
1162  120 first = false;
1163    }
1164    }
1165  120 return width;
1166    }
1167    }
1168   
1169    /**
1170    * DOCUMENT ME!
1171    *
1172    * @param c
1173    * DOCUMENT ME!
1174    */
 
1175  89 toggle public void setOutlineColour(Color c)
1176    {
1177  89 outlineColour = c;
1178    }
1179   
1180    /**
1181    * DOCUMENT ME!
1182    *
1183    * @return DOCUMENT ME!
1184    */
 
1185  360 toggle public Color getOutlineColour()
1186    {
1187  360 return outlineColour;
1188    }
1189   
1190    /**
1191    *
1192    * returns the sequences in the group ordered by the ordering given by al.
1193    * this used to return an array with null entries regardless, new behaviour is
1194    * below. TODO: verify that this does not affect use in applet or application
1195    *
1196    * @param al
1197    * Alignment
1198    * @return SequenceI[] intersection of sequences in group with al, ordered by
1199    * al, or null if group does not intersect with al
1200    */
 
1201  99 toggle public SequenceI[] getSequencesInOrder(AlignmentI al)
1202    {
1203  99 return getSequencesInOrder(al, true);
1204    }
1205   
1206    /**
1207    * return an array representing the intersection of the group with al,
1208    * optionally returning an array the size of al.getHeight() where nulls mark
1209    * the non-intersected sequences
1210    *
1211    * @param al
1212    * @param trim
1213    * @return null or array
1214    */
 
1215  106 toggle public SequenceI[] getSequencesInOrder(AlignmentI al, boolean trim)
1216    {
1217  106 synchronized (sequences)
1218    {
1219  106 int sSize = sequences.size();
1220  106 int alHeight = al.getHeight();
1221   
1222  106 SequenceI[] seqs = new SequenceI[(trim) ? sSize : alHeight];
1223   
1224  106 int index = 0;
1225  1010 for (int i = 0; i < alHeight && index < sSize; i++)
1226    {
1227  904 if (sequences.contains(al.getSequenceAt(i)))
1228    {
1229  326 seqs[(trim) ? index : i] = al.getSequenceAt(i);
1230  326 index++;
1231    }
1232    }
1233  106 if (index == 0)
1234    {
1235  33 return null;
1236    }
1237  73 if (!trim)
1238    {
1239  0 return seqs;
1240    }
1241  73 if (index < seqs.length)
1242    {
1243  0 SequenceI[] dummy = seqs;
1244  0 seqs = new SequenceI[index];
1245  0 while (--index >= 0)
1246    {
1247  0 seqs[index] = dummy[index];
1248  0 dummy[index] = null;
1249    }
1250    }
1251  73 return seqs;
1252    }
1253    }
1254   
1255    /**
1256    * @return the idColour
1257    */
 
1258  59 toggle public Color getIdColour()
1259    {
1260  59 return idColour;
1261    }
1262   
1263    /**
1264    * @param idColour
1265    * the idColour to set
1266    */
 
1267  68 toggle public void setIdColour(Color idColour)
1268    {
1269  68 this.idColour = idColour;
1270    }
1271   
1272    /**
1273    * @return the representative sequence for this group
1274    */
 
1275  31129 toggle @Override
1276    public SequenceI getSeqrep()
1277    {
1278  31129 return seqrep;
1279    }
1280   
1281    /**
1282    * set the representative sequence for this group. Note - this affects the
1283    * interpretation of the Hidereps attribute.
1284    *
1285    * @param seqrep
1286    * the seqrep to set (null means no sequence representative)
1287    */
 
1288  258 toggle @Override
1289    public void setSeqrep(SequenceI seqrep)
1290    {
1291  258 this.seqrep = seqrep;
1292    }
1293   
1294    /**
1295    *
1296    * @return true if group has a sequence representative
1297    */
 
1298  10 toggle @Override
1299    public boolean hasSeqrep()
1300    {
1301  10 return seqrep != null;
1302    }
1303   
1304    /**
1305    * set visibility of sequences covered by (if no sequence representative is
1306    * defined) or represented by this group.
1307    *
1308    * @param visibility
1309    */
 
1310  6 toggle public void setHidereps(boolean visibility)
1311    {
1312  6 hidereps = visibility;
1313    }
1314   
1315    /**
1316    *
1317    * @return true if sequences represented (or covered) by this group should be
1318    * hidden
1319    */
 
1320  12 toggle public boolean isHidereps()
1321    {
1322  12 return hidereps;
1323    }
1324   
1325    /**
1326    * set intended visibility of columns covered by this group
1327    *
1328    * @param visibility
1329    */
 
1330  1 toggle public void setHideCols(boolean visibility)
1331    {
1332  1 hidecols = visibility;
1333    }
1334   
1335    /**
1336    *
1337    * @return true if columns covered by group should be hidden
1338    */
 
1339  12 toggle public boolean isHideCols()
1340    {
1341  12 return hidecols;
1342    }
1343   
1344    /**
1345    * create a new sequence group from the intersection of this group with an
1346    * alignment Hashtable of hidden representatives
1347    *
1348    * @param alignment
1349    * (may not be null)
1350    * @param map
1351    * (may be null)
1352    * @return new group containing sequences common to this group and alignment
1353    */
 
1354  33 toggle public SequenceGroup intersect(AlignmentI alignment,
1355    Map<SequenceI, SequenceCollectionI> map)
1356    {
1357  33 SequenceGroup sgroup = new SequenceGroup(this);
1358  33 SequenceI[] insect = getSequencesInOrder(alignment);
1359  33 sgroup.sequences = new ArrayList<>();
1360  33 for (int s = 0; insect != null && s < insect.length; s++)
1361    {
1362  0 if (map == null || map.containsKey(insect[s]))
1363    {
1364  0 sgroup.sequences.add(insect[s]);
1365    }
1366    }
1367  33 return sgroup;
1368    }
1369   
1370    /**
1371    * This method returns annotations in the group formed by
1372    * splitting annotation based tree
1373    * @return annotations in the group from annotation based tree
1374    */
 
1375  1126 toggle public List<AlignmentAnnotation> getAnnotationsFromTree()
1376    {
1377  1126 return annotationsFromTree;
1378    }
1379   
1380    /**
1381    * This method sets annotations in the group formed by
1382    * splitting annotation based tree
1383    * @param annotationsFromTree
1384    */
 
1385  0 toggle public void setAnnotationsFromTree(
1386    List<AlignmentAnnotation> annotationsFromTree)
1387    {
1388  0 this.annotationsFromTree = annotationsFromTree;
1389    }
1390   
 
1391  164 toggle public boolean hasAnnotationsFromTree()
1392    {
1393  164 return (this.annotationsFromTree != null
1394    && !this.annotationsFromTree.isEmpty()) ;
1395    }
1396   
1397   
1398    /**
1399    * @return the showUnconserved
1400    */
 
1401  31039 toggle public boolean getShowNonconserved()
1402    {
1403  31039 return showNonconserved;
1404    }
1405   
1406    /**
1407    * @param showNonconserved
1408    * the showUnconserved to set
1409    */
 
1410  90 toggle public void setShowNonconserved(boolean displayNonconserved)
1411    {
1412  90 this.showNonconserved = displayNonconserved;
1413    }
1414   
1415    /**
1416    * set this alignmentAnnotation object as the one used to render consensus
1417    * annotation
1418    *
1419    * @param aan
1420    */
 
1421  24 toggle public void setConsensus(AlignmentAnnotation aan)
1422    {
1423  24 if (consensus == null)
1424    {
1425  24 consensus = aan;
1426    }
1427    }
1428   
 
1429  0 toggle public void setSSConsensusRow(AlignmentAnnotation aan)
1430    {
1431   
1432  0 if (ssConsensus == null)
1433    {
1434  0 ssConsensus = new ArrayList<AlignmentAnnotation>();
1435  0 ssConsensus.add(aan);
1436    }
1437    else
1438    {
1439  0 boolean annotExists = ssConsensus.stream()
1440    .anyMatch(ssa -> ssa.label.equals(aan.label));
1441  0 if (!annotExists)
1442    {
1443  0 ssConsensus.add(aan);
1444    }
1445    }
1446    }
1447   
1448    /**
1449    *
1450    * @return automatically calculated consensus row note: the row is a stub if a
1451    * consensus calculation has not yet been performed on the group
1452    */
 
1453  114 toggle public AlignmentAnnotation getConsensus()
1454    {
1455    // TODO get or calculate and get consensus annotation row for this group
1456  114 int aWidth = this.getWidth();
1457    // pointer
1458    // possibility
1459    // here.
1460  114 if (aWidth < 0)
1461    {
1462  0 return null;
1463    }
1464  114 if (consensus == null)
1465    {
1466  0 consensus = new AlignmentAnnotation("", "", new Annotation[1], 0f,
1467    100f, AlignmentAnnotation.BAR_GRAPH);
1468  0 consensus.hasText = true;
1469  0 consensus.autoCalculated = true;
1470  0 consensus.groupRef = this;
1471  0 consensus.label = "Consensus for " + getName();
1472  0 consensus.description = "Percent Identity";
1473    }
1474  114 return consensus;
1475    }
1476   
 
1477  0 toggle public List<AlignmentAnnotation> getSSConsensus(List<String> ssSources)
1478    {
1479    // TODO get or calculate and get consensus annotation row for this group
1480  0 int aWidth = this.getWidth();
1481    // pointer
1482    // possibility
1483    // here.
1484  0 if (aWidth < 0)
1485    {
1486  0 return null;
1487    }
1488  0 if (ssConsensus == null && ssSources != null)
1489    {
1490  0 ssConsensus = new ArrayList<AlignmentAnnotation>();
1491   
1492  0 for (String ssSource : ssSources)
1493    {
1494  0 AlignmentAnnotation aa = new AlignmentAnnotation("", "",
1495    new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1496    // Setting the annotation visibility to true of the provider is "All"
1497    // and false otherwise.
1498  0 aa.visible = Constants.SS_ALL_PROVIDERS.equals(ssSource);
1499  0 aa.hasText = true;
1500  0 aa.autoCalculated = true;
1501  0 aa.groupRef = this;
1502   
1503  0 aa.label = Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL + " "
1504    + ssSource + " " + getName();
1505  0 aa.description = ssSource + " "
1506    + Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL + " for "
1507    + getName();
1508  0 ssConsensus.add(aa);
1509    }
1510   
1511    }
1512  0 return ssConsensus;
1513    }
1514   
1515    /**
1516    * Creates the Hidden Markov Model annotation for this group
1517    */
 
1518  0 toggle void createInformationAnnotation()
1519    {
1520  0 hmmInformation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
1521    6.25f, AlignmentAnnotation.BAR_GRAPH);
1522  0 hmmInformation.hasText = true;
1523  0 hmmInformation.autoCalculated = false;
1524  0 hmmInformation.groupRef = this;
1525  0 hmmInformation.label = getName();
1526  0 hmmInformation.description = MessageManager
1527    .getString("label.information_description");
1528  0 hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
1529    }
1530   
1531    /**
1532    * set this alignmentAnnotation object as the one used to render consensus
1533    * annotation
1534    *
1535    * @param aan
1536    */
 
1537  0 toggle public void setConservationRow(AlignmentAnnotation aan)
1538    {
1539  0 if (conservation == null)
1540    {
1541  0 conservation = aan;
1542    }
1543    }
1544   
1545    /**
1546    * get the conservation annotation row for this group
1547    *
1548    * @return autoCalculated annotation row
1549    */
 
1550  0 toggle public AlignmentAnnotation getConservationRow()
1551    {
1552  0 if (conservation == null)
1553    {
1554  0 conservation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
1555    11f, AlignmentAnnotation.BAR_GRAPH);
1556    }
1557   
1558  0 conservation.hasText = true;
1559  0 conservation.autoCalculated = true;
1560  0 conservation.groupRef = this;
1561  0 conservation.label = "Conservation for " + getName();
1562  0 conservation.description = "Conservation for group " + getName()
1563    + " less than " + consPercGaps + "% gaps";
1564  0 return conservation;
1565    }
1566   
1567    /**
1568    *
1569    * @return true if annotation rows have been instantiated for this group
1570    */
 
1571  0 toggle public boolean hasAnnotationRows()
1572    {
1573  0 return consensus != null || conservation != null;
1574    }
1575   
 
1576  0 toggle public SequenceI getConsensusSeq()
1577    {
1578  0 getConsensus();
1579  0 StringBuffer seqs = new StringBuffer();
1580  0 for (int i = 0; i < consensus.annotations.length; i++)
1581    {
1582  0 if (consensus.annotations[i] != null)
1583    {
1584  0 String desc = consensus.annotations[i].description;
1585  0 if (desc.length() > 1 && desc.charAt(0) == '[')
1586    {
1587  0 seqs.append(desc.charAt(1));
1588    }
1589    else
1590    {
1591  0 seqs.append(consensus.annotations[i].displayCharacter);
1592    }
1593    }
1594    }
1595   
1596  0 SequenceI sq = new Sequence("Group" + getName() + " Consensus",
1597    seqs.toString());
1598  0 sq.setDescription("Percentage Identity Consensus "
1599  0 + ((ignoreGapsInConsensus) ? " without gaps" : ""));
1600  0 return sq;
1601    }
1602   
 
1603  60 toggle public void setIgnoreGapsConsensus(boolean state)
1604    {
1605  60 if (this.ignoreGapsInConsensus != state && consensus != null)
1606    {
1607  0 ignoreGapsInConsensus = state;
1608  0 recalcConservation();
1609    }
1610  60 ignoreGapsInConsensus = state;
1611    }
1612   
 
1613  32 toggle public boolean getIgnoreGapsConsensus()
1614    {
1615  32 return ignoreGapsInConsensus;
1616    }
1617   
 
1618  0 toggle public void setIgnoreBelowBackground(boolean state)
1619    {
1620  0 hmmIgnoreBelowBackground = state;
1621    }
1622   
 
1623  0 toggle public boolean isIgnoreBelowBackground()
1624    {
1625  0 return hmmIgnoreBelowBackground;
1626    }
1627   
 
1628  0 toggle public void setInfoLetterHeight(boolean state)
1629    {
1630  0 hmmUseInfoLetterHeight = state;
1631    }
1632   
 
1633  0 toggle public boolean isUseInfoLetterHeight()
1634    {
1635  0 return hmmUseInfoLetterHeight;
1636    }
1637   
1638    /**
1639    * @param showSequenceLogo
1640    * indicates if a sequence logo is shown for consensus annotation
1641    */
 
1642  71 toggle public void setshowSequenceLogo(boolean showSequenceLogo)
1643    {
1644    // TODO: decouple calculation from settings update
1645  71 if (this.showSequenceLogo != showSequenceLogo && consensus != null)
1646    {
1647  0 this.showSequenceLogo = showSequenceLogo;
1648  0 recalcConservation();
1649    }
1650  71 this.showSequenceLogo = showSequenceLogo;
1651    }
1652   
 
1653  11 toggle public void setshowSequenceSSLogo(boolean showSequenceSSLogo)
1654    {
1655    // TODO: decouple calculation from settings update
1656  11 if (this.showSequenceSSLogo != showSequenceSSLogo
1657    && ssConsensus != null)
1658    {
1659  0 this.showSequenceSSLogo = showSequenceSSLogo;
1660  0 recalcConservation();
1661    }
1662  11 this.showSequenceSSLogo = showSequenceSSLogo;
1663    }
1664   
1665    /**
1666    *
1667    * @param showSSConsHist
1668    * flag indicating if the secondary structure consensus histogram for this group should
1669    * be rendered
1670    */
 
1671  11 toggle public void setShowSSConsensusHistogram(boolean showSSConsHist)
1672    {
1673   
1674  11 if (showSSConsensusHistogram != showSSConsHist && ssConsensus != null)
1675    {
1676  0 this.showSSConsensusHistogram = showSSConsHist;
1677  0 recalcConservation();
1678    }
1679  11 this.showSSConsensusHistogram = showSSConsHist;
1680    }
1681   
1682    /**
1683    *
1684    * @param showConsHist
1685    * flag indicating if the consensus histogram for this group should
1686    * be rendered
1687    */
 
1688  71 toggle public void setShowConsensusHistogram(boolean showConsHist)
1689    {
1690   
1691  71 if (showConsensusHistogram != showConsHist && consensus != null)
1692    {
1693  0 this.showConsensusHistogram = showConsHist;
1694  0 recalcConservation();
1695    }
1696  71 this.showConsensusHistogram = showConsHist;
1697    }
1698   
1699    /**
1700    * @return the showConsensusHistogram
1701    */
 
1702  114 toggle public boolean isShowConsensusHistogram()
1703    {
1704  114 return showConsensusHistogram;
1705    }
1706   
1707    /**
1708    * set flag indicating if logo should be normalised when rendered
1709    *
1710    * @param norm
1711    */
 
1712  71 toggle public void setNormaliseSequenceLogo(boolean norm)
1713    {
1714  71 normaliseSequenceLogo = norm;
1715    }
1716   
 
1717  114 toggle public boolean isNormaliseSequenceLogo()
1718    {
1719  114 return normaliseSequenceLogo;
1720    }
1721   
 
1722  925 toggle @Override
1723    /**
1724    * returns a new array with all annotation involving this group
1725    */
1726    public AlignmentAnnotation[] getAlignmentAnnotation()
1727    {
1728    // TODO add in other methods like 'getAlignmentAnnotation(String label),
1729    // etc'
1730  925 ArrayList<AlignmentAnnotation> annot = new ArrayList<>();
1731  925 synchronized (sequences)
1732    {
1733  925 for (SequenceI seq : sequences)
1734    {
1735  6875 AlignmentAnnotation[] aa = seq.getAnnotation();
1736  6875 if (aa != null)
1737    {
1738  525 for (AlignmentAnnotation al : aa)
1739    {
1740  4515 if (al.groupRef == this)
1741    {
1742  0 annot.add(al);
1743    }
1744    }
1745    }
1746    }
1747  925 if (consensus != null)
1748    {
1749  384 annot.add(consensus);
1750    }
1751  925 if (conservation != null)
1752    {
1753  0 annot.add(conservation);
1754    }
1755    }
1756  925 return annot.toArray(new AlignmentAnnotation[0]);
1757    }
1758   
 
1759  5 toggle @Override
1760    public boolean isSecondaryStructurePresent()
1761    {
1762    // TODO should merge with getAlignmentAnnotation and use a lambda. Need a
1763    // specific test for secondary structure annotation too.
1764   
1765  5 synchronized (sequences)
1766    {
1767  5 for (SequenceI seq : sequences)
1768    {
1769  9 AlignmentAnnotation[] aa = seq.getAnnotation();
1770  9 if (AlignmentUtils.isSecondaryStructurePresent(aa))
1771    {
1772  2 return true;
1773    }
1774    }
1775    }
1776  3 return false;
1777    }
1778   
1779    /**
1780    * This method calculates and returns the no of secondary annotations
1781    * present in the group
1782    * @return
1783    */
 
1784  0 toggle public int noOfSecondaryStructureAnnots()
1785    {
1786  0 int count = 0;
1787  0 synchronized (sequences)
1788    {
1789  0 for (SequenceI seq : sequences)
1790    {
1791  0 AlignmentAnnotation[] aa = seq.getAnnotation();
1792  0 if (AlignmentUtils.getSecondaryStructureAnnots(aa) != null)
1793    {
1794  0 count = count + AlignmentUtils.getSecondaryStructureAnnots(aa).size();
1795    }
1796    }
1797    }
1798  0 return count;
1799    }
1800   
1801   
 
1802  0 toggle @Override
1803    public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
1804    {
1805  0 return AlignmentAnnotation.findAnnotation(
1806    Arrays.asList(getAlignmentAnnotation()), calcId);
1807    }
1808   
 
1809  0 toggle @Override
1810    public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
1811    String calcId, String label)
1812    {
1813  0 return AlignmentAnnotation.findAnnotations(
1814    Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
1815    }
1816   
1817    /**
1818    * Answer true if any annotation matches the calcId passed in (if not null).
1819    *
1820    * @param calcId
1821    * @return
1822    */
 
1823  0 toggle public boolean hasAnnotation(String calcId)
1824    {
1825  0 return AlignmentAnnotation
1826    .hasAnnotation(Arrays.asList(getAlignmentAnnotation()), calcId);
1827    }
1828   
1829    /**
1830    * Remove all sequences from the group (leaving other properties unchanged).
1831    */
 
1832  15 toggle public void clear()
1833    {
1834  15 synchronized (sequences)
1835    {
1836  15 int before = sequences.size();
1837  15 sequences.clear();
1838  15 changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before,
1839    sequences.size());
1840    }
1841    }
1842   
1843    /**
1844    * Sets the alignment or group context for this group, and whether it is
1845    * defined as a group
1846    *
1847    * @param ctx
1848    * the context for the group
1849    * @param defined
1850    * whether the group is defined on the alignment or is just a
1851    * selection
1852    * @throws IllegalArgumentException
1853    * if setting the context would result in a circular reference chain
1854    */
 
1855  181 toggle public void setContext(AnnotatedCollectionI ctx, boolean defined)
1856    {
1857  181 setContext(ctx);
1858  180 this.isDefined = defined;
1859    }
1860   
1861    /**
1862    * Sets the alignment or group context for this group
1863    *
1864    * @param ctx
1865    * the context for the group
1866    * @throws IllegalArgumentException
1867    * if setting the context would result in a circular reference chain
1868    */
 
1869  226 toggle public void setContext(AnnotatedCollectionI ctx)
1870    {
1871  226 AnnotatedCollectionI ref = ctx;
1872  562 while (ref != null)
1873    {
1874  339 if (ref == this || ref.getContext() == ctx)
1875    {
1876  3 throw new IllegalArgumentException(
1877    "Circular reference in SequenceGroup.context");
1878    }
1879  336 ref = ref.getContext();
1880    }
1881  223 this.context = ctx;
1882    }
1883   
1884    /*
1885    * (non-Javadoc)
1886    *
1887    * @see jalview.datamodel.AnnotatedCollectionI#getContext()
1888    */
 
1889  646 toggle @Override
1890    public AnnotatedCollectionI getContext()
1891    {
1892  646 return context;
1893    }
1894   
 
1895  9 toggle public boolean isDefined()
1896    {
1897  9 return isDefined;
1898    }
1899   
 
1900  88 toggle public void setColourScheme(ColourSchemeI scheme)
1901    {
1902  88 if (cs == null)
1903    {
1904  1 cs = new ResidueShader();
1905    }
1906  88 cs.setColourScheme(scheme);
1907    }
1908   
 
1909  2 toggle public void setGroupColourScheme(ResidueShaderI scheme)
1910    {
1911  2 cs = scheme;
1912    }
1913   
 
1914  155 toggle public ColourSchemeI getColourScheme()
1915    {
1916  155 return cs == null ? null : cs.getColourScheme();
1917    }
1918   
 
1919  143249 toggle public ResidueShaderI getGroupColourScheme()
1920    {
1921  143251 return cs;
1922    }
1923   
 
1924  72 toggle @Override
1925    public boolean isNucleotide()
1926    {
1927  72 if (context != null)
1928    {
1929  72 return context.isNucleotide();
1930    }
1931  0 return false;
1932    }
1933   
1934    /**
1935    * @param seq
1936    * @return true if seq is a member of the group
1937    */
1938   
 
1939  35 toggle public boolean contains(SequenceI seq1)
1940    {
1941  35 return sequences.contains(seq1);
1942    }
1943   
 
1944  0 toggle public boolean containsAnnotation(AlignmentAnnotation annot)
1945    {
1946  0 if (annotationsFromTree == null)
1947    {
1948  0 return false;
1949    }
1950  0 return annotationsFromTree.contains(annot);
1951    }
1952   
1953    /**
1954    * @param seq
1955    * @param apos
1956    * @return true if startRes<=apos and endRes>=apos and seq is in the group
1957    */
 
1958  15 toggle public boolean contains(SequenceI seq, int apos)
1959    {
1960  15 return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
1961    }
1962   
1963    ////
1964    //// Contact Matrix Holder Boilerplate
1965    ////
1966    ContactMapHolder cmholder = new ContactMapHolder();
1967   
 
1968  0 toggle @Override
1969    public Collection<ContactMatrixI> getContactMaps()
1970    {
1971  0 return cmholder.getContactMaps();
1972    }
1973   
 
1974  0 toggle @Override
1975    public ContactMatrixI getContactMatrixFor(AlignmentAnnotation ann)
1976    {
1977  0 return cmholder.getContactMatrixFor(ann);
1978    }
1979   
 
1980  0 toggle @Override
1981    public ContactListI getContactListFor(AlignmentAnnotation _aa, int column)
1982    {
1983  0 return cmholder.getContactListFor(_aa, column);
1984    }
1985   
 
1986  0 toggle @Override
1987    public AlignmentAnnotation addContactList(ContactMatrixI cm)
1988    {
1989  0 AlignmentAnnotation aa = cmholder.addContactList(cm);
1990   
1991  0 Annotation _aa[] = new Annotation[getWidth()];
1992  0 Annotation dummy = new Annotation(0.0f);
1993  0 for (int i = 0; i < _aa.length; _aa[i++] = dummy)
1994    {
1995  0 ;
1996    }
1997  0 aa.annotations = _aa;
1998    // TODO passing annotations back to context to be added
1999  0 return aa;
2000    }
2001   
 
2002  0 toggle @Override
2003    public void addContactListFor(AlignmentAnnotation annotation,
2004    ContactMatrixI cm)
2005    {
2006  0 cmholder.addContactListFor(annotation, cm);
2007    }
2008   
 
2009  0 toggle public boolean isShowInformationHistogram()
2010    {
2011  0 return hmmShowHistogram;
2012    }
2013   
 
2014  11 toggle public void setShowInformationHistogram(boolean state)
2015    {
2016  11 if (hmmShowHistogram != state && hmmInformation != null)
2017    {
2018  0 this.hmmShowHistogram = state;
2019    // recalcConservation(); TODO don't know what to do here next
2020    }
2021  11 this.hmmShowHistogram = state;
2022    }
2023   
 
2024  0 toggle public boolean isShowHMMSequenceLogo()
2025    {
2026  0 return hmmShowSequenceLogo;
2027    }
2028   
 
2029  11 toggle public void setShowHMMSequenceLogo(boolean state)
2030    {
2031  11 hmmShowSequenceLogo = state;
2032    }
2033   
 
2034  0 toggle public boolean isNormaliseHMMSequenceLogo()
2035    {
2036  0 return hmmNormaliseSequenceLogo;
2037    }
2038   
 
2039  11 toggle public void setNormaliseHMMSequenceLogo(boolean state)
2040    {
2041  11 hmmNormaliseSequenceLogo = state;
2042    }
2043   
 
2044  5742 toggle public ProfilesI getConsensusData()
2045    {
2046  5742 return consensusProfiles;
2047    }
2048   
 
2049  0 toggle public ProfilesI getHmmProfiles()
2050    {
2051  0 return hmmProfiles;
2052    }
2053   
 
2054  0 toggle public void setHmmProfiles(ProfilesI hmmProfiles)
2055    {
2056  0 this.hmmProfiles = hmmProfiles;
2057    }
2058   
 
2059  12 toggle @Override
2060    public List<SequenceI> getHmmSequences()
2061    {
2062  12 List<SequenceI> result = new ArrayList<>();
2063  27 for (int i = 0; i < sequences.size(); i++)
2064    {
2065  15 SequenceI seq = sequences.get(i);
2066  15 if (seq.hasHMMProfile())
2067    {
2068  0 result.add(seq);
2069    }
2070    }
2071  12 return result;
2072    }
2073   
2074    }