Java tutorial
/* * The aspiredb project * * Copyright (c) 2012 University of British Columbia * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package ubc.pavlab.aspiredb.server.dao; import gemma.gsec.SecurityService; import gemma.gsec.authentication.UserManager; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.time.StopWatch; import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.stat.SecondLevelCacheStatistics; import org.hibernate.stat.Statistics; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import ubc.pavlab.aspiredb.server.exceptions.BioMartServiceException; import ubc.pavlab.aspiredb.server.exceptions.NeurocartaServiceException; import ubc.pavlab.aspiredb.server.model.Characteristic; import ubc.pavlab.aspiredb.server.model.Subject; import ubc.pavlab.aspiredb.server.model.Variant; import ubc.pavlab.aspiredb.server.model.Variant2VariantOverlap; import ubc.pavlab.aspiredb.server.util.GenomeBin; import ubc.pavlab.aspiredb.server.util.PhenotypeUtil; import ubc.pavlab.aspiredb.shared.GenomicRange; import ubc.pavlab.aspiredb.shared.NumericValue; import ubc.pavlab.aspiredb.shared.VariantValueObject; import ubc.pavlab.aspiredb.shared.query.AspireDbFilterConfig; import ubc.pavlab.aspiredb.shared.query.Operator; import ubc.pavlab.aspiredb.shared.query.PhenotypeFilterConfig; import ubc.pavlab.aspiredb.shared.query.ProjectFilterConfig; import ubc.pavlab.aspiredb.shared.query.ProjectOverlapFilterConfig; import ubc.pavlab.aspiredb.shared.query.Property; import ubc.pavlab.aspiredb.shared.query.SubjectFilterConfig; import ubc.pavlab.aspiredb.shared.query.VariantFilterConfig; import ubc.pavlab.aspiredb.shared.query.restriction.PhenotypeRestriction; import ubc.pavlab.aspiredb.shared.query.restriction.RestrictionExpression; import ubc.pavlab.aspiredb.shared.query.restriction.SimpleRestriction; import ubc.pavlab.aspiredb.shared.suggestions.SuggestionContext; /** * TODO Document Me * * @author anton */ public abstract class VariantDaoBaseImpl<T extends Variant> extends DaoBaseImpl<T> implements VariantDaoBase<T> { private Class<T> elementClass; @Autowired private Variant2SpecialVariantOverlapDao variant2SpecialVariantOverlapDao; @Autowired private SubjectDao subjectDao; @Autowired private ProjectDao projectDao; @Autowired private PhenotypeUtil phenotypeUtils; @Autowired private SecurityService securityService; @Autowired private UserManager userManager; @SuppressWarnings({ "unchecked", "rawtypes" }) protected VariantDaoBaseImpl(Class elementClass) { super(elementClass); assert elementClass.isAssignableFrom(elementClass); this.elementClass = elementClass; } @Override public void printCacheStatistics() { String regionName = this.elementClass.getName(); Statistics stats = this.getSessionFactory().getStatistics(); SecondLevelCacheStatistics secondLevelStats = stats.getSecondLevelCacheStatistics(regionName); log.info("SecondLevelCache:" + regionName + "," + secondLevelStats); } @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true) public Collection<VariantValueObject> loadByGenomicLocationIDs(Collection<Long> genomicLocIDs) { if (genomicLocIDs == null || genomicLocIDs.size() == 0) { return new ArrayList<>(); } Session session = this.getSessionFactory().getCurrentSession(); String hql = "select distinct variant.id, location.chromosome, location.start, location.end from Variant variant inner join variant.location as location WHERE location.id in (:locIDs)"; Query query = session.createQuery(hql); query.setParameterList("locIDs", genomicLocIDs); Collection<Object[]> results = query.list(); Collection<VariantValueObject> ret = new ArrayList<>(); for (Object[] r : results) { VariantValueObject vvoOverlapped = new VariantValueObject(); vvoOverlapped.setId((Long) r[0]); vvoOverlapped.setGenomicRange(new GenomicRange((String) r[1], (int) r[2], (int) r[3])); ret.add(vvoOverlapped); } return ret; } @SuppressWarnings({ "boxing", "unchecked" }) @Override @Transactional(readOnly = true) public Collection<T> findByGenomicLocation(GenomicRange range, Collection<Long> activeProjectIds) { Session session = this.getSessionFactory().getCurrentSession(); List<Integer> bins = GenomeBin.relevantBins(range.getChromosome(), range.getBaseStart(), range.getBaseEnd()); String hql = "select distinct variant from Variant variant inner join variant.location as location inner join variant.subject.project as project WHERE project.id in (:projectIds) and location.bin in (:bins) and location.chromosome=:chromosome and ((location.start>=:start and location.end<=:end) or (location.start<=:start and location.end>=:start) or (location.start<=:end and location.end>=:end))"; Query query = session.createQuery(hql); query.setParameterList("bins", bins); query.setParameterList("projectIds", activeProjectIds); query.setParameter("chromosome", range.getChromosome()); query.setParameter("start", range.getBaseStart()); query.setParameter("end", range.getBaseEnd()); Collection<T> variants = query.list(); return variants; } @SuppressWarnings({ "boxing", "unchecked" }) @Override @Transactional(readOnly = true) public Collection<VariantValueObject> findByGenomicLocationQuick(GenomicRange range, Collection<Long> activeProjectIds) { Session session = this.getSessionFactory().getCurrentSession(); List<Integer> bins = GenomeBin.relevantBins(range.getChromosome(), range.getBaseStart(), range.getBaseEnd()); String hql = "select distinct variant.id, location.chromosome, location.start, location.end from Variant variant inner join variant.location as location inner join variant.subject.project as project WHERE project.id in (:projectIds) and location.bin in (:bins) and location.chromosome=:chromosome and ((location.start>=:start and location.end<=:end) or (location.start<=:start and location.end>=:start) or (location.start<=:end and location.end>=:end))"; Query query = session.createQuery(hql); query.setParameterList("bins", bins); query.setParameterList("projectIds", activeProjectIds); query.setParameter("chromosome", range.getChromosome()); query.setParameter("start", range.getBaseStart()); query.setParameter("end", range.getBaseEnd()); Collection<Object[]> results = query.list(); Collection<VariantValueObject> ret = new ArrayList<>(); for (Object[] r : results) { VariantValueObject vvoOverlapped = new VariantValueObject(); vvoOverlapped.setId((Long) r[0]); vvoOverlapped.setGenomicRange(new GenomicRange((String) r[1], (int) r[2], (int) r[3])); ret.add(vvoOverlapped); } return ret; } /** * Returns list of IDs that satisfy the PhenotypeFilter. Get a list of Subjects first then get the list of all the * Variants for that Subject. This is done for performance reasons. * * @param filterConfig */ public List<Variant> findByPhenotype(PhenotypeFilterConfig filterConfig) { Collection<Subject> subjects = subjectDao.findByPhenotype(filterConfig); if (subjects.size() == 0) { return new ArrayList<Variant>(); } return this.getSessionFactory().getCurrentSession().createCriteria(this.elementClass) .add(Restrictions.in("subject", subjects)).list(); } @Override @Transactional(readOnly = true) public Collection<T> findBySubjectPatientId(Long projectId, String id) { if (projectId == null) { log.warn("Project id is null, retrieving all variants with patient id " + id); return findBySubjectPatientId(id); } List<Subject> subjects = this.getSessionFactory().getCurrentSession().createCriteria(Subject.class) .add(Restrictions.eq("patientId", id)).createAlias("project", "project") .add(Restrictions.eq("project.id", projectId)).list(); if (subjects.size() == 0) { return new ArrayList<T>(); } List<T> variants = this.getSessionFactory().getCurrentSession().createCriteria(this.elementClass) .add(Restrictions.in("subject", subjects)).list(); return variants; } @Override @Deprecated @Transactional(readOnly = true) public Collection<T> findBySubjectPatientId(String id) { List<Subject> subjects = this.getSessionFactory().getCurrentSession().createCriteria(Subject.class) .add(Restrictions.eq("patientId", id)).list(); if (subjects.size() == 0) { return new ArrayList<T>(); } List<T> variants = this.getSessionFactory().getCurrentSession().createCriteria(this.elementClass) .add(Restrictions.in("subject", subjects)).list(); return variants; } /** * @param overlapFilter * @return */ public List<Long> getProjectOverlapVariantIds(ProjectOverlapFilterConfig overlapFilter) { // overlapProjectIds are required for this method if (overlapFilter.getOverlapProjectIds() == null || overlapFilter.getOverlapProjectIds().isEmpty()) { return new ArrayList<Long>(); } List<Long> activeProjectsVariantIds = getVariantIdsForProjects(overlapFilter.getProjectIds()); PhenotypeRestriction phenRestriction = overlapFilter.getPhenotypeRestriction(); Boolean hasPhenotypeRestriction = phenRestriction != null && phenRestriction.getValue() != null && phenRestriction.getName() != null; List<Long> overlapProjsPhenoAssociatedVariantIds = new ArrayList<>(); // Get variants in specified overlapping projects with specified phenotype for easier checking later if (hasPhenotypeRestriction) { overlapProjsPhenoAssociatedVariantIds = getPhenoAssociatedVariantIdsForProjects(overlapFilter); } SimpleRestriction overlapRestriction1 = (SimpleRestriction) overlapFilter.getRestriction1(); // if this is false then retrieve all overlaps Boolean hasOverlapRestriction = validateOverlapRestriction(overlapRestriction1); SimpleRestriction numVariantsOverlapRestriction = (SimpleRestriction) overlapFilter.getRestriction2(); Boolean hasSecondaryOverlapRestriction = validateOverlapRestriction(numVariantsOverlapRestriction); SimpleRestriction supportOfVariantsOverlapRestriction = (SimpleRestriction) overlapFilter.getRestriction3(); Boolean hasTertiaryOverlapRestriction = validateOverlapRestriction(supportOfVariantsOverlapRestriction); List<Long> variantIdsSatisfyingRestrictions = new ArrayList<>(); log.info("Iterating through variants in projectids:" + overlapFilter.getProjectIds() + " for overlap with variants in projectids:" + overlapFilter.getOverlapProjectIds() + "\n hasOverlapRestriction:" + hasOverlapRestriction + " hasSecondaryOverlapRestriction:" + hasSecondaryOverlapRestriction + " hasTertiaryOverlapRestriction: " + hasTertiaryOverlapRestriction + " hasPhenotypeRestriction:" + hasPhenotypeRestriction); // Iterate over all variants in active Projects to see if they meet the restriction criteria for (Long vId : activeProjectsVariantIds) { Collection<Variant2VariantOverlap> infos = new ArrayList<>(); if (hasOverlapRestriction) { infos = getOverlapsSatisfyingInitialOverlapRestriction(vId, overlapRestriction1, overlapFilter.getOverlapProjectIds()); if (infos.size() == 0) { NumericValue numeric1 = (NumericValue) overlapRestriction1.getValue(); Integer value1 = numeric1.getValue(); // if this is a less than restriction we have to include overlaps of 0, entries of which do not // exist in the Variant2SpecialVariantOverlap table if (overlapRestriction1.getOperator().equals(Operator.NUMERIC_LESS_OR_EQUAL)) { Collection<Variant2VariantOverlap> allInfos = variant2SpecialVariantOverlapDao .loadByVariantId(vId, overlapFilter.getOverlapProjectIds()); // Further to the comment above, if the vId's overlapinfos returned by the restriction are zero, // but it has overlapinfo's, // we want to skip it because ALL of its overlaps are not less than the restriction if (allInfos.size() != 0) { continue; } // else this vId should fall through to the bottom and get added if there are no more // restrictions } else if (overlapRestriction1.getOperator().equals(Operator.NUMERIC_GREATER_OR_EQUAL) && value1 < 1) { // edge case, all vids implicitly have an overlap of 0 with something, (unless they overlap with // every variant in the project but I am not going to worry about that) // this means that this vId is still in consideration } else { // If this is a "GREATER_THAN" and no overlaps meet the restriction, we skip this vId and // continue with the next one continue; } } } else { // get all overlaps infos = variant2SpecialVariantOverlapDao.loadByVariantId(vId, overlapFilter.getOverlapProjectIds()); if (infos.size() == 0) { if (hasSecondaryOverlapRestriction && satisfiesSecondaryOverlapRestriction(numVariantsOverlapRestriction, infos)) { // This is for the case where a vId has no overlaps, yet wants all overlaps "less then" a number // so overlaps of '0' (not stored in the database) need to be taken into account // (this behaviour is what was requested in the meeting) } else { continue; } } } // This will be the case where the user asks: show me variants that "do"/"do not" overlap with x number // of variants in DGV/DECIPHER if (hasSecondaryOverlapRestriction) { if (!satisfiesSecondaryOverlapRestriction(numVariantsOverlapRestriction, infos)) { // This variant id(vId) does not satisfy the secondary overlap restriction, continue to the next vId continue; } } // This is the case where the user wants to restrict based on the number of different support evidence if (hasTertiaryOverlapRestriction) { if (!satisfiesTertiaryOverlapRestriction(supportOfVariantsOverlapRestriction, infos)) { continue; } } if (hasPhenotypeRestriction) { Boolean satisfiesPhenotypeRestriction = false; for (Variant2VariantOverlap info : infos) { // if the overlapped variant has the specified phenotype associated with it if (overlapProjsPhenoAssociatedVariantIds.contains(info.getOverlapSpecialVariantId())) { satisfiesPhenotypeRestriction = true; // all of this 'infos' deal with the same variantId so we can break on the first one break; } } if (!satisfiesPhenotypeRestriction) { continue; } } // if we get to this point, then we know that this variantId satisfies all the restrictions variantIdsSatisfyingRestrictions.add(vId); } if (overlapFilter.getInvert() != null && overlapFilter.getInvert()) { activeProjectsVariantIds.removeAll(variantIdsSatisfyingRestrictions); return activeProjectsVariantIds; } return variantIdsSatisfyingRestrictions; } /** * Returns list of Subject and Variant IDs that satisfy the PhenotypeFilter. Get a list of Subjects first then get * the list of all the Variants for that Subject. This is done for performance reasons. {@link Bug#3892} * * @param filterConfig * @return key = {@link VariantDao#SUBJECT_IDS_KEY} and {@link VariantDao#VARIANT_IDS_KEY} */ public Map<Integer, Collection<Long>> getSubjectVariantIdsByPhenotype(PhenotypeFilterConfig filterConfig) { Collection<Subject> subjects = subjectDao.findByPhenotype(filterConfig); Collection<Variant> variants = this.getSessionFactory().getCurrentSession() .createCriteria(this.elementClass).add(Restrictions.in("subject", subjects)).list(); Collection<Long> subjectIds = new HashSet<Long>(); Collection<Long> variantIds = new HashSet<Long>(); for (Subject s : subjects) { subjectIds.add(s.getId()); } for (Variant v : variants) { variantIds.add(v.getId()); } Map<Integer, Collection<Long>> map = new HashMap<Integer, Collection<Long>>(); map.put(VariantDao.SUBJECT_IDS_KEY, subjectIds); map.put(VariantDao.VARIANT_IDS_KEY, variantIds); return map; } @Override public Collection<? extends T> load(Set<AspireDbFilterConfig> filters) throws BioMartServiceException, NeurocartaServiceException { return loadPage(0, 0, null, null, filters); } @Override @Transactional(readOnly = true) public Page<? extends T> loadPage(int offset, int limit, String sortField, String sortDirection, Set<AspireDbFilterConfig> filters) throws BioMartServiceException, NeurocartaServiceException { assert (filters != null); List<Long> variantIds = getFilteredIds(filters); List<T> variants = new ArrayList<T>(this.load(variantIds)); // List<T> variants = new ArrayList<T>( loadVariants( variantIds ) ); if (limit == 0) { limit = variants.size(); } int pageSize = Math.min(limit, variants.size()); List<T> variantPage = variants.subList(0, pageSize); return new PageBean<T>(variantPage, variantIds.size()); } private Session currentSession() { return getSession(); } @Override public Collection<String> suggestValuesForEntityProperty(@SuppressWarnings("rawtypes") Property property, SuggestionContext suggestionContext) { Session session = currentSession(); Criteria criteria = session.createCriteria(this.elementClass); if (suggestionContext.getValuePrefix() != null) { // TODO: escape certain chars String valuePrefix = suggestionContext.getValuePrefix(); String valueWildcard = "%" + valuePrefix + "%"; criteria.add(Restrictions.like(property.getName(), valueWildcard)); } criteria.setProjection(Projections.distinct(Projections.property(property.getName()))); if (suggestionContext.getActiveProjectIds() != null && !suggestionContext.getActiveProjectIds().isEmpty()) { criteria.createAlias("subject", "subject").createAlias("subject.project", "project") .add(Restrictions.in("project.id", suggestionContext.getActiveProjectIds())); } return criteria.list(); } // - use Factory pattern with registration? to map config to appropriate filter subclass // FOR NOW: use getClass private void addSingleFilter(AspireDbFilterConfig filter, Criteria criteria) { if (filter.getClass() == VariantFilterConfig.class) { VariantFilterConfig locationFilter = (VariantFilterConfig) filter; RestrictionExpression restriction = locationFilter.getRestriction(); addSingleVariantFilter(restriction, criteria); } else if (filter.getClass() == SubjectFilterConfig.class) { SubjectFilterConfig subjectFilter = (SubjectFilterConfig) filter; RestrictionExpression restrictionExpression = subjectFilter.getRestriction(); Criterion criterion = CriteriaBuilder.buildCriteriaRestriction(restrictionExpression, CriteriaBuilder.EntityType.VARIANT); criteria.createAlias("subject", "subject").createAlias("subject.labels", "subject_label", CriteriaSpecification.LEFT_JOIN); criteria.add(criterion); } } private void addSingleVariantFilter(RestrictionExpression restrictionExpression, Criteria criteria) { criteria.createAlias("location", "location").createAlias("subject", "subject"); Criterion junction = CriteriaBuilder.buildCriteriaRestriction(restrictionExpression, CriteriaBuilder.EntityType.VARIANT); criteria.add(junction); } private List<Long> findIds(AspireDbFilterConfig filter) { // for performance reasons as Criteria can be slow if (filter instanceof ProjectFilterConfig) { return this.getVariantIdsByProject((ProjectFilterConfig) filter); // Project overlap filter requires a little more data processing than the other filters and uses // precalculated database table as it doesn't quite fit the same paradigm as the other filters I am breaking // it off into its own method } else if (filter instanceof ProjectOverlapFilterConfig) { return this.getProjectOverlapVariantIds((ProjectOverlapFilterConfig) filter); } else if (filter instanceof PhenotypeFilterConfig) { List<Variant> variants = findByPhenotype((PhenotypeFilterConfig) filter); ArrayList<Long> variantIds = new ArrayList<Long>(); for (Variant v : variants) { variantIds.add(v.getId()); } return variantIds; } Session session = this.getSessionFactory().getCurrentSession(); Criteria criteria = session.createCriteria(this.elementClass); addSingleFilter(filter, criteria); criteria.setProjection(Projections.distinct(Projections.id())); return criteria.list(); } private List<Long> getVariantIdsByProject(ProjectFilterConfig filter) { Long projectId = filter.getProjectIds().iterator().next(); Query query = this.getSessionFactory().getCurrentSession() .createQuery("select v.id from Variant v left join v.subject.project p where p.id = :id"); query.setParameter("id", projectId); return query.list(); } private List<Long> getFilteredIds(Set<AspireDbFilterConfig> filters) { Iterator<AspireDbFilterConfig> iterator = filters.iterator(); AspireDbFilterConfig filterConfig = iterator.next(); // First iteration List<Long> variantIds = findIds(filterConfig); while (iterator.hasNext()) { filterConfig = iterator.next(); List<Long> ids = findIds(filterConfig); // intersect results variantIds.retainAll(ids); // if size is 0 -> stop if (variantIds.isEmpty()) { break; } } return variantIds; } /** * This restriction is of the type: * "Show me variants that overlap/mutually overlap by less/greater than x bases/percentage" find overlaps for a * specific variant Id that meet this restriction Note that the functionality requested was that in the case of a * "LESS THAN" ALL of a variants overlaps must meet this restriction and we return all of its overlaps however in * the case of "GREATER THAN" we only return the overlaps that meet this restriction. (we don't require that "ALL OF * THE VARIANTS MUST MEET THE RESTRICTION) so this means that it will either be all overlaps returned or no overlaps * * @param vId * @param overlapRestriction * @param overlapProjectIds * @return */ private Collection<Variant2VariantOverlap> getOverlapsSatisfyingInitialOverlapRestriction(Long vId, SimpleRestriction overlapRestriction, Collection<Long> overlapProjectIds) { Collection<Variant2VariantOverlap> allOverlaps = variant2SpecialVariantOverlapDao.loadByVariantId(vId, overlapProjectIds); Collection<Variant2VariantOverlap> overlapsMeetingRestriction = variant2SpecialVariantOverlapDao .loadByVariantIdAndOverlap(vId, overlapRestriction, overlapProjectIds); if (overlapRestriction.getOperator().equals(Operator.NUMERIC_LESS_OR_EQUAL) && allOverlaps.size() == overlapsMeetingRestriction.size()) { // we could also have returned overlapsMeetingRestriction as they are identical sets return allOverlaps; } if (overlapRestriction.getOperator().equals(Operator.NUMERIC_GREATER_OR_EQUAL)) { return overlapsMeetingRestriction; } return new ArrayList<Variant2VariantOverlap>(); } private List<Long> getPhenoAssociatedVariantIdsForProjects(ProjectOverlapFilterConfig overlapFilter) { List<Long> ids = new ArrayList<Long>(); PhenotypeFilterConfig phenConfig = new PhenotypeFilterConfig(); phenConfig.setRestriction(overlapFilter.getPhenotypeRestriction()); phenConfig.setActiveProjectIds(overlapFilter.getOverlapProjectIds()); Set<AspireDbFilterConfig> phenFilterSet = new HashSet<AspireDbFilterConfig>(); phenFilterSet.add(phenConfig); try { StopWatch timer = new StopWatch(); timer.start(); log.info("fetching phenotype associated variant ids for overlapped projects"); ids = getFilteredIds(phenFilterSet); if (timer.getTime() > 100) { log.info("fetching phenotype associated variant ids for overlapped projects took " + timer.getTime() + "ms"); } } catch (Exception e) { log.error("exception while getting projectOverlapIds for phenotype"); } return ids; } private List<Long> getVariantIdsForProjects(Collection<Long> projectIds) { ProjectFilterConfig projectFilterConfig = new ProjectFilterConfig(); projectFilterConfig.setProjectIds(projectIds); Set<AspireDbFilterConfig> filterSet = new HashSet<AspireDbFilterConfig>(); filterSet.add(projectFilterConfig); List<Long> projectsVariantIds = new ArrayList<Long>(); try { // get the variantIds of all the variants in the activeProjects to iterate over later and search for overlap projectsVariantIds = getFilteredIds(filterSet); } catch (Exception e) { log.error("exception while getting projectOverlapIds"); } return projectsVariantIds; } // This will be the case where the user asks: show me variants that "do"/"do not" overlap with x number of variants // in DGV/DECIPHER private Boolean satisfiesSecondaryOverlapRestriction(SimpleRestriction overlapRestriction, Collection<Variant2VariantOverlap> overlaps) { Operator o = overlapRestriction.getOperator(); NumericValue numeric = (NumericValue) overlapRestriction.getValue(); Integer value = numeric.getValue(); if (o.equals(Operator.NUMERIC_GREATER_OR_EQUAL)) { return overlaps.size() >= value; } else if (o.equals(Operator.NUMERIC_LESS_OR_EQUAL)) { return overlaps.size() <= value; } return false; } private Boolean satisfiesTertiaryOverlapRestriction(SimpleRestriction overlapRestriction, Collection<Variant2VariantOverlap> overlaps) { // If this is a less than, we probably have to take into account the the variants with 0 overlap???? Set<String> supportSet = new HashSet<String>(); // check and warn if (overlaps.iterator().hasNext()) { Long projectId = overlaps.iterator().next().getOverlapProjectId(); if (projectDao.getOverlapProjectVariantSupportCharacteristicKey(projectId) == null) { log.warn("Support key is null for overlap project '" + projectDao.load(projectId).getName() + "'"); } } // note all of these overlaps are associated with the same variantId for (Variant2VariantOverlap overlap : overlaps) { Variant v = load(overlap.getOverlapSpecialVariantId()); String supportKey = projectDao .getOverlapProjectVariantSupportCharacteristicKey(overlap.getOverlapProjectId()); for (Characteristic c : v.getCharacteristics()) { if (c.getKey().equals(supportKey) && c.getValue() != null) { supportSet.add(c.getValue()); } } } Operator o = overlapRestriction.getOperator(); NumericValue numeric = (NumericValue) overlapRestriction.getValue(); Integer value = numeric.getValue(); if (o.equals(Operator.NUMERIC_GREATER_OR_EQUAL)) { return supportSet.size() >= value; } else if (o.equals(Operator.NUMERIC_LESS_OR_EQUAL)) { return supportSet.size() <= value; } return false; } private Boolean validateOverlapRestriction(SimpleRestriction r) { // TODO test other things like value and type, discern if it is percentage or number of bases if (r == null || r.getValue() == null) { return false; } NumericValue numeric = (NumericValue) r.getValue(); Integer value = numeric.getValue(); return value != null && value >= 0 && r.getOperator() != null; } }