/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.ui.service.population.internal; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.ui.PlatformUI; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.ChildContext; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.impl.TypeEntityDefinition; import eu.esdihumboldt.hale.common.instance.model.DataSet; import eu.esdihumboldt.hale.common.instance.model.Instance; import eu.esdihumboldt.hale.common.instance.model.InstanceCollection; import eu.esdihumboldt.hale.common.instance.model.ResourceIterator; import eu.esdihumboldt.hale.common.instance.model.TypeFilter; import eu.esdihumboldt.hale.common.schema.SchemaSpaceID; import eu.esdihumboldt.hale.common.service.helper.population.EntityPopulationCount; import eu.esdihumboldt.hale.common.service.helper.population.IPopulationUpdater; import eu.esdihumboldt.hale.ui.common.service.population.Population; import eu.esdihumboldt.hale.ui.common.service.population.PopulationService; import eu.esdihumboldt.hale.ui.common.service.population.impl.AbstractPopulationService; import eu.esdihumboldt.hale.ui.service.entity.EntityDefinitionService; import eu.esdihumboldt.hale.ui.service.entity.EntityDefinitionServiceListener; import eu.esdihumboldt.hale.ui.service.instance.InstanceService; import eu.esdihumboldt.hale.ui.service.instance.InstanceServiceAdapter; /** * Service that stores information about population count. * * @author Simon Templer */ public class PopulationServiceImpl extends AbstractPopulationService implements IPopulationUpdater { private static final Population NO_POPULATION = new Population() { @Override public int getParentsCount() { return 0; } @Override public int getOverallCount() { return 0; } }; private static final Population UNKNOWN_POPULATION = new Population() { @Override public int getParentsCount() { return Population.UNKNOWN; } @Override public int getOverallCount() { return Population.UNKNOWN; } }; private final Map<EntityDefinition, PopulationImpl> sourcePopulation = new HashMap<EntityDefinition, PopulationImpl>(); private final Map<EntityDefinition, PopulationImpl> targetPopulation = new HashMap<EntityDefinition, PopulationImpl>(); private final EntityDefinitionService entityDefinitionService; private static EntityPopulationCount populationCount; /** * Create a population service instance. * * @param instanceService the instance service */ public PopulationServiceImpl(final InstanceService instanceService) { entityDefinitionService = PlatformUI.getWorkbench() .getService(EntityDefinitionService.class); entityDefinitionService.addListener(new EntityDefinitionServiceListener() { @Override public void contextsAdded(Iterable<EntityDefinition> contextEntities) { for (EntityDefinition ed : contextEntities) { contextAdded(ed); } } @Override public void contextRemoved(EntityDefinition contextEntity) { // Not needed } @Override public void contextAdded(EntityDefinition contextEntity) { // Target contexts should have unknown counts, as they cannot be // determined based on the instances alone. if (contextEntity.getSchemaSpace() != SchemaSpaceID.TARGET) { // go through instances to determine occurring values // if population is already counted before for given Entity, // then no need to count it again Population population = null; synchronized (PopulationServiceImpl.this) { population = sourcePopulation.get(contextEntity); } if (population == null) { Job job = new PopulationCountJob(contextEntity); job.schedule(); } } } }); instanceService.addListener(new InstanceServiceAdapter() { @Override public void datasetChanged(DataSet type) { SchemaSpaceID ssid; switch (type) { case TRANSFORMED: ssid = SchemaSpaceID.TARGET; break; case SOURCE: default: ssid = SchemaSpaceID.SOURCE; } // two possibilities // 1 - data was added /* * XXX this is currently handled in StoreInstancesJob and * OrientInstanceSink, to prevent reading the whole data again * from the database, just for determining the population. (If * this would be done, it could be for instance in a job) An * event is fired nonetheless at this point, to trigger an * update. */ // 2 - data was cleared // purge the corresponding population InstanceCollection instances = instanceService.getInstances(type); if (instances.isEmpty()) { synchronized (PopulationServiceImpl.this) { switch (ssid) { case TARGET: targetPopulation.clear(); break; case SOURCE: default: sourcePopulation.clear(); } } } firePopulationChanged(ssid); } }); populationCount = new EntityPopulationCount(this); } /** * @see PopulationService#getPopulation(EntityDefinition) */ @Override public Population getPopulation(EntityDefinition entity) { if (entity.getSchemaSpace() == null) { // can't determine population return UNKNOWN_POPULATION; } Population population; synchronized (this) { switch (entity.getSchemaSpace()) { case TARGET: population = targetPopulation.get(entity); break; case SOURCE: default: population = sourcePopulation.get(entity); } } if (population == null) { if (AlignmentUtil.isDefaultEntity(entity)) { return NO_POPULATION; } return UNKNOWN_POPULATION; } return population; } /** * @see PopulationService#hasPopulation(SchemaSpaceID) */ @Override public boolean hasPopulation(SchemaSpaceID schemaSpace) { synchronized (this) { Map<EntityDefinition, PopulationImpl> population = (schemaSpace == SchemaSpaceID.TARGET) ? (targetPopulation) : (sourcePopulation); return !population.isEmpty(); } } /** * @see PopulationService#addToPopulation(Instance) */ @Override public void addToPopulation(Instance instance) { addToPopulation(instance, instance.getDataSet()); } /** * @see PopulationService#addToPopulation(Instance, DataSet) */ @Override public void addToPopulation(Instance instance, DataSet dataSet) { SchemaSpaceID schemaSpace; if (dataSet != null) { switch (dataSet) { case TRANSFORMED: schemaSpace = SchemaSpaceID.TARGET; break; case SOURCE: default: schemaSpace = SchemaSpaceID.SOURCE; } } else { throw new IllegalArgumentException("Invalid data set specified."); } // count for each Type definitions of instance type Collection<? extends TypeEntityDefinition> typeDefinitions = entityDefinitionService .getTypeEntities(instance.getDefinition(), schemaSpace); for (TypeEntityDefinition def : typeDefinitions) { if (def.getFilter() == null || def.getFilter().match(instance)) { increase(def, 1); populationCount.addToPopulation(instance, def); } } } /** * @see PopulationService#resetPopulation(DataSet) */ @Override public void resetPopulation(DataSet dataSet) { SchemaSpaceID schemaSpace; switch (dataSet) { case TRANSFORMED: schemaSpace = SchemaSpaceID.TARGET; break; case SOURCE: default: schemaSpace = SchemaSpaceID.SOURCE; } synchronized (this) { Map<EntityDefinition, PopulationImpl> population = (schemaSpace == SchemaSpaceID.TARGET) ? (targetPopulation) : (sourcePopulation); population.clear(); } // XXX rely on dataSetChanged events for update } /** * @see eu.esdihumboldt.hale.common.service.helper.population.IPopulationUpdater#increaseForEntity(eu.esdihumboldt.hale.common.align.model.EntityDefinition, * int) */ @Override public void increaseForEntity(EntityDefinition def, int count) { increase(def, count); } /** * @see eu.esdihumboldt.hale.common.service.helper.population.IPopulationUpdater#getChildren(eu.esdihumboldt.hale.common.align.model.EntityDefinition) */ @Override public Collection<? extends EntityDefinition> getChildren(EntityDefinition entityDef) { if (entityDef.getSchemaSpace() == SchemaSpaceID.SOURCE) return entityDefinitionService.getChildren(entityDef); else return AlignmentUtil.getChildrenWithoutContexts(entityDef); } private void addNoneToPopulation(EntityDefinition groupDef, List<ChildContext> path) { Iterable<? extends EntityDefinition> children = entityDefinitionService .getChildren(groupDef); if (children != null && children.iterator().hasNext()) { for (EntityDefinition def : children) { if (groupDef instanceof TypeEntityDefinition) path = def.getPropertyPath(); addNoneToChildren(def, path); } } else { addNoneToChildren(groupDef, path); } } private void addNoneToChildren(EntityDefinition groupDef, List<ChildContext> path) { if (path == null || path.isEmpty()) { increase(groupDef, 0); } else { List<ChildContext> subPath = null; if (path.size() > 0) { subPath = path.subList(1, path.size()); } increase(groupDef, 0); addNoneToPopulation(groupDef, subPath); } } /** * Increase the counter for the given entity per parent. * * @param entity the entity * @param values number of values */ private void increase(EntityDefinition entity, int values) { synchronized (this) { Map<EntityDefinition, PopulationImpl> population = (entity .getSchemaSpace() == SchemaSpaceID.TARGET) ? (targetPopulation) : (sourcePopulation); PopulationImpl pop = population.get(entity); if (pop == null) { pop = new PopulationImpl(values != 0 ? 1 : 0, values); population.put(entity, pop); } else { if (values != 0) pop.increaseParents(); pop.increaseOverall(values); } } } /** * Job determining the occurring values for a specific property entity. */ private class PopulationCountJob extends Job { private final EntityDefinition ccEntityDefinition; private InstanceCollection instanceCollection; /** * Create a Job to get the population of given {@link EntityDefinition} * * @param ccEntityDefinition the condition context entity definition */ public PopulationCountJob(EntityDefinition ccEntityDefinition) { this(ccEntityDefinition, null); } /** * Create a Job to get the population of given {@link EntityDefinition} * * @param ccEntityDefinition the condition context entity definition * @param instanceCollection an instance collection */ public PopulationCountJob(EntityDefinition ccEntityDefinition, InstanceCollection instanceCollection) { super("Determinining count for contexts"); this.ccEntityDefinition = ccEntityDefinition; this.instanceCollection = instanceCollection; setUser(false); } @Override protected IStatus run(IProgressMonitor monitor) { String taskName = "Check instances for condition"; monitor.beginTask(taskName, IProgressMonitor.UNKNOWN); if (this.instanceCollection == null) { InstanceService instanceService = PlatformUI.getWorkbench() .getService(InstanceService.class); // determine data set DataSet dataSet = DataSet.forSchemaSpace(ccEntityDefinition.getSchemaSpace()); this.instanceCollection = instanceService.getInstances(dataSet); } if (!instanceCollection.isEmpty()) { // only select instances of the correct type InstanceCollection instances = instanceCollection .select(new TypeFilter(ccEntityDefinition.getType())); // and apply an eventual filter if (ccEntityDefinition.getFilter() != null) { instances = instances.select(ccEntityDefinition.getFilter()); } if (instances.isEmpty()) { Map<EntityDefinition, PopulationImpl> population = (ccEntityDefinition .getSchemaSpace() == SchemaSpaceID.TARGET) ? (targetPopulation) : (sourcePopulation); PopulationImpl pop = population.get(ccEntityDefinition); if (pop == null) { population.put(ccEntityDefinition, new PopulationImpl(0, 0)); } addNoneToPopulation(ccEntityDefinition, ccEntityDefinition.getPropertyPath()); } else { // count instances ResourceIterator<Instance> it = instances.iterator(); try { while (it.hasNext()) { PopulationServiceImpl.populationCount .countPopulation(ccEntityDefinition, it.next()); } } finally { it.close(); } } } monitor.done(); firePopulationChanged(ccEntityDefinition.getSchemaSpace()); return Status.OK_STATUS; } } }