X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2Ffeatures%2FSequenceFeatures.java;h=54102ceaf28952bc9848b2c0cfe9fe919ccbd6a1;hb=5fa443fd521570a00ce85a627761582adad2bfd4;hp=5fa9a3cf120e0d047daf6749da729bac421d2df7;hpb=b5a5b5fa7fe3fb77e55acf930d25d10ddc0e80e2;p=jalview.git diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index 5fa9a3c..54102ce 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -1,10 +1,32 @@ +/* + * 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. + * + * 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 . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.datamodel.features; import jalview.datamodel.SequenceFeature; +import jalview.io.gff.SequenceOntologyFactory; +import jalview.io.gff.SequenceOntologyI; +import jalview.util.Platform; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -12,6 +34,8 @@ import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; +import intervalstore.api.IntervalI; + /** * A class that stores sequence features in a way that supports efficient * querying by type and location (overlap). Intended for (but not limited to) @@ -27,7 +51,9 @@ public class SequenceFeatures implements SequenceFeaturesI * map from feature type to structured store of features for that type * null types are permitted (but not a good idea!) */ - private Map featureStore; + private Map featureStore; + + private static boolean useIntervalStore = !Platform.isJS(); /** * Constructor @@ -36,10 +62,26 @@ public class SequenceFeatures implements SequenceFeaturesI { /* * use a TreeMap so that features are returned in alphabetical order of type - * wrap as a synchronized map for add and delete operations + * ? wrap as a synchronized map for add and delete operations */ - featureStore = Collections - .synchronizedSortedMap(new TreeMap()); + // featureStore = Collections + // .synchronizedSortedMap(new TreeMap()); + featureStore = new TreeMap<>(); + } + + /** + * Constructor given a list of features + */ + public SequenceFeatures(List features) + { + this(); + if (features != null) + { + for (SequenceFeature feature : features) + { + add(feature); + } + } } /** @@ -57,11 +99,17 @@ public class SequenceFeatures implements SequenceFeaturesI if (featureStore.get(type) == null) { - featureStore.put(type, new FeatureStore()); + featureStore.put(type, newFeatureStore()); } return featureStore.get(type).addFeature(sf); } + private FeatureStoreI newFeatureStore() + { + // TODO Auto-generated method stub + return (useIntervalStore ? new FeatureStoreImpl() : new FeatureStoreJS()); + } + /** * {@inheritDoc} */ @@ -69,17 +117,11 @@ public class SequenceFeatures implements SequenceFeaturesI public List findFeatures(int from, int to, String... type) { - List result = new ArrayList(); - - for (String featureType : varargToTypes(type)) + List result = new ArrayList<>(); + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore features = featureStore.get(featureType); - if (features != null) - { - result.addAll(features.findOverlappingFeatures(from, to)); - } + result.addAll(featureSet.findOverlappingFeatures(from, to, null)); } - return result; } @@ -89,11 +131,11 @@ public class SequenceFeatures implements SequenceFeaturesI @Override public List getAllFeatures(String... type) { - List result = new ArrayList(); + List result = new ArrayList<>(); result.addAll(getPositionalFeatures(type)); - result.addAll(getNonPositionalFeatures(type)); + result.addAll(getNonPositionalFeatures()); return result; } @@ -102,17 +144,37 @@ public class SequenceFeatures implements SequenceFeaturesI * {@inheritDoc} */ @Override + public List getFeaturesByOntology(String... ontologyTerm) + { + if (ontologyTerm == null || ontologyTerm.length == 0) + { + return new ArrayList<>(); + } + + Set featureTypes = getFeatureTypes(ontologyTerm); + if (featureTypes.isEmpty()) + { + /* + * no features of the specified type or any sub-type + */ + return new ArrayList<>(); + } + + return getAllFeatures(featureTypes.toArray(new String[featureTypes + .size()])); + } + + /** + * {@inheritDoc} + */ + @Override public int getFeatureCount(boolean positional, String... type) { int result = 0; - for (String featureType : varargToTypes(type)) + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore featureSet = featureStore.get(featureType); - if (featureSet != null) - { - result += featureSet.getFeatureCount(positional); - } + result += featureSet.getFeatureCount(positional); } return result; } @@ -125,16 +187,11 @@ public class SequenceFeatures implements SequenceFeaturesI { int result = 0; - for (String featureType : varargToTypes(type)) + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore featureSet = featureStore.get(featureType); - if (featureSet != null) - { - result += featureSet.getTotalFeatureLength(); - } + result += featureSet.getTotalFeatureLength(); } return result; - } /** @@ -143,43 +200,42 @@ public class SequenceFeatures implements SequenceFeaturesI @Override public List getPositionalFeatures(String... type) { - List result = new ArrayList(); + List result = new ArrayList<>(); - for (String featureType : varargToTypes(type)) + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore featureSet = featureStore.get(featureType); - if (featureSet != null) - { - result.addAll(featureSet.getPositionalFeatures()); - } + featureSet.getPositionalFeatures(result); } return result; } /** * A convenience method that converts a vararg for feature types to an - * Iterable, replacing the value with the stored feature types if it is null - * or empty + * Iterable over matched feature sets in key order * * @param type * @return */ - protected Iterable varargToTypes(String... type) + protected Iterable varargToTypes(String... type) { if (type == null || type.length == 0) { /* - * no vararg parameter supplied + * no vararg parameter supplied - return all */ - return featureStore.keySet(); + return featureStore.values(); } - /* - * else make a copy of the list, and remove any null value just in case, - * as it would cause errors looking up the features Map - */ - List types = new ArrayList(Arrays.asList(type)); - types.remove(null); + + List types = new ArrayList<>(); + List args = Arrays.asList(type); + for (Entry featureType : featureStore.entrySet()) + { + if (args.contains(featureType.getKey())) + { + types.add(featureType.getValue()); + } + } return types; } @@ -189,15 +245,11 @@ public class SequenceFeatures implements SequenceFeaturesI @Override public List getContactFeatures(String... type) { - List result = new ArrayList(); + List result = new ArrayList<>(); - for (String featureType : varargToTypes(type)) + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore featureSet = featureStore.get(featureType); - if (featureSet != null) - { - result.addAll(featureSet.getContactFeatures()); - } + featureSet.getContactFeatures(result); } return result; } @@ -208,15 +260,11 @@ public class SequenceFeatures implements SequenceFeaturesI @Override public List getNonPositionalFeatures(String... type) { - List result = new ArrayList(); + List result = new ArrayList<>(); - for (String featureType : varargToTypes(type)) + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore featureSet = featureStore.get(featureType); - if (featureSet != null) - { - result.addAll(featureSet.getNonPositionalFeatures()); - } + featureSet.getNonPositionalFeatures(result); } return result; } @@ -227,7 +275,7 @@ public class SequenceFeatures implements SequenceFeaturesI @Override public boolean delete(SequenceFeature sf) { - for (FeatureStore featureSet : featureStore.values()) + for (FeatureStoreI featureSet : featureStore.values()) { if (featureSet.delete(sf)) { @@ -243,7 +291,7 @@ public class SequenceFeatures implements SequenceFeaturesI @Override public boolean hasFeatures() { - for (FeatureStore featureSet : featureStore.values()) + for (FeatureStoreI featureSet : featureStore.values()) { if (!featureSet.isEmpty()) { @@ -260,17 +308,11 @@ public class SequenceFeatures implements SequenceFeaturesI public Set getFeatureGroups(boolean positionalFeatures, String... type) { - Set groups = new HashSet(); - - Iterable types = varargToTypes(type); + Set groups = new HashSet<>(); - for (String featureType : types) + for (FeatureStoreI featureSet : varargToTypes(type)) { - FeatureStore featureSet = featureStore.get(featureType); - if (featureSet != null) - { - groups.addAll(featureSet.getFeatureGroups(positionalFeatures)); - } + groups.addAll(featureSet.getFeatureGroups(positionalFeatures)); } return groups; @@ -283,9 +325,9 @@ public class SequenceFeatures implements SequenceFeaturesI public Set getFeatureTypesForGroups(boolean positionalFeatures, String... groups) { - Set result = new HashSet(); + Set result = new HashSet<>(); - for (Entry featureType : featureStore.entrySet()) + for (Entry featureType : featureStore.entrySet()) { Set featureGroups = featureType.getValue().getFeatureGroups( positionalFeatures); @@ -309,20 +351,48 @@ public class SequenceFeatures implements SequenceFeaturesI * {@inheritDoc} */ @Override - public Set getFeatureTypes() + public Set getFeatureTypes(String... soTerm) { - Set types = new HashSet(); - for (Entry entry : featureStore.entrySet()) + Set types = new HashSet<>(); + for (Entry entry : featureStore.entrySet()) { - if (!entry.getValue().isEmpty()) + String type = entry.getKey(); + if (!entry.getValue().isEmpty() && isOntologyTerm(type, soTerm)) { - types.add(entry.getKey()); + types.add(type); } } return types; } /** + * Answers true if the given type matches one of the specified terms (or is a + * sub-type of one in the Sequence Ontology), or if no terms are supplied. + * Answers false if filter terms are specified and the given term does not + * match any of them. + * + * @param type + * @param soTerm + * @return + */ + protected boolean isOntologyTerm(String type, String... soTerm) + { + if (soTerm == null || soTerm.length == 0) + { + return true; + } + SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology(); + for (String term : soTerm) + { + if (type.equals(term) || so.isA(type, term)) + { + return true; + } + } + return false; + } + + /** * {@inheritDoc} */ @Override @@ -341,4 +411,127 @@ public class SequenceFeatures implements SequenceFeaturesI return featureStore.containsKey(type) ? featureStore.get(type) .getMaximumScore(positional) : Float.NaN; } + + /** + * A convenience method to sort features by start position ascending (if on + * forward strand), or end position descending (if on reverse strand) + * + * @param features + * @param forwardStrand + */ + public static void sortFeatures(List features, + final boolean forwardStrand) + { + IntervalI.sortIntervals(features, forwardStrand); + } + + /** + * {@inheritDoc} This method is 'semi-optimised': it only inspects features + * for types that include the specified group, but has to inspect every + * feature of those types for matching feature group. This is efficient unless + * a sequence has features that share the same type but are in different + * groups - an unlikely case. + *

