Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 GMT
  2. Package jalview.datamodel

File SequenceGroup.java

 

Coverage histogram

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

Code metrics

212
497
121
1
2,036
1,276
249
0.5
4.11
121
2.06

Classes

Class Line # Actions
SequenceGroup 50 497 249
0.5469879554.7%
 

Contributing tests

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