Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.datamodel

File SequenceGroup.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
29% of files have more coverage

Code metrics

174
391
96
1
1,631
994
203
0.52
4.07
96
2.11

Classes

Class Line # Actions
SequenceGroup 44 391 203
0.6202723462%
 

Contributing tests

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