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
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.
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.
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
19 package jalview.datamodel;
25 import jalview.analysis.*;
26 import jalview.schemes.*;
29 * Collects a set contiguous ranges on a set of sequences
34 public class SequenceGroup
40 Conservation conserve;
44 boolean displayBoxes = true;
46 boolean displayText = true;
48 boolean colourText = false;
50 * after Olivier's non-conserved only character display
52 boolean showUnconserved = false;
57 private Vector sequences = new Vector();
59 * representative sequence for this group (if any)
61 private SequenceI seqrep = null;
65 * Colourscheme applied to group if any */
66 public ColourSchemeI cs;
72 public Color outlineColour = Color.black;
74 public Color idColour = null;
76 public int thresholdTextColour = 0;
78 public Color textColour = Color.black;
80 public Color textColour2 = Color.white;
83 * consensus calculation property
85 private boolean ignoreGapsInConsensus=true;
87 * consensus calculation property
89 private boolean showSequenceLogo=false;
92 * @return the includeAllConsSymbols
94 public boolean isShowSequenceLogo()
96 return showSequenceLogo;
101 * Creates a new SequenceGroup object.
103 public SequenceGroup()
105 groupName = "JGroup:" + this.hashCode();
109 * Creates a new SequenceGroup object.
114 * @param displayBoxes
118 * first column of group
120 * last column of group
122 public SequenceGroup(Vector sequences, String groupName,
123 ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
124 boolean colourText, int start, int end)
126 this.sequences = sequences;
127 this.groupName = groupName;
128 this.displayBoxes = displayBoxes;
129 this.displayText = displayText;
130 this.colourText = colourText;
134 recalcConservation();
140 public SequenceGroup(SequenceGroup seqsel)
144 sequences=new Vector();
145 Enumeration sq = seqsel.sequences.elements();
146 while (sq.hasMoreElements()) {
147 sequences.addElement(sq.nextElement());
149 if (seqsel.groupName!=null)
151 groupName = new String(seqsel.groupName);
153 displayBoxes = seqsel.displayBoxes;
154 displayText = seqsel.displayText;
155 colourText = seqsel.colourText;
156 startRes = seqsel.startRes;
157 endRes = seqsel.endRes;
159 if (seqsel.description!=null)
160 description = new String(seqsel.description);
161 hidecols = seqsel.hidecols;
162 hidereps = seqsel.hidereps;
163 idColour = seqsel.idColour;
164 outlineColour = seqsel.outlineColour;
165 seqrep = seqsel.seqrep;
166 textColour = seqsel.textColour;
167 textColour2 = seqsel.textColour2;
168 thresholdTextColour = seqsel.thresholdTextColour;
169 width = seqsel.width;
170 ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
171 if (seqsel.conserve!=null)
173 recalcConservation(); // safer than
174 // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
179 public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
181 int iSize = sequences.size();
182 SequenceI[] seqs = new SequenceI[iSize];
183 SequenceI[] inorder = getSequencesInOrder(align);
185 for (int i = 0, ipos = 0; i < inorder.length; i++)
187 SequenceI seq = inorder[i];
189 seqs[ipos] = seq.getSubSequence(startRes, endRes + 1);
190 if (seqs[ipos] != null)
192 seqs[ipos].setDescription(seq.getDescription());
193 seqs[ipos].setDBRef(seq.getDBRef());
194 seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
195 if (seq.getDatasetSequence() != null)
197 seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
200 if (seq.getAnnotation() != null)
202 AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
203 // Only copy annotation that is either a score or referenced by the
204 // alignment's annotation vector
205 for (int a = 0; a < seq.getAnnotation().length; a++)
207 AlignmentAnnotation tocopy = seq.getAnnotation()[a];
210 boolean found = false;
211 for (int pos = 0; pos < alann.length; pos++)
213 if (alann[pos] == tocopy)
222 AlignmentAnnotation newannot = new AlignmentAnnotation(seq
223 .getAnnotation()[a]);
224 newannot.restrict(startRes, endRes);
225 newannot.setSequenceRef(seqs[ipos]);
226 newannot.adjustForAlignment();
227 seqs[ipos].addAlignmentAnnotation(newannot);
237 if (iSize != inorder.length)
239 SequenceI[] nseqs = new SequenceI[iSize];
240 System.arraycopy(seqs, 0, nseqs, 0, iSize);
248 * If sequence ends in gaps, the end residue can be correctly calculated here
254 public int findEndRes(SequenceI seq)
259 for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
261 ch = seq.getCharAt(j);
262 if (!jalview.util.Comparison.isGap((ch)))
270 eres += seq.getStart() - 1;
276 public Vector getSequences(Hashtable hiddenReps)
278 if (hiddenReps == null)
284 Vector allSequences = new Vector();
286 for (int i = 0; i < sequences.size(); i++)
288 seq = (SequenceI) sequences.elementAt(i);
289 allSequences.addElement(seq);
290 if (hiddenReps.containsKey(seq))
292 SequenceGroup hsg = (SequenceGroup) hiddenReps.get(seq);
293 for (int h = 0; h < hsg.getSize(); h++)
295 seq2 = hsg.getSequenceAt(h);
296 if (seq2 != seq && !allSequences.contains(seq2))
298 allSequences.addElement(seq2);
308 public SequenceI[] getSequencesAsArray(Hashtable hiddenReps)
310 Vector tmp = getSequences(hiddenReps);
315 SequenceI[] result = new SequenceI[tmp.size()];
316 for (int i = 0; i < result.length; i++)
318 result[i] = (SequenceI) tmp.elementAt(i);
330 * @return DOCUMENT ME!
332 public boolean adjustForRemoveLeft(int col)
334 // return value is true if the group still exists
337 startRes = startRes - col;
342 endRes = endRes - col;
344 if (startRes > endRes)
351 // must delete this group!!
364 * @return DOCUMENT ME!
366 public boolean adjustForRemoveRight(int col)
385 * @return DOCUMENT ME!
387 public String getName()
392 public String getDescription()
403 public void setName(String name)
406 // TODO: URGENT: update dependent objects (annotation row)
409 public void setDescription(String desc)
417 * @return DOCUMENT ME!
419 public Conservation getConservation()
430 public void setConservation(Conservation c)
436 * Add s to this sequence group
439 * alignment sequence to be added
441 * true means Group's conservation should be recalculated
443 public void addSequence(SequenceI s, boolean recalc)
445 if (s != null && !sequences.contains(s))
447 sequences.addElement(s);
452 recalcConservation();
457 * Max Gaps Threshold for performing a conservation calculation
458 * TODO: make this a configurable property - or global to an alignment view
460 private int consPercGaps=25;
462 * calculate residue conservation for group - but only if necessary.
464 public void recalcConservation()
466 if (cs == null && consensus == null && conservation == null)
473 Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes, endRes + 1, showSequenceLogo);
474 if (consensus != null)
476 _updateConsensusRow(cnsns);
480 cs.setConsensus(cnsns);
482 if (cs instanceof ClustalxColourScheme)
484 ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
488 if ((conservation!=null) || (cs!=null && cs.conservationApplied()))
490 Conservation c = new Conservation(groupName,
491 ResidueProperties.propHash, 3, sequences, startRes,
494 c.verdict(false, consPercGaps);
495 if (conservation!=null)
497 _updateConservationRow(c);
501 cs.setConservation(c);
503 if (cs instanceof ClustalxColourScheme)
505 ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
509 } catch (java.lang.OutOfMemoryError err)
512 System.out.println("Out of memory loading groups: " + err);
517 private void _updateConservationRow(Conservation c)
519 if (conservation==null)
524 conservation.label = "Conservation for "+getName();
525 conservation.description = "Conservation for group "+getName()+" less than " + consPercGaps
527 // preserve width if already set
528 int aWidth = (conservation.annotations!=null) ? (endRes<conservation.annotations.length ? conservation.annotations.length : endRes+1) : endRes+1;
529 conservation.annotations = null;
530 conservation.annotations = new Annotation[aWidth]; // should be alignment width
531 c.completeAnnotations(conservation,null, startRes, endRes+1);
533 public Hashtable[] consensusData = null;
534 private void _updateConsensusRow(Hashtable[] cnsns)
540 consensus.label = "Consensus for "+getName();
541 consensus.description = "Percent Identity";
542 consensusData = cnsns;
543 // preserve width if already set
544 int aWidth = (consensus.annotations!=null) ? (endRes<consensus.annotations.length ? consensus.annotations.length : endRes+1) : endRes+1;
545 consensus.annotations = null;
546 consensus.annotations = new Annotation[aWidth]; // should be alignment width
548 AAFrequency.completeConsensus(consensus,cnsns,startRes,endRes+1,ignoreGapsInConsensus, showSequenceLogo); // TODO: setting container for ignoreGapsInConsensusCalculation);
552 * @param s sequence to either add or remove from group
553 * @param recalc flag passed to delete/addSequence to indicate if group properties should be recalculated
555 public void addOrRemove(SequenceI s, boolean recalc)
557 if (sequences.contains(s))
559 deleteSequence(s, recalc);
563 addSequence(s, recalc);
575 public void deleteSequence(SequenceI s, boolean recalc)
577 sequences.removeElement(s);
581 recalcConservation();
588 * @return DOCUMENT ME!
590 public int getStartRes()
598 * @return DOCUMENT ME!
600 public int getEndRes()
611 public void setStartRes(int i)
622 public void setEndRes(int i)
630 * @return DOCUMENT ME!
634 return sequences.size();
643 * @return DOCUMENT ME!
645 public SequenceI getSequenceAt(int i)
647 return (SequenceI) sequences.elementAt(i);
656 public void setColourText(boolean state)
664 * @return DOCUMENT ME!
666 public boolean getColourText()
677 public void setDisplayText(boolean state)
685 * @return DOCUMENT ME!
687 public boolean getDisplayText()
698 public void setDisplayBoxes(boolean state)
700 displayBoxes = state;
706 * @return DOCUMENT ME!
708 public boolean getDisplayBoxes()
716 * @return DOCUMENT ME!
718 public int getWidth()
720 // MC This needs to get reset when characters are inserted and deleted
721 if (sequences.size() > 0)
723 width = ((SequenceI) sequences.elementAt(0)).getLength();
726 for (int i = 1; i < sequences.size(); i++)
728 SequenceI seq = (SequenceI) sequences.elementAt(i);
730 if (seq.getLength() > width)
732 width = seq.getLength();
745 public void setOutlineColour(Color c)
753 * @return DOCUMENT ME!
755 public Color getOutlineColour()
757 return outlineColour;
762 * returns the sequences in the group ordered by the ordering given by al.
763 * this used to return an array with null entries regardless, new behaviour is below.
764 * TODO: verify that this does not affect use in applet or application
767 * @return SequenceI[] intersection of sequences in group with al, ordered by al, or null if group does not intersect with al
769 public SequenceI[] getSequencesInOrder(AlignmentI al)
771 int sSize = sequences.size();
772 int alHeight = al.getHeight();
774 SequenceI[] seqs = new SequenceI[sSize];
777 for (int i = 0; i < alHeight && index < sSize; i++)
779 if (sequences.contains(al.getSequenceAt(i)))
781 seqs[index++] = al.getSequenceAt(i);
788 if (index<seqs.length)
790 SequenceI[] dummy = seqs;
791 seqs = new SequenceI[index];
794 seqs[index] = dummy[index];
802 * @return the idColour
804 public Color getIdColour()
811 * the idColour to set
813 public void setIdColour(Color idColour)
815 this.idColour = idColour;
819 * @return the representative sequence for this group
821 public SequenceI getSeqrep()
827 * set the representative sequence for this group.
828 * Note - this affects the interpretation of the Hidereps attribute.
829 * @param seqrep the seqrep to set (null means no sequence representative)
831 public void setSeqrep(SequenceI seqrep)
833 this.seqrep = seqrep;
837 * @return true if group has a sequence representative
839 public boolean hasSeqrep()
841 return seqrep != null;
844 * visibility of rows or represented rows covered by group
846 private boolean hidereps=false;
848 * set visibility of sequences covered by (if no sequence representative is defined)
849 * or represented by this group.
852 public void setHidereps(boolean visibility)
854 hidereps = visibility;
858 * @return true if sequences represented (or covered) by this group should be hidden
860 public boolean isHidereps()
865 * visibility of columns intersecting this group
867 private boolean hidecols=false;
869 * set intended visibility of columns covered by this group
872 public void setHideCols(boolean visibility)
874 hidecols = visibility;
878 * @return true if columns covered by group should be hidden
880 public boolean isHideCols()
885 * create a new sequence group from the intersection of this group
886 * with an alignment Hashtable of hidden representatives
888 * @param alignment (may not be null)
889 * @param hashtable (may be null)
890 * @return new group containing sequences common to this group and alignment
892 public SequenceGroup intersect(AlignmentI alignment, Hashtable hashtable)
894 SequenceGroup sgroup = new SequenceGroup(this);
895 SequenceI[] insect=getSequencesInOrder(alignment);
896 sgroup.sequences = new Vector();
897 for (int s=0;insect!=null && s<insect.length;s++) {
898 if (hashtable==null || hashtable.containsKey(insect[s]))
900 sgroup.sequences.addElement(insect[s]); }
902 //Enumeration en =getSequences(hashtable).elements();
903 //while (en.hasMoreElements())
905 // SequenceI elem = (SequenceI) en.nextElement();
906 // if (alignment.getSequences().contains(elem))
908 // sgroup.addSequence(elem, false);
915 * @return the showUnconserved
917 public boolean getShowunconserved()
919 return showUnconserved;
923 * @param showUnconserved the showUnconserved to set
925 public void setShowunconserved(boolean displayNonconserved)
927 this.showUnconserved = displayNonconserved;
929 AlignmentAnnotation consensus=null,conservation=null;
932 * flag indicating if consensus histogram should be rendered
934 private boolean showConsensusHistogram;
939 * @return automatically calculated consensus row
941 public AlignmentAnnotation getConsensus()
943 // TODO get or calculate and get consensus annotation row for this group
944 int aWidth = this.getWidth();
954 consensus = new AlignmentAnnotation("","",
955 new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
956 consensus.hasText = true;
957 consensus.autoCalculated = true;
958 consensus.groupRef = this;
960 consensus.label = "Consensus for "+getName();
961 consensus.description = "Percent Identity";
965 * get the conservation annotation row for this group
966 * @return autoCalculated annotation row
968 public AlignmentAnnotation getConservationRow() {
969 if (conservation == null) {
970 conservation = new AlignmentAnnotation("","", new Annotation[1], 0f, 11f,
971 AlignmentAnnotation.BAR_GRAPH);
972 conservation.hasText = true;
973 conservation.autoCalculated = true;
974 conservation.groupRef = this;
976 conservation.label = "Conservation for "+getName();
977 conservation.description = "Conservation for group "+getName()+" less than " + consPercGaps
984 * @return true if annotation rows have been instantiated for this group
986 public boolean hasAnnotationRows()
988 return consensus!=null || conservation!=null;
991 public SequenceI getConsensusSeq()
994 StringBuffer seqs = new StringBuffer();
995 for (int i = 0; i < consensus.annotations.length; i++)
997 if (consensus.annotations[i] != null)
999 if (consensus.annotations[i].description.charAt(0) == '[')
1001 seqs.append(consensus.annotations[i].description.charAt(1));
1005 seqs.append(consensus.annotations[i].displayCharacter);
1010 SequenceI sq = new Sequence("Group"+getName()+" Consensus", seqs.toString());
1011 sq.setDescription("Percentage Identity Consensus "
1012 + ((ignoreGapsInConsensus) ? " without gaps" : ""));
1016 public void setIgnoreGapsConsensus(boolean state)
1018 if (this.ignoreGapsInConsensus!=state && consensus!=null)
1020 ignoreGapsInConsensus = state;
1021 recalcConservation();
1023 ignoreGapsInConsensus = state;
1025 public boolean getIgnoreGapsConsensus()
1027 return ignoreGapsInConsensus;
1030 * @param includeAllConsSymbols the includeAllConsSymbols to set
1032 public void setIncludeAllConsSymbols(boolean includeAllConsSymbols)
1034 if (this.showSequenceLogo!=includeAllConsSymbols && consensus!=null) {
1035 this.showSequenceLogo = includeAllConsSymbols;
1036 recalcConservation();
1038 this.showSequenceLogo = includeAllConsSymbols;
1044 * @param showConsHist flag indicating if the consensus histogram for this group should be rendered
1046 public void setShowConsensusHistogram(boolean showConsHist)
1049 if (showConsensusHistogram!=showConsHist && consensus!=null) {
1050 this.showConsensusHistogram = showConsHist;
1051 recalcConservation();
1053 this.showConsensusHistogram = showConsHist;
1056 * @return the showConsensusHistogram
1058 public boolean isShowConsensusHistogram()
1060 return showConsensusHistogram;