sequence groups carry their own conservation and consensus annotation rows
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.datamodel;
20
21 import java.util.*;
22
23 import java.awt.*;
24
25 import jalview.analysis.*;
26 import jalview.schemes.*;
27
28 /**
29  * Collects a set contiguous ranges on a set of sequences
30  * 
31  * @author $author$
32  * @version $Revision$
33  */
34 public class SequenceGroup
35 {
36   String groupName;
37
38   String description;
39
40   Conservation conserve;
41
42   Vector aaFrequency;
43
44   boolean displayBoxes = true;
45
46   boolean displayText = true;
47
48   boolean colourText = false;
49   /**
50    * after Olivier's non-conserved only character display
51    */
52   boolean showUnconserved = false;
53   
54   /**
55    * group members
56    */
57   private Vector sequences = new Vector();
58   /**
59    * representative sequence for this group (if any)
60    */
61   private SequenceI seqrep = null;
62   int width = -1;
63
64   /**
65    * Colourscheme applied to group if any */
66   public ColourSchemeI cs;
67
68   int startRes = 0;
69
70   int endRes = 0;
71
72   public Color outlineColour = Color.black;
73
74   public Color idColour = null;
75
76   public int thresholdTextColour = 0;
77
78   public Color textColour = Color.black;
79
80   public Color textColour2 = Color.white;
81
82   private boolean ignoreGapsInConsensusCalculation=true;
83
84   /**
85    * Creates a new SequenceGroup object.
86    */
87   public SequenceGroup()
88   {
89     groupName = "JGroup:" + this.hashCode();
90   }
91
92   /**
93    * Creates a new SequenceGroup object.
94    * 
95    * @param sequences
96    * @param groupName
97    * @param scheme
98    * @param displayBoxes
99    * @param displayText
100    * @param colourText
101    * @param start
102    *                first column of group
103    * @param end
104    *                last column of group
105    */
106   public SequenceGroup(Vector sequences, String groupName,
107           ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
108           boolean colourText, int start, int end)
109   {
110     this.sequences = sequences;
111     this.groupName = groupName;
112     this.displayBoxes = displayBoxes;
113     this.displayText = displayText;
114     this.colourText = colourText;
115     this.cs = scheme;
116     startRes = start;
117     endRes = end;
118     recalcConservation();
119   }
120   /**
121    * copy constructor
122    * @param seqsel
123    */
124   public SequenceGroup(SequenceGroup seqsel)
125   {
126     if (seqsel!=null)
127     {
128       sequences=new Vector();
129       Enumeration sq = seqsel.sequences.elements();
130       while (sq.hasMoreElements()) { 
131         sequences.addElement(sq.nextElement()); 
132       };
133       if (seqsel.groupName!=null)
134       {
135         groupName = new String(seqsel.groupName);
136       }
137       displayBoxes = seqsel.displayBoxes;
138       displayText = seqsel.displayText;
139       colourText = seqsel.colourText;
140       startRes = seqsel.startRes;
141       endRes = seqsel.endRes;
142       cs =seqsel.cs;
143       if (seqsel.description!=null)
144         description = new String(seqsel.description);
145       hidecols = seqsel.hidecols;
146       hidereps = seqsel.hidereps;
147       idColour = seqsel.idColour;
148       outlineColour = seqsel.outlineColour;
149       seqrep = seqsel.seqrep;
150       textColour = seqsel.textColour;
151       textColour2 = seqsel.textColour2;
152       thresholdTextColour = seqsel.thresholdTextColour;
153       width = seqsel.width;
154       ignoreGapsInConsensusCalculation = seqsel.ignoreGapsInConsensusCalculation;
155       if (seqsel.conserve!=null)
156       {
157         recalcConservation(); // safer than 
158         // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
159       }
160     }
161   }
162
163   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
164   {
165     int iSize = sequences.size();
166     SequenceI[] seqs = new SequenceI[iSize];
167     SequenceI[] inorder = getSequencesInOrder(align);
168
169     for (int i = 0, ipos = 0; i < inorder.length; i++)
170     {
171       SequenceI seq = inorder[i];
172
173       seqs[ipos] = seq.getSubSequence(startRes, endRes + 1);
174       if (seqs[ipos] != null)
175       {
176         seqs[ipos].setDescription(seq.getDescription());
177         seqs[ipos].setDBRef(seq.getDBRef());
178         seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
179         if (seq.getDatasetSequence() != null)
180         {
181           seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
182         }
183
184         if (seq.getAnnotation() != null)
185         {
186           AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
187           // Only copy annotation that is either a score or referenced by the
188           // alignment's annotation vector
189           for (int a = 0; a < seq.getAnnotation().length; a++)
190           {
191             AlignmentAnnotation tocopy = seq.getAnnotation()[a];
192             if (alann != null)
193             {
194               boolean found = false;
195               for (int pos = 0; pos < alann.length; pos++)
196               {
197                 if (alann[pos] == tocopy)
198                 {
199                   found = true;
200                   break;
201                 }
202               }
203               if (!found)
204                 continue;
205             }
206             AlignmentAnnotation newannot = new AlignmentAnnotation(seq
207                     .getAnnotation()[a]);
208             newannot.restrict(startRes, endRes);
209             newannot.setSequenceRef(seqs[ipos]);
210             newannot.adjustForAlignment();
211             seqs[ipos].addAlignmentAnnotation(newannot);
212           }
213         }
214         ipos++;
215       }
216       else
217       {
218         iSize--;
219       }
220     }
221     if (iSize != inorder.length)
222     {
223       SequenceI[] nseqs = new SequenceI[iSize];
224       System.arraycopy(seqs, 0, nseqs, 0, iSize);
225       seqs = nseqs;
226     }
227     return seqs;
228
229   }
230
231   /**
232    * If sequence ends in gaps, the end residue can be correctly calculated here
233    * 
234    * @param seq
235    *                SequenceI
236    * @return int
237    */
238   public int findEndRes(SequenceI seq)
239   {
240     int eres = 0;
241     char ch;
242
243     for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
244     {
245       ch = seq.getCharAt(j);
246       if (!jalview.util.Comparison.isGap((ch)))
247       {
248         eres++;
249       }
250     }
251
252     if (eres > 0)
253     {
254       eres += seq.getStart() - 1;
255     }
256
257     return eres;
258   }
259
260   public Vector getSequences(Hashtable hiddenReps)
261   {
262     if (hiddenReps == null)
263     {
264       return sequences;
265     }
266     else
267     {
268       Vector allSequences = new Vector();
269       SequenceI seq, seq2;
270       for (int i = 0; i < sequences.size(); i++)
271       {
272         seq = (SequenceI) sequences.elementAt(i);
273         allSequences.addElement(seq);
274         if (hiddenReps.containsKey(seq))
275         {
276           SequenceGroup hsg = (SequenceGroup) hiddenReps.get(seq);
277           for (int h = 0; h < hsg.getSize(); h++)
278           {
279             seq2 = hsg.getSequenceAt(h);
280             if (seq2 != seq && !allSequences.contains(seq2))
281             {
282               allSequences.addElement(seq2);
283             }
284           }
285         }
286       }
287
288       return allSequences;
289     }
290   }
291
292   public SequenceI[] getSequencesAsArray(Hashtable hiddenReps)
293   {
294     Vector tmp = getSequences(hiddenReps);
295     if (tmp == null)
296     {
297       return null;
298     }
299     SequenceI[] result = new SequenceI[tmp.size()];
300     for (int i = 0; i < result.length; i++)
301     {
302       result[i] = (SequenceI) tmp.elementAt(i);
303     }
304
305     return result;
306   }
307
308   /**
309    * DOCUMENT ME!
310    * 
311    * @param col
312    *                DOCUMENT ME!
313    * 
314    * @return DOCUMENT ME!
315    */
316   public boolean adjustForRemoveLeft(int col)
317   {
318     // return value is true if the group still exists
319     if (startRes >= col)
320     {
321       startRes = startRes - col;
322     }
323
324     if (endRes >= col)
325     {
326       endRes = endRes - col;
327
328       if (startRes > endRes)
329       {
330         startRes = 0;
331       }
332     }
333     else
334     {
335       // must delete this group!!
336       return false;
337     }
338
339     return true;
340   }
341
342   /**
343    * DOCUMENT ME!
344    * 
345    * @param col
346    *                DOCUMENT ME!
347    * 
348    * @return DOCUMENT ME!
349    */
350   public boolean adjustForRemoveRight(int col)
351   {
352     if (startRes > col)
353     {
354       // delete this group
355       return false;
356     }
357
358     if (endRes >= col)
359     {
360       endRes = col;
361     }
362
363     return true;
364   }
365
366   /**
367    * DOCUMENT ME!
368    * 
369    * @return DOCUMENT ME!
370    */
371   public String getName()
372   {
373     return groupName;
374   }
375
376   public String getDescription()
377   {
378     return description;
379   }
380
381   /**
382    * DOCUMENT ME!
383    * 
384    * @param name
385    *                DOCUMENT ME!
386    */
387   public void setName(String name)
388   {
389     groupName = name;
390   }
391
392   public void setDescription(String desc)
393   {
394     description = desc;
395   }
396
397   /**
398    * DOCUMENT ME!
399    * 
400    * @return DOCUMENT ME!
401    */
402   public Conservation getConservation()
403   {
404     return conserve;
405   }
406
407   /**
408    * DOCUMENT ME!
409    * 
410    * @param c
411    *                DOCUMENT ME!
412    */
413   public void setConservation(Conservation c)
414   {
415     conserve = c;
416   }
417
418   /**
419    * Add s to this sequence group
420    * 
421    * @param s
422    *                alignment sequence to be added
423    * @param recalc
424    *                true means Group's conservation should be recalculated
425    */
426   public void addSequence(SequenceI s, boolean recalc)
427   {
428     if (s != null && !sequences.contains(s))
429     {
430       sequences.addElement(s);
431     }
432
433     if (recalc)
434     {
435       recalcConservation();
436     }
437   }
438
439   /**
440    * calculate residue conservation for group
441    */
442   public void recalcConservation()
443   {
444     if (cs == null && consensus == null && conservation == null)
445     {
446       return;
447     }
448     
449     try
450     {
451       Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes, endRes + 1);
452       if (consensus != null)
453       {
454         _updateConsensusRow(cnsns);
455       }
456       if (cs!=null)
457       {
458         cs.setConsensus(cnsns);
459       
460       if (cs instanceof ClustalxColourScheme)
461       {
462         ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
463       }
464       }
465
466       if ((conservation!=null) || (cs!=null && cs.conservationApplied()))
467       {
468         Conservation c = new Conservation(groupName,
469                 ResidueProperties.propHash, 3, sequences, startRes,
470                 endRes + 1);
471         c.calculate();
472         c.verdict(false, 25);
473         if (conservation!=null)
474         {
475           _updateConservationRow(c);
476         }
477         if (cs!=null)
478         {
479           cs.setConservation(c);
480         
481         if (cs instanceof ClustalxColourScheme)
482         {
483           ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
484         }
485         }
486       }
487     } catch (java.lang.OutOfMemoryError err)
488     {
489       // TODO: catch OOM
490       System.out.println("Out of memory loading groups: " + err);
491     }
492
493   }
494
495   private void _updateConservationRow(Conservation c)
496   {
497     if (conservation==null)
498     {
499       getConservation();
500     }
501     // preserve width if already set
502     int aWidth = (conservation.annotations!=null) ? (endRes<conservation.annotations.length ? conservation.annotations.length : endRes+1) : endRes+1;
503     c.completeAnnotations(conservation,null, aWidth);
504   }
505
506   private void _updateConsensusRow(Hashtable[] cnsns)
507   {
508     if (consensus==null)
509     {
510       getConsensus();
511     }
512     // preserve width if already set
513     int aWidth = (consensus.annotations!=null) ? (endRes<consensus.annotations.length ? consensus.annotations.length : endRes+1) : endRes+1;
514     consensus.annotations = null;
515     consensus.annotations = new Annotation[aWidth]; // should be alignment width
516
517     AAFrequency.completeConsensus(consensus,cnsns,startRes,endRes,ignoreGapsInConsensusCalculation); // TODO: setting container for ignoreGapsInConsensusCalculation);
518   }
519
520   /**
521    * DOCUMENT ME!
522    * 
523    * @param s
524    *                DOCUMENT ME!
525    * @param recalc
526    *                DOCUMENT ME!
527    */
528   public void addOrRemove(SequenceI s, boolean recalc)
529   {
530     if (sequences.contains(s))
531     {
532       deleteSequence(s, recalc);
533     }
534     else
535     {
536       addSequence(s, recalc);
537     }
538   }
539
540   /**
541    * DOCUMENT ME!
542    * 
543    * @param s
544    *                DOCUMENT ME!
545    * @param recalc
546    *                DOCUMENT ME!
547    */
548   public void deleteSequence(SequenceI s, boolean recalc)
549   {
550     sequences.removeElement(s);
551
552     if (recalc)
553     {
554       recalcConservation();
555     }
556   }
557
558   /**
559    * DOCUMENT ME!
560    * 
561    * @return DOCUMENT ME!
562    */
563   public int getStartRes()
564   {
565     return startRes;
566   }
567
568   /**
569    * DOCUMENT ME!
570    * 
571    * @return DOCUMENT ME!
572    */
573   public int getEndRes()
574   {
575     return endRes;
576   }
577
578   /**
579    * DOCUMENT ME!
580    * 
581    * @param i
582    *                DOCUMENT ME!
583    */
584   public void setStartRes(int i)
585   {
586     startRes = i;
587   }
588
589   /**
590    * DOCUMENT ME!
591    * 
592    * @param i
593    *                DOCUMENT ME!
594    */
595   public void setEndRes(int i)
596   {
597     endRes = i;
598   }
599
600   /**
601    * DOCUMENT ME!
602    * 
603    * @return DOCUMENT ME!
604    */
605   public int getSize()
606   {
607     return sequences.size();
608   }
609
610   /**
611    * DOCUMENT ME!
612    * 
613    * @param i
614    *                DOCUMENT ME!
615    * 
616    * @return DOCUMENT ME!
617    */
618   public SequenceI getSequenceAt(int i)
619   {
620     return (SequenceI) sequences.elementAt(i);
621   }
622
623   /**
624    * DOCUMENT ME!
625    * 
626    * @param state
627    *                DOCUMENT ME!
628    */
629   public void setColourText(boolean state)
630   {
631     colourText = state;
632   }
633
634   /**
635    * DOCUMENT ME!
636    * 
637    * @return DOCUMENT ME!
638    */
639   public boolean getColourText()
640   {
641     return colourText;
642   }
643
644   /**
645    * DOCUMENT ME!
646    * 
647    * @param state
648    *                DOCUMENT ME!
649    */
650   public void setDisplayText(boolean state)
651   {
652     displayText = state;
653   }
654
655   /**
656    * DOCUMENT ME!
657    * 
658    * @return DOCUMENT ME!
659    */
660   public boolean getDisplayText()
661   {
662     return displayText;
663   }
664
665   /**
666    * DOCUMENT ME!
667    * 
668    * @param state
669    *                DOCUMENT ME!
670    */
671   public void setDisplayBoxes(boolean state)
672   {
673     displayBoxes = state;
674   }
675
676   /**
677    * DOCUMENT ME!
678    * 
679    * @return DOCUMENT ME!
680    */
681   public boolean getDisplayBoxes()
682   {
683     return displayBoxes;
684   }
685
686   /**
687    * DOCUMENT ME!
688    * 
689    * @return DOCUMENT ME!
690    */
691   public int getWidth()
692   {
693     // MC This needs to get reset when characters are inserted and deleted
694     if (sequences.size() > 0)
695     {
696       width = ((SequenceI) sequences.elementAt(0)).getLength();
697     }
698
699     for (int i = 1; i < sequences.size(); i++)
700     {
701       SequenceI seq = (SequenceI) sequences.elementAt(i);
702
703       if (seq.getLength() > width)
704       {
705         width = seq.getLength();
706       }
707     }
708
709     return width;
710   }
711
712   /**
713    * DOCUMENT ME!
714    * 
715    * @param c
716    *                DOCUMENT ME!
717    */
718   public void setOutlineColour(Color c)
719   {
720     outlineColour = c;
721   }
722
723   /**
724    * DOCUMENT ME!
725    * 
726    * @return DOCUMENT ME!
727    */
728   public Color getOutlineColour()
729   {
730     return outlineColour;
731   }
732
733   /**
734    * 
735    * returns the sequences in the group ordered by the ordering given by al.
736    * this used to return an array with null entries regardless, new behaviour is below.
737    * TODO: verify that this does not affect use in applet or application
738    * @param al
739    *                Alignment
740    * @return SequenceI[] intersection of sequences in group with al, ordered by al, or null if group does not intersect with al
741    */
742   public SequenceI[] getSequencesInOrder(AlignmentI al)
743   {
744     int sSize = sequences.size();
745     int alHeight = al.getHeight();
746
747     SequenceI[] seqs = new SequenceI[sSize];
748
749     int index = 0;
750     for (int i = 0; i < alHeight && index < sSize; i++)
751     {
752       if (sequences.contains(al.getSequenceAt(i)))
753       {
754         seqs[index++] = al.getSequenceAt(i);
755       }
756     }
757     if (index==0)
758     {
759       return null;
760     }
761     if (index<seqs.length)
762     {
763       SequenceI[] dummy = seqs;
764       seqs = new SequenceI[index];
765       while (--index>=0)
766       {
767         seqs[index] = dummy[index];
768         dummy[index] = null;
769       }
770     }
771     return seqs;
772   }
773
774   /**
775    * @return the idColour
776    */
777   public Color getIdColour()
778   {
779     return idColour;
780   }
781
782   /**
783    * @param idColour
784    *                the idColour to set
785    */
786   public void setIdColour(Color idColour)
787   {
788     this.idColour = idColour;
789   }
790
791   /**
792    * @return the representative sequence for this group
793    */
794   public SequenceI getSeqrep()
795   {
796     return seqrep;
797   }
798
799   /**
800    * set the representative sequence for this group.
801    * Note - this affects the interpretation of the Hidereps attribute.
802    * @param seqrep the seqrep to set (null means no sequence representative)
803    */
804   public void setSeqrep(SequenceI seqrep)
805   {
806     this.seqrep = seqrep;
807   }
808   /**
809    * 
810    * @return true if group has a sequence representative
811    */
812   public boolean hasSeqrep()
813   {
814     return seqrep != null;
815   }
816   /**
817    * visibility of rows or represented rows covered by group
818    */
819   private boolean hidereps=false;
820   /**
821    * set visibility of sequences covered by (if no sequence representative is defined) 
822    * or represented by this group.
823    * @param visibility
824    */
825   public void setHidereps(boolean visibility)
826   {
827     hidereps = visibility;
828   }
829   /**
830    * 
831    * @return true if sequences represented (or covered) by this group should be hidden
832    */
833   public boolean isHidereps()
834   {
835     return hidereps;
836   }
837   /**
838    * visibility of columns intersecting this group
839    */
840   private boolean hidecols=false;
841   /**
842    * set intended visibility of columns covered by this group
843    * @param visibility
844    */
845   public void setHideCols(boolean visibility)
846   {
847     hidecols = visibility;
848   }
849   /**
850    * 
851    * @return true if columns covered by group should be hidden
852    */
853   public boolean isHideCols()
854   {
855     return hidecols;
856   }
857   /**
858    * create a new sequence group from the intersection of this group
859    * with an alignment Hashtable of hidden representatives
860    * 
861    * @param alignment (may not be null)
862    * @param hashtable (may be null)
863    * @return new group containing sequences common to this group and alignment
864    */
865   public SequenceGroup intersect(AlignmentI alignment, Hashtable hashtable)
866   {
867     SequenceGroup sgroup = new SequenceGroup(this);
868     SequenceI[] insect=getSequencesInOrder(alignment);
869     sgroup.sequences = new Vector();
870     for (int s=0;insect!=null && s<insect.length;s++) { 
871       if (hashtable==null || hashtable.containsKey(insect[s]))
872               {
873       sgroup.sequences.addElement(insect[s]); }
874     }
875     //Enumeration en =getSequences(hashtable).elements();
876     //while (en.hasMoreElements())
877    // {
878    //   SequenceI elem = (SequenceI) en.nextElement();
879    //   if (alignment.getSequences().contains(elem))
880    //   {
881    //     sgroup.addSequence(elem, false);
882    //   }
883    // }
884     return sgroup;
885   }
886
887   /**
888    * @return the showUnconserved
889    */
890   public boolean getShowunconserved()
891   {
892     return showUnconserved;
893   }
894
895   /**
896    * @param showUnconserved the showUnconserved to set
897    */
898   public void setShowunconserved(boolean displayNonconserved)
899   {
900     this.showUnconserved = displayNonconserved;
901   }
902   AlignmentAnnotation consensus=null,conservation=null;
903   /**
904    * 
905    * @return automatically calculated consensus row
906    */
907   public AlignmentAnnotation getConsensus()
908   {
909     // TODO get or calculate and get consensus annotation row for this group
910     int aWidth = this.getWidth();
911     // pointer
912     // possibility
913     // here.
914     if (aWidth < 0)
915     {
916       return null;
917     }
918     if (consensus==null)
919     {
920        consensus = new AlignmentAnnotation("Consensus for "+getName(), "PID",
921               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
922       consensus.hasText = true;
923       consensus.autoCalculated = true;
924       consensus.groupRef = this;
925     }
926     return consensus;
927   }
928     /**
929    * get the conservation annotation row for this group
930    * @return autoCalculated annotation row
931    */
932   public AlignmentAnnotation getConservationRow() {
933     int ConsPercGaps = 25;
934     if (conservation == null) {
935       conservation = new AlignmentAnnotation("Conservation",
936             "Conservation of total alignment less than " + ConsPercGaps
937                     + "% gaps", new Annotation[1], 0f, 11f,
938             AlignmentAnnotation.BAR_GRAPH);
939     conservation.hasText = true;
940     conservation.autoCalculated = true;
941     conservation.groupRef = this;
942       }
943     
944     return conservation;
945   }
946
947   /**
948    * 
949    * @return true if annotation rows have been instantiated for this group
950    */
951     public boolean hasAnnotationRows()
952     {
953       return consensus!=null || conservation!=null;
954     }
955
956   public SequenceI getConsensusSeq()
957   {
958     getConsensus();
959     StringBuffer seqs = new StringBuffer();
960     for (int i = 0; i < consensus.annotations.length; i++)
961     {
962       if (consensus.annotations[i] != null)
963       {
964         if (consensus.annotations[i].description.charAt(0) == '[')
965         {
966           seqs.append(consensus.annotations[i].description.charAt(1));
967         }
968         else
969         {
970           seqs.append(consensus.annotations[i].displayCharacter);
971         }
972       }
973     }
974
975     SequenceI sq = new Sequence("Group"+getName()+" Consensus", seqs.toString());
976     sq.setDescription("Percentage Identity Consensus "
977             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
978     return sq;    
979   }
980
981   public void setIgnoreGapsConsensus(boolean state)
982   {
983     if (this.ignoreGapsInConsensusCalculation!=state && consensus!=null)
984     {
985       ignoreGapsInConsensusCalculation = state;
986       recalcConservation();
987     }
988     ignoreGapsInConsensusCalculation = state;
989   }
990 }