Clover icon

Coverage Report

  1. Project Clover database Tue Nov 18 2025 10:51:49 GMT
  2. Package jalview.datamodel

File SequenceGroup.java

 

Coverage histogram

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

Code metrics

198
450
105
1
1,867
1,149
224
0.5
4.29
105
2.13

Classes

Class Line # Actions
SequenceGroup 48 450 224
0.568393156.8%
 

Contributing tests

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