blob: 5445b87c8cf29e1f4b541b1c6c88aeebccccce2d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2017 THALES GLOBAL SERVICES.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.business.internal.session.danalysis;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.sirius.business.api.query.ResourceQuery;
import org.eclipse.sirius.common.tools.api.util.SiriusCrossReferenceAdapterImpl;
import org.eclipse.sirius.common.tools.internal.util.FastInverseCrossReferencesList;
/**
* A {@link IResourceCollector} for local {@link Resource}.
*
* @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a>
*/
public class LocalResourceCollector extends SiriusCrossReferenceAdapterImpl implements IResourceCollector {
private ResourceSet resourceSet;
private Map<Resource, Collection<Resource>> directlyReferencingResources;
private Map<Resource, Collection<Resource>> directlyReferencedResources;
private boolean initialized;
/**
* Default constructor.
*
* @param resourceSet
* the {@link ResourceSet} on which to listens {@link ResourceSet#getResources()} changes
*/
public LocalResourceCollector(ResourceSet resourceSet) {
super();
this.resourceSet = resourceSet;
directlyReferencingResources = new WeakHashMap<Resource, Collection<Resource>>();
directlyReferencedResources = new WeakHashMap<Resource, Collection<Resource>>();
}
@Override
protected InverseCrossReferencer createInverseCrossReferencer() {
return new LocalInverseCrossReferencer();
}
/**
* Add a inter resource reference.
*
* @param referencingResource
* the specified referencing {@link Resource}
* @param referencedResource
* the specified referenced {@link Resource}
*/
public void addInterResourceResourceReference(Resource referencingResource, Resource referencedResource) {
if (!isAnAirdOrSrmResourceConcerned(referencingResource, referencedResource)) {
// Update referenced resources
Collection<Resource> allReferencedResourcesByResource = directlyReferencedResources.get(referencingResource);
if (allReferencedResourcesByResource == null) {
allReferencedResourcesByResource = new LinkedHashSet<Resource>();
directlyReferencedResources.put(referencingResource, allReferencedResourcesByResource);
}
allReferencedResourcesByResource.add(referencedResource);
// Update referencing resources
Collection<Resource> allReferencingResourcesByResource = directlyReferencingResources.get(referencedResource);
if (allReferencingResourcesByResource == null) {
allReferencingResourcesByResource = new LinkedHashSet<Resource>();
directlyReferencingResources.put(referencedResource, allReferencingResourcesByResource);
}
allReferencingResourcesByResource.add(referencingResource);
}
}
private boolean isAnAirdOrSrmResourceConcerned(Resource referencingResource, Resource referencedResource) {
return new ResourceQuery(referencingResource).isAirdOrSrmResource() || new ResourceQuery(referencedResource).isAirdOrSrmResource();
}
/**
* Remove new inter resource reference.
*
* @param referencingResource
* the specified referencing {@link Resource}
* @param referencedResource
* the specified referenced {@link Resource}
*/
public void removeInterResourceResourceReference(Resource referencingResource, Resource referencedResource) {
directlyReferencedResources.remove(referencingResource);
directlyReferencingResources.remove(referencedResource);
}
/**
* {@inheritDoc}
*/
@Override
public Collection<Resource> getAllReferencedResources(Resource resource) {
if (!initialized) {
resourceSet.eAdapters().add(this);
initialized = true;
}
Collection<Resource> allReferencedResources = getTransitivelyAllResoures(directlyReferencedResources, resource, Collections.<Resource> emptyList());
return allReferencedResources;
}
/**
* {@inheritDoc}
*/
@Override
public Collection<Resource> getAllReferencingResources(Resource resource) {
if (!initialized) {
resourceSet.eAdapters().add(this);
initialized = true;
}
Collection<Resource> allReferencingResources = getTransitivelyAllResoures(directlyReferencingResources, resource, Collections.<Resource> emptyList());
return allReferencingResources;
}
private Collection<Resource> getTransitivelyAllResoures(Map<Resource, Collection<Resource>> map, Resource resource, Collection<Resource> resourcesAlreadyVisited) {
Collection<Resource> allTransitiveResources = new LinkedHashSet<Resource>(resourcesAlreadyVisited);
Collection<Resource> transitiveResources = map.get(resource);
if (transitiveResources != null) {
allTransitiveResources.addAll(transitiveResources);
for (Resource transitiveResource : transitiveResources) {
if (!resourcesAlreadyVisited.contains(transitiveResource)) {
allTransitiveResources.addAll(getTransitivelyAllResoures(map, transitiveResource, allTransitiveResources));
}
}
}
return allTransitiveResources;
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
if (initialized) {
resourceSet.eAdapters().remove(this);
initialized = false;
}
resourceSet = null;
directlyReferencingResources = null;
directlyReferencedResources = null;
}
class LocalInverseCrossReferencer extends InverseCrossReferencer {
private static final long serialVersionUID = 1L;
/**
* A Map for each Resource to have all resource externally referenced EObjects through resource local EObject
* through which EReferences .
*/
private Map<Resource, Map<EObject, Map<EObject, EStructuralFeature>>> resourcesRefs = new WeakHashMap<Resource, Map<EObject, Map<EObject, EStructuralFeature>>>();
@Override
protected void add(InternalEObject eObject, EReference eReference, EObject crossReferencedEObject) {
super.add(eObject, eReference, crossReferencedEObject);
Resource referencingResource = eObject.eResource();
Resource referencedResource = crossReferencedEObject.eResource();
if (referencingResource != null && referencedResource != null && referencingResource != referencedResource) {
if (!isAnAirdOrSrmResourceConcerned(referencingResource, referencedResource)) {
Map<EObject, Map<EObject, EStructuralFeature>> referencedEObjects = resourcesRefs.get(referencingResource);
if (referencedEObjects == null) {
referencedEObjects = new LinkedHashMap<EObject, Map<EObject, EStructuralFeature>>();
resourcesRefs.put(referencingResource, referencedEObjects);
}
Map<EObject, EStructuralFeature> settings = referencedEObjects.get(crossReferencedEObject);
if (settings == null) {
settings = new LinkedHashMap<EObject, EStructuralFeature>();
referencedEObjects.put(crossReferencedEObject, settings);
}
Setting setting = eObject.eSetting(eReference);
// Does not add
// EcorePackage.Literals.ETYPED_ELEMENT__EGENERIC_TYPE
// reference because it is not removed from crossReference
if (setting.getEStructuralFeature() != EcorePackage.Literals.EGENERIC_TYPE__ECLASSIFIER) {
settings.put(setting.getEObject(), setting.getEStructuralFeature());
}
addInterResourceResourceReference(referencingResource, referencedResource);
}
}
}
@Override
public void remove(EObject eObject, EReference eReference, EObject crossReferencedEObject) {
super.remove(eObject, eReference, crossReferencedEObject);
Resource referencingResource = eObject.eResource();
Resource referencedResource = crossReferencedEObject.eResource();
if (referencingResource != null && referencedResource != null && referencingResource != referencedResource) {
if (!isAnAirdOrSrmResourceConcerned(referencingResource, referencedResource)) {
removeInMap(referencedResource, referencingResource, eObject, crossReferencedEObject);
}
}
}
private void removeInMap(Resource referencedResource, Resource referencingResource, EObject eObject, EObject crossReferencedEObject) {
Map<EObject, Map<EObject, EStructuralFeature>> referencedEObjects = resourcesRefs.get(referencingResource);
if (referencedEObjects != null) {
Map<EObject, EStructuralFeature> settings = referencedEObjects.get(crossReferencedEObject);
if (settings != null) {
settings.remove(eObject);
}
if (settings == null || settings.isEmpty()) {
referencedEObjects.remove(crossReferencedEObject);
if (referencedEObjects.isEmpty()) {
resourcesRefs.remove(referencingResource);
removeInterResourceResourceReference(referencingResource, referencedResource);
}
}
}
}
@Override
protected Collection<EStructuralFeature.Setting> newCollection() {
return new FastInverseCrossReferencesList(() -> !LocalResourceCollector.this.settingTargets || LocalResourceCollector.this.resolve());
}
}
}