package jalview.datamodel.features; import jalview.datamodel.SequenceFeature; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * 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 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; /** * Constructor */ public SequenceFeatures() { featureStore = new HashMap(); } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#add(jalview.datamodel.SequenceFeature) */ @Override 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); } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#findFeatures(int, int, java.lang.String) */ @Override public List findFeatures(int from, int to, String... type) { List result = new ArrayList(); for (String featureType : varargToTypes(type)) { FeatureStore features = featureStore.get(featureType); if (features != null) { result.addAll(features.findOverlappingFeatures(from, to)); } } return result; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getAllFeatures(java.lang.String) */ @Override public List getAllFeatures(String... type) { List result = new ArrayList(); result.addAll(getPositionalFeatures(type)); result.addAll(getNonPositionalFeatures(type)); return result; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureCount(boolean, java.lang.String) */ @Override public int getFeatureCount(boolean positional, String... type) { int result = 0; for (FeatureStore fs : featureStore.values()) { result += fs.size(positional); } return result; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getPositionalFeatures(java.lang.String) */ @Override public List getPositionalFeatures(String... type) { List result = new ArrayList(); for (String featureType : varargToTypes(type)) { FeatureStore featureSet = featureStore.get(featureType); if (featureSet != null) { result.addAll(featureSet.getPositionalFeatures()); } } 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 * * @param type * @return */ protected Iterable varargToTypes(String... type) { return type == null || type.length == 0 ? featureStore .keySet() : Arrays.asList(type); } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getContactFeatures(java.lang.String) */ @Override public List getContactFeatures(String... type) { List result = new ArrayList(); for (String featureType : varargToTypes(type)) { FeatureStore featureSet = featureStore.get(featureType); if (featureSet != null) { result.addAll(featureSet.getContactFeatures()); } } return result; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getNonPositionalFeatures(java.lang.String) */ @Override public List getNonPositionalFeatures(String... type) { List result = new ArrayList(); for (String featureType : varargToTypes(type)) { FeatureStore featureSet = featureStore.get(featureType); if (featureSet != null) { result.addAll(featureSet.getNonPositionalFeatures()); } } return result; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#delete(jalview.datamodel.SequenceFeature) */ @Override public boolean delete(SequenceFeature sf) { for (FeatureStore featureSet : featureStore.values()) { if (featureSet.delete(sf)) { return true; } } return false; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#hasFeatures() */ @Override public boolean hasFeatures() { for (FeatureStore featureSet : featureStore.values()) { if (!featureSet.isEmpty()) { return true; } } return false; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureGroups(boolean, java.lang.String) */ @Override public Set getFeatureGroups(boolean positionalFeatures, String... type) { Set groups = new HashSet(); Iterable types = varargToTypes(type); for (String featureType : types) { FeatureStore featureSet = featureStore.get(featureType); if (featureSet != null) { groups.addAll(featureSet.getFeatureGroups(positionalFeatures)); } } return groups; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureTypesForGroups(boolean, java.lang.String) */ @Override public Set getFeatureTypesForGroups(boolean positionalFeatures, String... groups) { Set result = new HashSet(); for (Entry featureType : featureStore.entrySet()) { Set featureGroups = featureType.getValue().getFeatureGroups( positionalFeatures); for (String group : groups) { if (featureGroups.contains(group)) { /* * yes this feature type includes one of the query groups */ result.add(featureType.getKey()); break; } } } return result; } /* (non-Javadoc) * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureTypes() */ @Override public Set getFeatureTypes() { return Collections.unmodifiableSet(featureStore.keySet()); } }