Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.datamodel

File SequenceGroup.java

 

Coverage histogram

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

Code metrics

154
346
87
1
1,488
883
182
0.53
3.98
87
2.09

Classes

Class Line # Actions
SequenceGroup 43 346 182
0.54855254.9%
 

Contributing tests

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