package jalview.datamodel.features; import jalview.datamodel.SequenceFeature; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A class that stores sequence features in a way that supports efficient * querying by type and location (overlap). Intended for (but not limited to) * storage of features for one sequence. * * @author gmcarstairs * */ public class SequenceFeatures { /* * map from feature type to structured store of features for that type * null types are permitted (but not a good idea!) */ private Map featureStore; /** * Constructor */ public SequenceFeatures() { featureStore = new HashMap(); } /** * Adds one sequence feature to the store, and returns true, unless the * feature is already contained in the store, in which case this method * returns false. Containment is determined by SequenceFeature.equals() * comparison. * * @param sf */ public boolean add(SequenceFeature sf) { String type = sf.getType(); if (featureStore.get(type) == null) { featureStore.put(type, new FeatureStore()); } return featureStore.get(type).addFeature(sf); } /** * Returns a (possibly empty) list of features of the given type which overlap * the (inclusive) sequence position range * * @param type * @param from * @param to * @return */ public List findFeatures(String type, int from, int to) { FeatureStore features = featureStore.get(type); if (features == null) { return Collections.emptyList(); } return features.findOverlappingFeatures(from, to); } /** * Answers a list of all features stored (including non-positional), in no * particular guaranteed order * * @return */ public List getFeatures() { List result = new ArrayList(); for (FeatureStore featureSet : featureStore.values()) { result.addAll(featureSet.getFeatures()); } return result; } /** * Answers a list of all non-positional features stored, in no particular * guaranteed order * * @return */ public List getNonPositionalFeatures() { List result = new ArrayList(); for (FeatureStore featureSet : featureStore.values()) { result.addAll(featureSet.getNonPositionalFeatures()); } return result; } /** * Answers a list of all contact features stored, in no particular guaranteed * order * * @return */ public List getContactFeatures() { List result = new ArrayList(); for (FeatureStore featureSet : featureStore.values()) { result.addAll(featureSet.getContactFeatures()); } return result; } /** * Answers a list of all features of the given type (including * non-positional), in no particular guaranteed order * * @return */ public List getFeatures(String type) { List result = new ArrayList(); FeatureStore featureSet = featureStore.get(type); if (featureSet != null) { result.addAll(featureSet.getFeatures()); } return result; } /** * Answers a list of all contact features of the given type, in no particular * guaranteed order * * @return */ public List getContactFeatures(String type) { List result = new ArrayList(); FeatureStore featureSet = featureStore.get(type); if (featureSet != null) { result.addAll(featureSet.getContactFeatures()); } return result; } /** * Answers a list of all non-positional features of the given type, in no * particular guaranteed order * * @return */ public List getNonPositionalFeatures(String type) { List result = new ArrayList(); FeatureStore featureSet = featureStore.get(type); if (featureSet != null) { result.addAll(featureSet.getNonPositionalFeatures()); } return result; } /** * Deletes the given feature from the store, returning true if it was found * (and deleted), else false. This method makes no assumption that the feature * is in the 'expected' place in the store, in case it has been modified since * it was added. * * @param sf */ public boolean delete(SequenceFeature sf) { for (FeatureStore featureSet : featureStore.values()) { if (featureSet.delete(sf)) { return true; } } return false; } /** * Answers true if this store contains at least one feature, else false * * @return */ public boolean hasFeatures() { for (FeatureStore featureSet : featureStore.values()) { if (!featureSet.isEmpty()) { return true; } } return false; } }