X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=inline;f=src%2Fjalview%2Fdatamodel%2FSequenceGroup.java;h=4f9d530fb317cc9ca16adc8319dbd5650f3b3491;hb=ff8c06845590fd9fd423aa59809dcce9610ab295;hp=65dc13f9e9860703640f676f5328d13771ad1df1;hpb=45043731e74856fbf6f181e508b8d79316048ce2;p=jalview.git
diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java
index 65dc13f..4f9d530 100755
--- a/src/jalview/datamodel/SequenceGroup.java
+++ b/src/jalview/datamodel/SequenceGroup.java
@@ -1,29 +1,35 @@
/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
- * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along with Jalview. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see .
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.datamodel;
-import java.util.*;
-import java.util.List;
-
-import java.awt.*;
+import jalview.analysis.AAFrequency;
+import jalview.analysis.Conservation;
+import jalview.renderer.ResidueShader;
+import jalview.renderer.ResidueShaderI;
+import jalview.schemes.ColourSchemeI;
-import jalview.analysis.*;
-import jalview.schemes.*;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
/**
* Collects a set contiguous ranges on a set of sequences
@@ -39,8 +45,6 @@ public class SequenceGroup implements AnnotatedCollectionI
Conservation conserve;
- Vector aaFrequency;
-
boolean displayBoxes = true;
boolean displayText = true;
@@ -55,7 +59,7 @@ public class SequenceGroup implements AnnotatedCollectionI
/**
* group members
*/
- private Vector sequences = new Vector();
+ private List sequences = new ArrayList();
/**
* representative sequence for this group (if any)
@@ -67,10 +71,12 @@ public class SequenceGroup implements AnnotatedCollectionI
/**
* Colourscheme applied to group if any
*/
- public ColourSchemeI cs;
+ public ResidueShaderI cs;
+ // start column (base 0)
int startRes = 0;
+ // end column (base 0)
int endRes = 0;
public Color outlineColour = Color.black;
@@ -112,6 +118,7 @@ public class SequenceGroup implements AnnotatedCollectionI
public SequenceGroup()
{
groupName = "JGroup:" + this.hashCode();
+ cs = new ResidueShader();
}
/**
@@ -128,16 +135,17 @@ public class SequenceGroup implements AnnotatedCollectionI
* @param end
* last column of group
*/
- public SequenceGroup(Vector sequences, String groupName,
+ public SequenceGroup(List sequences, String groupName,
ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
boolean colourText, int start, int end)
{
+ this();
this.sequences = sequences;
this.groupName = groupName;
this.displayBoxes = displayBoxes;
this.displayText = displayText;
this.colourText = colourText;
- this.cs = scheme;
+ this.cs = new ResidueShader(scheme);
startRes = start;
endRes = end;
recalcConservation();
@@ -150,15 +158,11 @@ public class SequenceGroup implements AnnotatedCollectionI
*/
public SequenceGroup(SequenceGroup seqsel)
{
+ this();
if (seqsel != null)
{
- sequences = new Vector();
- Enumeration sq = seqsel.sequences.elements();
- while (sq.hasMoreElements())
- {
- sequences.addElement(sq.nextElement());
- }
- ;
+ sequences = new ArrayList();
+ sequences.addAll(seqsel.sequences);
if (seqsel.groupName != null)
{
groupName = new String(seqsel.groupName);
@@ -170,7 +174,9 @@ public class SequenceGroup implements AnnotatedCollectionI
endRes = seqsel.endRes;
cs = seqsel.cs;
if (seqsel.description != null)
+ {
description = new String(seqsel.description);
+ }
hidecols = seqsel.hidecols;
hidereps = seqsel.hidereps;
idColour = seqsel.idColour;
@@ -203,7 +209,7 @@ public class SequenceGroup implements AnnotatedCollectionI
if (seqs[ipos] != null)
{
seqs[ipos].setDescription(seq.getDescription());
- seqs[ipos].setDBRef(seq.getDBRef());
+ seqs[ipos].setDBRefs(seq.getDBRefs());
seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
if (seq.getDatasetSequence() != null)
{
@@ -230,7 +236,9 @@ public class SequenceGroup implements AnnotatedCollectionI
}
}
if (!found)
+ {
continue;
+ }
}
AlignmentAnnotation newannot = new AlignmentAnnotation(
seq.getAnnotation()[a]);
@@ -286,26 +294,27 @@ public class SequenceGroup implements AnnotatedCollectionI
return eres;
}
+ @Override
public List getSequences()
{
return sequences;
}
+ @Override
public List getSequences(
Map hiddenReps)
{
if (hiddenReps == null)
{
+ // TODO: need a synchronizedCollection here ?
return sequences;
}
else
{
- Vector allSequences = new Vector();
- SequenceI seq;
- for (int i = 0; i < sequences.size(); i++)
+ List allSequences = new ArrayList();
+ for (SequenceI seq : sequences)
{
- seq = (SequenceI) sequences.elementAt(i);
- allSequences.addElement(seq);
+ allSequences.add(seq);
if (hiddenReps.containsKey(seq))
{
SequenceCollectionI hsg = hiddenReps.get(seq);
@@ -313,7 +322,7 @@ public class SequenceGroup implements AnnotatedCollectionI
{
if (seq2 != seq && !allSequences.contains(seq2))
{
- allSequences.addElement(seq2);
+ allSequences.add(seq2);
}
}
}
@@ -457,55 +466,89 @@ public class SequenceGroup implements AnnotatedCollectionI
*/
public void addSequence(SequenceI s, boolean recalc)
{
- if (s != null && !sequences.contains(s))
+ synchronized (sequences)
{
- sequences.addElement(s);
- }
+ if (s != null && !sequences.contains(s))
+ {
+ sequences.add(s);
+ }
- if (recalc)
- {
- recalcConservation();
+ if (recalc)
+ {
+ recalcConservation();
+ }
}
}
/**
- * Max Gaps Threshold for performing a conservation calculation TODO: make
- * this a configurable property - or global to an alignment view
+ * Max Gaps Threshold (percent) for performing a conservation calculation
*/
private int consPercGaps = 25;
/**
- * calculate residue conservation for group - but only if necessary.
+ * @return Max Gaps Threshold for performing a conservation calculation
+ */
+ public int getConsPercGaps()
+ {
+ return consPercGaps;
+ }
+
+ /**
+ * set Max Gaps Threshold (percent) for performing a conservation calculation
+ *
+ * @param consPercGaps
+ */
+ public void setConsPercGaps(int consPercGaps)
+ {
+ this.consPercGaps = consPercGaps;
+ }
+
+ /**
+ * calculate residue conservation and colourschemes for group - but only if
+ * necessary. returns true if the calculation resulted in a visible change to
+ * group
+ */
+ public boolean recalcConservation()
+ {
+ return recalcConservation(false);
+ }
+
+ /**
+ * calculate residue conservation for group - but only if necessary. returns
+ * true if the calculation resulted in a visible change to group
+ *
+ * @param defer
+ * when set, colourschemes for this group are not refreshed after
+ * recalculation
*/
- public void recalcConservation()
+ public boolean recalcConservation(boolean defer)
{
if (cs == null && consensus == null && conservation == null)
{
- return;
- }
- if (cs != null)
- {
- cs.alignmentChanged(this, null);
+ return false;
}
+ // TODO: try harder to detect changes in state in order to minimise
+ // recalculation effort
+ boolean upd = false;
try
{
- Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes,
+ ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
endRes + 1, showSequenceLogo);
if (consensus != null)
{
- _updateConsensusRow(cnsns);
+ _updateConsensusRow(cnsns, sequences.size());
+ upd = true;
}
if (cs != null)
{
cs.setConsensus(cnsns);
- cs.alignmentChanged(this, null);
+ upd = true;
}
if ((conservation != null)
|| (cs != null && cs.conservationApplied()))
{
- Conservation c = new Conservation(groupName,
- ResidueProperties.propHash, 3, sequences, startRes,
+ Conservation c = new Conservation(groupName, sequences, startRes,
endRes + 1);
c.calculate();
c.verdict(false, consPercGaps);
@@ -518,16 +561,27 @@ public class SequenceGroup implements AnnotatedCollectionI
if (cs.conservationApplied())
{
cs.setConservation(c);
- cs.alignmentChanged(this, null);
}
}
+ // eager update - will cause a refresh of overview regardless
+ upd = true;
+ }
+ if (cs != null && !defer)
+ {
+ // TODO: JAL-2034 should cs.alignmentChanged modify return state
+ cs.alignmentChanged(context != null ? context : this, null);
+ return true;
+ }
+ else
+ {
+ return upd;
}
} catch (java.lang.OutOfMemoryError err)
{
// TODO: catch OOM
System.out.println("Out of memory loading groups: " + err);
}
-
+ return upd;
}
private void _updateConservationRow(Conservation c)
@@ -550,9 +604,9 @@ public class SequenceGroup implements AnnotatedCollectionI
c.completeAnnotations(conservation, null, startRes, endRes + 1);
}
- public Hashtable[] consensusData = null;
+ public ProfilesI consensusData = null;
- private void _updateConsensusRow(Hashtable[] cnsns)
+ private void _updateConsensusRow(ProfilesI cnsns, long nseq)
{
if (consensus == null)
{
@@ -569,9 +623,10 @@ public class SequenceGroup implements AnnotatedCollectionI
consensus.annotations = new Annotation[aWidth]; // should be alignment width
AAFrequency.completeConsensus(consensus, cnsns, startRes, endRes + 1,
- ignoreGapsInConsensus, showSequenceLogo); // TODO: setting container
- // for
- // ignoreGapsInConsensusCalculation);
+ ignoreGapsInConsensus, showSequenceLogo, nseq); // TODO: setting
+ // container
+ // for
+ // ignoreGapsInConsensusCalculation);
}
/**
@@ -583,49 +638,56 @@ public class SequenceGroup implements AnnotatedCollectionI
*/
public void addOrRemove(SequenceI s, boolean recalc)
{
- if (sequences.contains(s))
- {
- deleteSequence(s, recalc);
- }
- else
+ synchronized (sequences)
{
- addSequence(s, recalc);
+ if (sequences.contains(s))
+ {
+ deleteSequence(s, recalc);
+ }
+ else
+ {
+ addSequence(s, recalc);
+ }
}
}
/**
- * DOCUMENT ME!
+ * remove
*
* @param s
- * DOCUMENT ME!
+ * to be removed
* @param recalc
- * DOCUMENT ME!
+ * true means recalculate conservation
*/
public void deleteSequence(SequenceI s, boolean recalc)
{
- sequences.removeElement(s);
-
- if (recalc)
+ synchronized (sequences)
{
- recalcConservation();
+ sequences.remove(s);
+
+ if (recalc)
+ {
+ recalcConservation();
+ }
}
}
/**
- * DOCUMENT ME!
*
- * @return DOCUMENT ME!
+ *
+ * @return the first column selected by this group. Runs from 0<=i 0)
- {
- width = ((SequenceI) sequences.elementAt(0)).getLength();
- }
-
- for (int i = 1; i < sequences.size(); i++)
+ synchronized (sequences)
{
- SequenceI seq = (SequenceI) sequences.elementAt(i);
-
- if (seq.getLength() > width)
+ // MC This needs to get reset when characters are inserted and deleted
+ boolean first = true;
+ for (SequenceI seq : sequences)
{
- width = seq.getLength();
+ if (first || seq.getLength() > width)
+ {
+ width = seq.getLength();
+ first = false;
+ }
}
+ return width;
}
-
- return width;
}
/**
@@ -811,39 +863,42 @@ public class SequenceGroup implements AnnotatedCollectionI
*/
public SequenceI[] getSequencesInOrder(AlignmentI al, boolean trim)
{
- int sSize = sequences.size();
- int alHeight = al.getHeight();
+ synchronized (sequences)
+ {
+ int sSize = sequences.size();
+ int alHeight = al.getHeight();
- SequenceI[] seqs = new SequenceI[(trim) ? sSize : alHeight];
+ SequenceI[] seqs = new SequenceI[(trim) ? sSize : alHeight];
- int index = 0;
- for (int i = 0; i < alHeight && index < sSize; i++)
- {
- if (sequences.contains(al.getSequenceAt(i)))
+ int index = 0;
+ for (int i = 0; i < alHeight && index < sSize; i++)
{
- seqs[(trim) ? index : i] = al.getSequenceAt(i);
- index++;
+ if (sequences.contains(al.getSequenceAt(i)))
+ {
+ seqs[(trim) ? index : i] = al.getSequenceAt(i);
+ index++;
+ }
}
- }
- if (index == 0)
- {
- return null;
- }
- if (!trim)
- {
- return seqs;
- }
- if (index < seqs.length)
- {
- SequenceI[] dummy = seqs;
- seqs = new SequenceI[index];
- while (--index >= 0)
+ if (index == 0)
+ {
+ return null;
+ }
+ if (!trim)
+ {
+ return seqs;
+ }
+ if (index < seqs.length)
{
- seqs[index] = dummy[index];
- dummy[index] = null;
+ SequenceI[] dummy = seqs;
+ seqs = new SequenceI[index];
+ while (--index >= 0)
+ {
+ seqs[index] = dummy[index];
+ dummy[index] = null;
+ }
}
+ return seqs;
}
- return seqs;
}
/**
@@ -866,6 +921,7 @@ public class SequenceGroup implements AnnotatedCollectionI
/**
* @return the representative sequence for this group
*/
+ @Override
public SequenceI getSeqrep()
{
return seqrep;
@@ -878,6 +934,7 @@ public class SequenceGroup implements AnnotatedCollectionI
* @param seqrep
* the seqrep to set (null means no sequence representative)
*/
+ @Override
public void setSeqrep(SequenceI seqrep)
{
this.seqrep = seqrep;
@@ -887,6 +944,7 @@ public class SequenceGroup implements AnnotatedCollectionI
*
* @return true if group has a sequence representative
*/
+ @Override
public boolean hasSeqrep()
{
return seqrep != null;
@@ -957,23 +1015,14 @@ public class SequenceGroup implements AnnotatedCollectionI
{
SequenceGroup sgroup = new SequenceGroup(this);
SequenceI[] insect = getSequencesInOrder(alignment);
- sgroup.sequences = new Vector();
+ sgroup.sequences = new ArrayList();
for (int s = 0; insect != null && s < insect.length; s++)
{
if (map == null || map.containsKey(insect[s]))
{
- sgroup.sequences.addElement(insect[s]);
+ sgroup.sequences.add(insect[s]);
}
}
- // Enumeration en =getSequences(hashtable).elements();
- // while (en.hasMoreElements())
- // {
- // SequenceI elem = (SequenceI) en.nextElement();
- // if (alignment.getSequences().contains(elem))
- // {
- // sgroup.addSequence(elem, false);
- // }
- // }
return sgroup;
}
@@ -1017,7 +1066,8 @@ public class SequenceGroup implements AnnotatedCollectionI
/**
*
- * @return automatically calculated consensus row
+ * @return automatically calculated consensus row note: the row is a stub if a
+ * consensus calculation has not yet been performed on the group
*/
public AlignmentAnnotation getConsensus()
{
@@ -1193,43 +1243,172 @@ public class SequenceGroup implements AnnotatedCollectionI
// TODO add in other methods like 'getAlignmentAnnotation(String label),
// etc'
ArrayList annot = new ArrayList();
- for (SequenceI seq : (Vector) sequences)
+ synchronized (sequences)
{
- for (AlignmentAnnotation al : seq.getAnnotation())
+ for (SequenceI seq : sequences)
{
- if (al.groupRef == this)
+ AlignmentAnnotation[] aa = seq.getAnnotation();
+ if (aa != null)
{
- annot.add(al);
+ for (AlignmentAnnotation al : aa)
+ {
+ if (al.groupRef == this)
+ {
+ annot.add(al);
+ }
+ }
}
}
+ if (consensus != null)
+ {
+ annot.add(consensus);
+ }
+ if (conservation != null)
+ {
+ annot.add(conservation);
+ }
}
- if (consensus != null)
+ return annot.toArray(new AlignmentAnnotation[0]);
+ }
+
+ @Override
+ public Iterable findAnnotation(String calcId)
+ {
+ List aa = new ArrayList();
+ if (calcId == null)
{
- annot.add(consensus);
+ return aa;
}
- if (conservation != null)
+ for (AlignmentAnnotation a : getAlignmentAnnotation())
{
- annot.add(conservation);
+ if (calcId.equals(a.getCalcId()))
+ {
+ aa.add(a);
+ }
}
- return annot.toArray(new AlignmentAnnotation[0]);
+ return aa;
}
+ /**
+ * Returns a list of annotations that match the specified sequenceRef, calcId
+ * and label, ignoring null values.
+ *
+ * @return list of AlignmentAnnotation objects
+ */
@Override
- public Iterable findAnnotation(String calcId)
+ public Iterable findAnnotations(SequenceI seq,
+ String calcId, String label)
{
ArrayList aa = new ArrayList();
- for (AlignmentAnnotation a : getAlignmentAnnotation())
+ for (AlignmentAnnotation ann : getAlignmentAnnotation())
{
- if (a.getCalcId() == calcId)
+ if (ann.getCalcId() != null && ann.getCalcId().equals(calcId)
+ && ann.sequenceRef != null && ann.sequenceRef == seq
+ && ann.label != null && ann.label.equals(label))
{
- aa.add(a);
+ aa.add(ann);
}
}
return aa;
}
+ /**
+ * Answer true if any annotation matches the calcId passed in (if not null).
+ *
+ * @param calcId
+ * @return
+ */
+ public boolean hasAnnotation(String calcId)
+ {
+ if (calcId != null && !"".equals(calcId))
+ {
+ for (AlignmentAnnotation a : getAlignmentAnnotation())
+ {
+ if (a.getCalcId() == calcId)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove all sequences from the group (leaving other properties unchanged).
+ */
public void clear()
{
- sequences.clear();
+ synchronized (sequences)
+ {
+ sequences.clear();
+ }
+ }
+
+ private AnnotatedCollectionI context;
+
+ /**
+ * Sets the alignment or group context for this group
+ *
+ * @param ctx
+ * @throws IllegalArgumentException
+ * if setting the context would result in a circular reference chain
+ */
+ public void setContext(AnnotatedCollectionI ctx)
+ {
+ AnnotatedCollectionI ref = ctx;
+ while (ref != null)
+ {
+ if (ref == this)
+ {
+ throw new IllegalArgumentException(
+ "Circular reference in SequenceGroup.context");
+ }
+ ref = ref.getContext();
+ }
+ this.context = ctx;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see jalview.datamodel.AnnotatedCollectionI#getContext()
+ */
+ @Override
+ public AnnotatedCollectionI getContext()
+ {
+ return context;
+ }
+
+ public void setColourScheme(ColourSchemeI scheme)
+ {
+ if (cs == null)
+ {
+ cs = new ResidueShader();
+ }
+ cs.setColourScheme(scheme);
+ }
+
+ public void setGroupColourScheme(ResidueShaderI scheme)
+ {
+ cs = scheme;
+ }
+
+ public ColourSchemeI getColourScheme()
+ {
+ return cs == null ? null : cs.getColourScheme();
+ }
+
+ public ResidueShaderI getGroupColourScheme()
+ {
+ return cs;
+ }
+
+ @Override
+ public boolean isNucleotide()
+ {
+ if (context != null) {
+ return context.isNucleotide();
+ }
+ return false;
}
}