+ * For example, if RESNUM feature is created with group = PDBID, then features + * would only be retrieved for those sequences associated with the target + * PDBID (group). + */ + @Override + public List getFeaturesForGroup(boolean positional, + String group, String... type) + { + List result = new ArrayList<>(); + for (FeatureStoreI featureSet : varargToTypes(type)) + { + if (featureSet.getFeatureGroups(positional).contains(group)) + { + result.addAll(featureSet.getFeaturesForGroup(positional, group)); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean shiftFeatures(int fromPosition, int shiftBy) + { + boolean modified = false; + for (FeatureStoreI fs : featureStore.values()) + { + modified |= fs.shiftFeatures(fromPosition, shiftBy); + } + return modified; + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteAll() + { + featureStore.clear(); + } + + /** + * Simplified find for features associated with a given position. + * + * JavaScript set to not use IntervalI, but easily testable by setting false + * to true in javadoc + * + * FeatureRenderer has checked already that featureStore does contain type. + * + * @author Bob Hanson 2019.07.30 + */ + @Override + public List findFeatures(int pos, String type, + List list) + { + FeatureStoreI fs = featureStore.get(type); + return fs.findOverlappingFeatures(pos, pos, list); + } + + // Chrome; developer console closed + + // BH 2019.08.01 useIntervalStore true, redraw false: + // Platform: timer mark 13.848 0.367 overviewrender 16000 pixels row:14 + // Platform: timer mark 15.391 0.39 overviewrender 16000 pixels row:14 + // Platform: timer mark 16.498 0.39 overviewrender 16000 pixels row:14 + // Platform: timer mark 17.596 0.401 overviewrender 16000 pixels row:14 + // Platform: timer mark 18.738 0.363 overviewrender 16000 pixels row:14 + // Platform: timer mark 19.659 0.358 overviewrender 16000 pixels row:14 + // Platform: timer mark 20.737 0.359 overviewrender 16000 pixels row:14 + // Platform: timer mark 21.797 0.391 overviewrender 16000 pixels row:14 + // Platform: timer mark 22.851 0.361 overviewrender 16000 pixels row:14 + // Platform: timer mark 24.019 0.395 overviewrender 16000 pixels row:14 + + // BH 2019.08.01 useIntervalStore false, redraw false: + // Platform: timer mark 19.011 0.181 overviewrender 16000 pixels row:14 + // Platform: timer mark 20.311 0.183 overviewrender 16000 pixels row:14 + // Platform: timer mark 21.368 0.175 overviewrender 16000 pixels row:14 + // Platform: timer mark 22.347 0.178 overviewrender 16000 pixels row:14 + // Platform: timer mark 23.605 0.216 overviewrender 16000 pixels row:14 + // Platform: timer mark 24.836 0.191 overviewrender 16000 pixels row:14 + // Platform: timer mark 26.016 0.181 overviewrender 16000 pixels row:14 + // Platform: timer mark 27.278 0.178 overviewrender 16000 pixels row:14 + // Platform: timer mark 28.158 0.181 overviewrender 16000 pixels row:14 + // Platform: timer mark 29.227 0.196 overviewrender 16000 pixels row:14 + // Platform: timer mark 30.1 0.171 overviewrender 16000 pixels row:14 + // Platform: timer mark 31.684 0.196 overviewrender 16000 pixels row:14 + // Platform: timer mark 32.779 0.18 overviewrender 16000 pixels row:14 + // Platform: timer mark 52.355 0.185 overviewrender 16000 pixels row:14 + // Platform: timer mark 53.829 0.186 overviewrender 16000 pixels row:14 + + + + /** + * @author Bob Hanson 2019.08.01 + */ + @Override + public boolean hasFeatures(String type) + { + return featureStore.containsKey(type); + } + }