Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.datamodel

File SequenceGroup.java

 

Coverage histogram

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

Code metrics

154
343
86
1
1,469
877
181
0.53
3.99
86
2.1

Classes

Class Line # Actions
SequenceGroup 43 343 181 265
0.5454545654.5%
 

Contributing tests

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