package org.flymine.bj.bio;import java.util.Collection;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;import java.util.Map;import java.util.Set;import org.apache.log4j.Logger;import org.biojava.bio.BioException;import org.biojava.bio.seq.AbstractFeatureHolder;import org.biojava.bio.seq.Feature;import org.biojava.bio.seq.FeatureFilter;import org.biojava.bio.seq.FeatureHolder;import org.biojava.bio.seq.FilterUtils;import org.biojava.bio.seq.StrandedFeature;import org.biojava.utils.walker.Walker;import org.biojava.utils.walker.WalkerFactory;import org.flymine.bj.exceptions.ModelExceptionFM;import org.flymine.bj.query.QueryFM;import org.flymine.bj.query.VisitorSeq;import org.flymine.model.genomic.BioEntity;import org.intermine.objectstore.query.Query;/** * Implementation of a FlyMine (FM) specific BioJava FeatureHolder * * @author Markus Brosch */public class FeatureHolderFM extends AbstractFeatureHolder implements FeatureHolder { // ======================================================================= // Attributes // ======================================================================= /** * logger */ private static Logger _log = Logger.getLogger(FeatureHolderFM.class); /** * the source/root SequenceFM */ private final SequenceFM _sourceSeq; //required /** * if this FeatureHolder holds _all_ features of a chromosome, this flag ist true; <br> * if this FeatureHolder is a result of a filter and a subset of features of a chromosome, this * flag is false */ private final boolean _isAllFeatures; /** * chromosome ID which represents the _sourceSeq; can be extracted from sourceSeq, but it is * called many times, therefore a attribute is */ private final Integer _chromosomeID; /** * ID's of Features (BioEntities) represented by this FeatureHolder */ private Collection _ids; /** * Set of FeatureFM Objects which are held by this FeatureHolder */ private Collection _features = null; // ======================================================================= // Constructors // ======================================================================= /** * Constructor * * @param pSourceSeq * represents the source / root Sequence (FlyMine Chromosome). All operations are based * on this underlying sequence. Therefore, all Features of this sequence are taken into * account. If you want to build a subset of features, use the extended constructor and * pass in the ids of these features */ public FeatureHolderFM(final SequenceFM pSourceSeq) { this(pSourceSeq, null); } /** * Constructor * * @param pSourceSeq * represents the source / root Sequence (FlyMine Chromosome). * @param pIds * the IDs of Features (FlyMine BioEntities) this FeatureHolder holds; <br> * If IDs are provided, all operations and methods only work on this subset of features * according to the ID set. */ public FeatureHolderFM(final SequenceFM pSourceSeq, final Set pIds) { if (pSourceSeq == null) { throw new NullPointerException("SequenceFM must not be null"); } _sourceSeq = pSourceSeq; _chromosomeID = pSourceSeq.getBioEntityID(); _ids = pIds; if (pIds == null) { _isAllFeatures = true; } else { _isAllFeatures = false; } if (_log.isDebugEnabled()) { _log.debug("constructor():"); _log.debug("\t sourceSequence was set; name: " + _sourceSeq.getName()); _log.debug("\t chromosomeID was set: " + _chromosomeID); if (_isAllFeatures) { _log.debug("constructor(): ids=null; => FH ~> whole Chromosome "); } else { _log.debug("\t : ids were set; => FH for Features with these IDs"); } } } // ======================================================================= // Implements FeatureHolder interface // ======================================================================= /** * Count how many features (BioEntities) are contained on this Sequence (Chromosome) * * @return a positive integer or zero, equal to the number of features contained */ public final int countFeatures() { if (_ids != null) { _log.info("countFeatures(): ids.size();"); return _ids.size(); } if (_features != null) { _log.info("countFeatures(): features.size();"); return _features.size(); } final QueryFM q = new QueryFM(_chromosomeID); try { _log.info("countFeatures(): q.countResult(q.byClass(BioEntity.class));"); return q.countResult(q.byClass(BioEntity.class)); } catch (ModelExceptionFM e) { throw new RuntimeException("Not a BioEntity", e); } } /** * @see FeatureHolder#features() */ public final Iterator features() { return getFeatures().iterator(); } /** * @param pFilter * Your FeatureFilter. Be aware, that this implementation does not provide any hierarchy, * therefore the Filters: FeatureFilter.ByChild(), FeatureFilter.ByParent(), * FeatureFilter.ByAncestor() are returning an empty FeatureHolder. * @param pRecurse * you can set it true or false, in both cases it will not change the result as we don't * provide a hierarchy in this implementation. * @return FeatureFM Objects in a FeatureHolder which fulfil the filter ff * @deprecated * @see FeatureHolder#filter(org.biojava.bio.seq.FeatureFilter, boolean) */ public final FeatureHolder filter(final FeatureFilter pFilter, final boolean pRecurse) { return filter(pFilter, false); } /** * @param pFilter * Your FeatureFilter. Be aware, that this implementation does not provide any hierarchy, * therefore the Filters: FeatureFilter.ByChild(), FeatureFilter.ByParent(), * FeatureFilter.ByAncestor() are returning an empty FeatureHolder. * @return FeatureFM Objects in a FeatureHolder which fulfil the filter ff * @see FeatureHolder#filter(org.biojava.bio.seq.FeatureFilter) */ public final FeatureHolder filter(final FeatureFilter pFilter) { _log.info("filter(filter):"); //IF in memory processing if (_sourceSeq.isMemoryMode()) { _log.info("\t isMemoryMode ~> super.filter(filter, false);"); //note: features() is called in super.filter() return super.filter(pFilter, false); } //IF on the fly processing / non memory mode: query dynamically final QueryFM q = new QueryFM(_chromosomeID); //check if features of featureFilter tree are disjoint final FeatureFilter childF = new FeatureFilter.Not(FeatureFilter.top_level); if (FilterUtils.areDisjoint(pFilter, childF)) { return FeatureHolder.EMPTY_FEATURE_HOLDER; } //get a visitor & walk the filter tree, get the generated Query & return FH with resulting IDs final VisitorSeq visitor = new VisitorSeq(q); final WalkerFactory wf = WalkerFactory.getInstance(); _log.info("\t !isMemoryMode ~> walker / visitor used"); try { final Walker walker = wf.getWalker(visitor); walker.walk(pFilter, visitor); Query query = (Query) walker.getValue(); _log.info("\t walker.getValue() -> query -> \n" + query.toString()); assert (query != null) : "something went wrong by walking the feature filter tree"; if (_ids != null && !_isAllFeatures) { query = q.constrainQueryByIDs(query, _ids); //constrain by given IDs _log.info("\t constrained query: \n" + query.toString()); } final Set resultIDs = q.getIds(query); _log.info("\t return new FeatureHolderFM(_sourceSeq, resulIds of query)"); return new FeatureHolderFM(_sourceSeq, resultIDs); } catch (BioException e) { _log.fatal("Problem getting the walker from WalkerFactory" + e.getMessage()); throw new RuntimeException("Problem getting the walker from WalkerFactory", e); } } /** * @see FeatureHolder#containsFeature(org.biojava.bio.seq.Feature) */ public final boolean containsFeature(final Feature pFeature) { if (pFeature instanceof FeatureFM) { return getFeatures().contains(pFeature); } return false; //FeatureHolderFM can only hold instances of FeatureFM } /** * @see FeatureHolder#getSchema() */ public final FeatureFilter getSchema() { return FeatureFilter.all; } // ----------------------------------------------------------------------- // private helpers for implementation of FeatureHolder // ----------------------------------------------------------------------- /** * method to get features of this featureHolder. If features are already allocated, simply return. * Else: depending on the memory mode, create features and return. In memoryMode, the whole * Feature object is instantiated, in non memory mode, it is lazily instantiated by only passing * in the id (the Feature itself then can retrive it's data - once it is needed) * * @return an ArrayList of features */ private Collection getFeatures() { _log.info("getFeatures()"); if (_features == null) { _log.info("\t features == null"); final QueryFM q = new QueryFM(_chromosomeID); _features = new HashSet(); if (_sourceSeq.isMemoryMode()) { _log.info("\t isMemoryMode"); templatesFeatures(q); //memory mode - load all data } else { //non memory mode - lazy instantiation, Features keep only ID _log.info("\t !isMemoryMode"); if (_ids == null) { _log.info("\t ids == null ~> query for ids"); try { _ids = q.getIds(q.byClass(BioEntity.class)); } catch (ModelExceptionFM e) { throw new RuntimeException("Not a BioEntity", e); } } assert (_ids != null); _log.info("\t iterate over ids and add new lazy Features to features"); for (Iterator it = _ids.iterator(); it.hasNext();) { final Integer id = (Integer) it.next(); _features.add(new FeatureFM(_sourceSeq, id)); } } //else not in memory } //if featuresAllocated return _features; } /** * part of getFeatures() - helper method * * @param pQueryFM * the QueryFM of getFeatures() */ private void templatesFeatures(final QueryFM pQueryFM) { final Map map; if (_isAllFeatures) { _log.info("\t isAllFeatures"); //if this featureHolder has ALL features of the underlying chromosome _log.info("\t get map [ID-Template], iterate and add !lazy new Features to features"); map = (pQueryFM.getTemplate(null)); //get ALL feature templates of this sequence for (Iterator it = map.keySet().iterator(); it.hasNext();) { final Integer id = (Integer) it.next(); final StrandedFeature.Template templ = (StrandedFeature.Template) map.get(id); _features.add(new FeatureFM(_sourceSeq, id, templ)); } } else { //if subset of all features _log.info("\t !isAllFeaures"); map = new HashMap(); //if this featureHolder keeps only a subset of features of the underlying chromosome final int packageSize = 10000; //chop ids to bunches of 10000's - avoid too complex sql query _log.info("\t get.Templates of given IDs"); final Iterator iterateIds = _ids.iterator(); for (int i = 0; i < _ids.size(); i += packageSize) { for (int j = i; j < (i + packageSize); j++) { final Set bunchOfIds = new HashSet(); if (iterateIds.hasNext()) { bunchOfIds.add(iterateIds.next()); } else { break; } map.putAll(pQueryFM.getTemplate(bunchOfIds)); } //inner for if (!iterateIds.hasNext()) { break; } } //outer for _log.info("\t iterate map [ID-Template] and add !lazy features to features"); for (Iterator it = map.keySet().iterator(); it.hasNext();) { final Integer id = (Integer) it.next(); final StrandedFeature.Template templ = (StrandedFeature.Template) map.get(id); _features.add(new FeatureFM(_sourceSeq, id, templ)); } } //else }}