package org.activityinfo.legacy.shared.adapter; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import org.activityinfo.model.legacy.CuidAdapter; import org.activityinfo.model.resource.ResourceId; import org.activityinfo.core.shared.criteria.*; import java.util.List; import java.util.Set; /** * Created by alex on 3/15/14. */ public class CriteriaAnalysis extends CriteriaVisitor { /** * Instances must be a subclass of all of these FormClasses */ private final Set<ResourceId> classCriteria = Sets.newHashSet(); private final Set<ResourceId> parentCriteria = Sets.newHashSet(); private boolean rootOnly = false; private boolean classUnion = true; /** * Must be one of these ids */ private final Multimap<Character, Integer> ids = HashMultimap.create(); public ResourceId getParentCriteria() { return parentCriteria.iterator().next(); } public boolean isRootOnly() { return rootOnly; } @Override public void visitClassCriteria(ClassCriteria criteria) { classCriteria.add(criteria.getClassId()); } @Override public void visitInstanceIdCriteria(IdCriteria criteria) { // this is implicitly a union criteria // separate the instances out into domains for (ResourceId id : criteria.getInstanceIds()) { assert id != null : "ids cannot be null"; if (id.getDomain() != CuidAdapter.ACTIVITY_CATEGORY_DOMAIN) { ids.put(id.getDomain(), CuidAdapter.getLegacyIdFromCuid(id)); } } } @Override public void visitParentCriteria(ParentCriteria criteria) { if (criteria.selectsRoot()) { rootOnly = true; } else { parentCriteria.add(criteria.getParentId()); } } @Override public void visitIntersection(CriteriaIntersection intersection) { // A ∩ (B ∩ C) = A ∩ B ∩ C for (Criteria criteria : intersection) { criteria.accept(this); } } @Override public void visitUnion(CriteriaUnion criteriaUnion) { classUnion = true; // todo temp fix! - in general wrong approach, will work in flat case only! for (Criteria criteria : criteriaUnion.getElements()) { if (classUnion && !(criteria instanceof ClassCriteria)) { classUnion = false; } criteria.accept(this); } } public boolean isEmptySet() { if (classCriteria.size() > 1 && !classUnion) { // a single instance cannot (at this time) be a member of more than one // class, so the result of this query is logically the empty set return true; } if (parentCriteria.size() > 1 || (rootOnly && !parentCriteria.isEmpty())) { // likewise, a single instance cannot be a child of multiple parents, so // the result of this query is logically the empty set return true; } return false; } public boolean isRestrictedToSingleClass() { return classCriteria.size() == 1; } public boolean isRestrictedByUnionOfClasses() { return classUnion && !classCriteria.isEmpty(); } public boolean isRestrictedById() { return !ids.isEmpty(); } public boolean isLocationQuery() { return isRestrictedToSingleClass() && getClassRestriction().getDomain() == CuidAdapter.LOCATION_TYPE_DOMAIN; } public boolean isSiteQuery() { return isRestrictedToSingleClass() && getClassRestriction().getDomain() == CuidAdapter.ACTIVITY_DOMAIN; } public boolean isAncestorQuery() { return rootOnly || !parentCriteria.isEmpty(); } public ResourceId getClassRestriction() { return classCriteria.iterator().next(); } public Set<ResourceId> getClassCriteria() { return classCriteria; } public Multimap<Character, Integer> getIds() { return ids; } public List<Integer> getIds(char domain) { return Lists.newArrayList(ids.get(domain)); } public static CriteriaAnalysis analyze(Criteria criteria) { CriteriaAnalysis analysis = new CriteriaAnalysis(); criteria.accept(analysis); return analysis; } }