From: gmungoc Date: Wed, 19 Apr 2017 08:09:46 +0000 (+0100) Subject: JAL-2483 use SequenceFeatures to look up groups, types, feature lengths X-Git-Tag: Release_2_10_3b1~311 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=661f8bc80f969f31d7aeceb9af7d01374c8e1f35;p=jalview.git JAL-2483 use SequenceFeatures to look up groups, types, feature lengths --- diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index b93cae0..49caa4c 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -429,7 +429,8 @@ public class Sequence extends ASequence implements SequenceI @Override public SequenceFeaturesI getFeatures() { - return sequenceFeatureStore; + return datasetSequence != null ? datasetSequence.getFeatures() + : sequenceFeatureStore; } @Override diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index 4cbf1d9..2f40193 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -110,6 +110,26 @@ public class SequenceFeatures implements SequenceFeaturesI * {@inheritDoc} */ @Override + public int getTotalFeatureLength(String... type) + { + int result = 0; + + for (String featureType : varargToTypes(type)) + { + FeatureStore featureSet = featureStore.get(featureType); + if (featureSet != null) + { + result += featureSet.getTotalFeatureLength(); + } + } + return result; + + } + + /** + * {@inheritDoc} + */ + @Override public List getPositionalFeatures(String... type) { List result = new ArrayList(); diff --git a/src/jalview/datamodel/features/SequenceFeaturesI.java b/src/jalview/datamodel/features/SequenceFeaturesI.java index 5f904da..43e9448 100644 --- a/src/jalview/datamodel/features/SequenceFeaturesI.java +++ b/src/jalview/datamodel/features/SequenceFeaturesI.java @@ -16,7 +16,7 @@ public interface SequenceFeaturesI * * @param sf */ - public abstract boolean add(SequenceFeature sf); + abstract boolean add(SequenceFeature sf); /** * Returns a (possibly empty) list of features, optionally restricted to @@ -28,7 +28,7 @@ public interface SequenceFeaturesI * @param type * @return */ - public abstract List findFeatures(int from, int to, + abstract List findFeatures(int from, int to, String... type); /** @@ -38,9 +38,26 @@ public interface SequenceFeaturesI * @param type * @return */ - public abstract List getAllFeatures(String... type); + abstract List getAllFeatures(String... type); - public abstract int getFeatureCount(boolean positional, String... type); + /** + * Answers the number of (positional or non-positional) features, optionally + * restricted to specified feature types. Contact features are counted as 1. + * + * @param positional + * @param type + * @return + */ + abstract int getFeatureCount(boolean positional, String... type); + + /** + * Answers the total length of positional features, optionally restricted to + * specified feature types. Contact features are counted as length 1. + * + * @param type + * @return + */ + abstract int getTotalFeatureLength(String... type); /** * Answers a list of all positional features, optionally restricted to @@ -49,7 +66,7 @@ public interface SequenceFeaturesI * @param type * @return */ - public abstract List getPositionalFeatures( + abstract List getPositionalFeatures( String... type); /** @@ -58,7 +75,7 @@ public interface SequenceFeaturesI * * @return */ - public abstract List getContactFeatures(String... type); + abstract List getContactFeatures(String... type); /** * Answers a list of all non-positional features, optionally restricted to @@ -68,7 +85,7 @@ public interface SequenceFeaturesI * if no type is specified, all are returned * @return */ - public abstract List getNonPositionalFeatures( + abstract List getNonPositionalFeatures( String... type); /** @@ -79,14 +96,14 @@ public interface SequenceFeaturesI * * @param sf */ - public abstract boolean delete(SequenceFeature sf); + abstract boolean delete(SequenceFeature sf); /** * Answers true if this store contains at least one feature, else false * * @return */ - public abstract boolean hasFeatures(); + abstract boolean hasFeatures(); /** * Returns a set of the distinct feature groups present in the collection. The @@ -99,7 +116,7 @@ public interface SequenceFeaturesI * @param type * @return */ - public abstract Set getFeatureGroups(boolean positionalFeatures, + abstract Set getFeatureGroups(boolean positionalFeatures, String... type); /** @@ -112,7 +129,7 @@ public interface SequenceFeaturesI * @param groups * @return */ - public abstract Set getFeatureTypesForGroups( + abstract Set getFeatureTypesForGroups( boolean positionalFeatures, String... groups); /** @@ -120,6 +137,6 @@ public interface SequenceFeaturesI * * @return */ - public abstract Set getFeatureTypes(); + abstract Set getFeatureTypes(); } \ No newline at end of file diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index 067d8ae..a98e728 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -24,7 +24,6 @@ import jalview.api.FeatureColourI; import jalview.api.FeatureSettingsControllerI; import jalview.bin.Cache; import jalview.datamodel.AlignmentI; -import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.gui.Help.HelpId; import jalview.io.JalviewFileChooser; @@ -465,7 +464,7 @@ public class FeatureSettings extends JPanel implements private boolean handlingUpdate = false; /** - * contains a float[3] for each feature type string. created by setTableData + * holds {featureCount, totalExtent} for each feature type */ Map typeWidth = null; @@ -558,69 +557,57 @@ public class FeatureSettings extends JPanel implements typeWidth = new Hashtable(); // TODO: change avWidth calculation to 'per-sequence' average and use long // rather than float - float[] avWidth = null; - SequenceFeature[] tmpfeatures; - String group = null, type; - Vector visibleChecks = new Vector(); - - // Find out which features should be visible depending on which groups - // are selected / deselected - // and recompute average width ordering + + Set displayableTypes = new HashSet(); + + /* + * determine which feature types may be visible depending on + * which groups are selected, and recompute average width data + */ for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++) { SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i); - tmpfeatures = seq.getSequenceFeatures(); - if (tmpfeatures == null) - { - continue; - } - Set types = seq.getFeatures().getFeatureTypes(); - int index = 0; - while (index < tmpfeatures.length) - { - group = tmpfeatures[index].featureGroup; - - if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0) - { - index++; - continue; - } + /* + * get the sequence's groups for positional features + * and keep track of which groups are visible + */ + Set groups = seq.getFeatures().getFeatureGroups(true); + Set visibleGroups = new HashSet(); + for (String group : groups) + { if (group == null || checkGroupState(group)) { - type = tmpfeatures[index].getType(); - if (!visibleChecks.contains(type)) - { - visibleChecks.addElement(type); - } - } - if (!typeWidth.containsKey(tmpfeatures[index].getType())) - { - typeWidth.put(tmpfeatures[index].getType(), - avWidth = new float[3]); - } - else - { - avWidth = typeWidth.get(tmpfeatures[index].getType()); - } - avWidth[0]++; - if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd()) - { - avWidth[1] += 1 + tmpfeatures[index].getBegin() - - tmpfeatures[index].getEnd(); + visibleGroups.add(group); } - else + } + + /* + * get distinct feature types for visible groups + * record distinct visible types, and their count and total length + */ + Set types = seq.getFeatures().getFeatureTypesForGroups(true, + visibleGroups.toArray(new String[visibleGroups.size()])); + for (String type : types) + { + displayableTypes.add(type); + float[] avWidth = typeWidth.get(type); + if (avWidth == null) { - avWidth[1] += 1 + tmpfeatures[index].getEnd() - - tmpfeatures[index].getBegin(); + avWidth = new float[2]; + typeWidth.put(type, avWidth); } - index++; + // todo this could include features with a non-visible group + // - do we greatly care? + // todo should we include non-displayable features here, and only + // update when features are added? + avWidth[0] += seq.getFeatures().getFeatureCount(true, type); + avWidth[1] += seq.getFeatures().getTotalFeatureLength(type); } } - int fSize = visibleChecks.size(); - Object[][] data = new Object[fSize][3]; + Object[][] data = new Object[displayableTypes.size()][3]; int dataIndex = 0; if (fr.hasRenderOrder()) @@ -636,9 +623,9 @@ public class FeatureSettings extends JPanel implements List frl = fr.getRenderOrder(); for (int ro = frl.size() - 1; ro > -1; ro--) { - type = frl.get(ro); + String type = frl.get(ro); - if (!visibleChecks.contains(type)) + if (!displayableTypes.contains(type)) { continue; } @@ -648,16 +635,17 @@ public class FeatureSettings extends JPanel implements data[dataIndex][2] = new Boolean(af.getViewport() .getFeaturesDisplayed().isVisible(type)); dataIndex++; - visibleChecks.removeElement(type); + displayableTypes.remove(type); } } - fSize = visibleChecks.size(); - for (int i = 0; i < fSize; i++) + /* + * process any extra features belonging only to + * a group which was just selected + */ + while (!displayableTypes.isEmpty()) { - // These must be extra features belonging to the group - // which was just selected - type = visibleChecks.elementAt(i).toString(); + String type = displayableTypes.iterator().next(); data[dataIndex][0] = type; data[dataIndex][1] = fr.getFeatureStyle(type); @@ -670,6 +658,7 @@ public class FeatureSettings extends JPanel implements data[dataIndex][2] = new Boolean(true); dataIndex++; + displayableTypes.remove(type); } if (originalData == null) diff --git a/test/jalview/datamodel/features/SequenceFeaturesTest.java b/test/jalview/datamodel/features/SequenceFeaturesTest.java index 4cd37ee..31703cd 100644 --- a/test/jalview/datamodel/features/SequenceFeaturesTest.java +++ b/test/jalview/datamodel/features/SequenceFeaturesTest.java @@ -713,4 +713,59 @@ public class SequenceFeaturesTest assertEquals(features.size(), 3); assertFalse(features.contains(sf3)); } + + @Test(groups = "Functional") + public void testGetTotalFeatureLength() + { + SequenceFeaturesI store = new SequenceFeatures(); + assertEquals(store.getTotalFeatureLength(), 0); + + SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + assertTrue(store.add(sf1)); + assertEquals(store.getTotalFeatureLength(), 11); + + // re-add does nothing! + assertFalse(store.add(sf1)); + assertEquals(store.getTotalFeatureLength(), 11); + + /* + * add non-positional feature + */ + SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0, + Float.NaN, null); + store.add(sf3); + assertEquals(store.getTotalFeatureLength(), 11); + + /* + * add contact feature - counts 1 to feature length + */ + SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc", + 10, 20, Float.NaN, null); + store.add(sf4); + assertEquals(store.getTotalFeatureLength(), 12); + + /* + * add another Pfam + */ + SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20, + Float.NaN, null); + store.add(sf5); + assertEquals(store.getTotalFeatureLength(), 23); + + /* + * delete features + */ + assertTrue(store.delete(sf3)); // non-positional + assertEquals(store.getTotalFeatureLength(), 23); // no change + + assertTrue(store.delete(sf5)); + assertEquals(store.getTotalFeatureLength(), 12); + + assertTrue(store.delete(sf4)); // contact + assertEquals(store.getTotalFeatureLength(), 11); + + assertTrue(store.delete(sf1)); + assertEquals(store.getTotalFeatureLength(), 0); + } }