X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2Ffeatures%2FSequenceFeatures.java;h=8f6d49697eef4dc5abf315fb71569ce620a38737;hb=8eaa775e59bf928f8899f9ada5c9811200c2853a;hp=4489b1d82939ee0f5a42f37a2781248fb563f7a7;hpb=d08df378179f85c2142f69ce63380baad803a0ab;p=jalview.git diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index 4489b1d..8f6d496 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -1,16 +1,20 @@ package jalview.datamodel.features; +import jalview.datamodel.ContiguousI; import jalview.datamodel.SequenceFeature; +import jalview.io.gff.SequenceOntologyFactory; +import jalview.io.gff.SequenceOntologyI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TreeMap; /** * A class that stores sequence features in a way that supports efficient @@ -22,6 +26,29 @@ import java.util.Set; */ public class SequenceFeatures implements SequenceFeaturesI { + /** + * a comparator for sorting features by start position ascending + */ + private static Comparator FORWARD_STRAND = new Comparator() + { + @Override + public int compare(ContiguousI o1, ContiguousI o2) + { + return Integer.compare(o1.getBegin(), o2.getBegin()); + } + }; + + /** + * a comparator for sorting features by end position descending + */ + private static Comparator REVERSE_STRAND = new Comparator() + { + @Override + public int compare(ContiguousI o1, ContiguousI o2) + { + return Integer.compare(o2.getEnd(), o1.getEnd()); + } + }; /* * map from feature type to structured store of features for that type @@ -34,16 +61,42 @@ public class SequenceFeatures implements SequenceFeaturesI */ public SequenceFeatures() { - featureStore = new HashMap(); + /* + * use a TreeMap so that features are returned in alphabetical order of type + * ? wrap as a synchronized map for add and delete operations + */ + // featureStore = Collections + // .synchronizedSortedMap(new TreeMap()); + featureStore = new TreeMap(); } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#add(jalview.datamodel.SequenceFeature) + /** + * Constructor given a list of features + */ + public SequenceFeatures(List features) + { + this(); + if (features != null) + { + for (SequenceFeature feature : features) + { + add(feature); + } + } + } + + /** + * {@inheritDoc} */ @Override public boolean add(SequenceFeature sf) { String type = sf.getType(); + if (type == null) + { + System.err.println("Feature type may not be null: " + sf.toString()); + return false; + } if (featureStore.get(type) == null) { @@ -52,8 +105,8 @@ public class SequenceFeatures implements SequenceFeaturesI return featureStore.get(type).addFeature(sf); } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#findFeatures(int, int, java.lang.String) + /** + * {@inheritDoc} */ @Override public List findFeatures(int from, int to, @@ -73,8 +126,8 @@ public class SequenceFeatures implements SequenceFeaturesI return result; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getAllFeatures(java.lang.String) + /** + * {@inheritDoc} */ @Override public List getAllFeatures(String... type) @@ -83,28 +136,68 @@ public class SequenceFeatures implements SequenceFeaturesI result.addAll(getPositionalFeatures(type)); - result.addAll(getNonPositionalFeatures(type)); + result.addAll(getNonPositionalFeatures()); return result; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureCount(boolean, java.lang.String) + /** + * {@inheritDoc} + */ + @Override + public List getFeaturesByOntology(String... ontologyTerm) + { + if (ontologyTerm == null || ontologyTerm.length == 0) + { + return new ArrayList(); + } + + Set featureTypes = getFeatureTypes(ontologyTerm); + return getAllFeatures(featureTypes.toArray(new String[featureTypes + .size()])); + } + + /** + * {@inheritDoc} */ @Override public int getFeatureCount(boolean positional, String... type) { int result = 0; - for (FeatureStore fs : featureStore.values()) + + for (String featureType : varargToTypes(type)) { - result += fs.size(positional); + FeatureStore featureSet = featureStore.get(featureType); + if (featureSet != null) + { + result += featureSet.getFeatureCount(positional); + } } + return result; + } + + /** + * {@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; + } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getPositionalFeatures(java.lang.String) + /** + * {@inheritDoc} */ @Override public List getPositionalFeatures(String... type) @@ -132,12 +225,27 @@ public class SequenceFeatures implements SequenceFeaturesI */ protected Iterable varargToTypes(String... type) { - return type == null || type.length == 0 ? featureStore - .keySet() : Arrays.asList(type); + if (type == null || type.length == 0) + { + /* + * no vararg parameter supplied + */ + return featureStore.keySet(); + } + + /* + * 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 + * sort in alphabetical order for consistent output behaviour + */ + List types = new ArrayList(Arrays.asList(type)); + types.remove(null); + Collections.sort(types); + return types; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getContactFeatures(java.lang.String) + /** + * {@inheritDoc} */ @Override public List getContactFeatures(String... type) @@ -155,8 +263,8 @@ public class SequenceFeatures implements SequenceFeaturesI return result; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getNonPositionalFeatures(java.lang.String) + /** + * {@inheritDoc} */ @Override public List getNonPositionalFeatures(String... type) @@ -174,8 +282,8 @@ public class SequenceFeatures implements SequenceFeaturesI return result; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#delete(jalview.datamodel.SequenceFeature) + /** + * {@inheritDoc} */ @Override public boolean delete(SequenceFeature sf) @@ -190,8 +298,8 @@ public class SequenceFeatures implements SequenceFeaturesI return false; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#hasFeatures() + /** + * {@inheritDoc} */ @Override public boolean hasFeatures() @@ -206,8 +314,8 @@ public class SequenceFeatures implements SequenceFeaturesI return false; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureGroups(boolean, java.lang.String) + /** + * {@inheritDoc} */ @Override public Set getFeatureGroups(boolean positionalFeatures, @@ -229,8 +337,8 @@ public class SequenceFeatures implements SequenceFeaturesI return groups; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureTypesForGroups(boolean, java.lang.String) + /** + * {@inheritDoc} */ @Override public Set getFeatureTypesForGroups(boolean positionalFeatures, @@ -258,12 +366,129 @@ public class SequenceFeatures implements SequenceFeaturesI return result; } - /* (non-Javadoc) - * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureTypes() + /** + * {@inheritDoc} + */ + @Override + public Set getFeatureTypes(String... soTerm) + { + Set types = new HashSet(); + for (Entry entry : featureStore.entrySet()) + { + String type = entry.getKey(); + if (!entry.getValue().isEmpty() && isOntologyTerm(type, soTerm)) + { + types.add(type); + } + } + return types; + } + + /** + * Answers true if the given type is one of the specified sequence ontology + * terms (or a sub-type of one), 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.getInstance(); + for (String term : soTerm) + { + if (so.isA(type, term)) + { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public float getMinimumScore(String type, boolean positional) + { + return featureStore.containsKey(type) ? featureStore.get(type) + .getMinimumScore(positional) : Float.NaN; + } + + /** + * {@inheritDoc} + */ + @Override + public float getMaximumScore(String type, boolean positional) + { + 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) + { + Collections.sort(features, forwardStrand ? FORWARD_STRAND + : REVERSE_STRAND); + } + + /** + * {@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(); + Iterable types = varargToTypes(type); + + for (String featureType : types) + { + /* + * check whether the feature type is present, and also + * whether it has features for the specified group + */ + FeatureStore features = featureStore.get(featureType); + if (features != null + && features.getFeatureGroups(positional).contains(group)) + { + result.addAll(features.getFeaturesForGroup(positional, group)); + } + } + return result; + } + + /** + * {@inheritDoc} */ @Override - public Set getFeatureTypes() + public boolean shiftFeatures(int shift) { - return Collections.unmodifiableSet(featureStore.keySet()); + boolean modified = false; + for (FeatureStore fs : featureStore.values()) + { + modified |= fs.shiftFeatures(shift); + } + return modified; } -} +} \ No newline at end of file