blob: c29cc624d9189cc8991fce893a8fdac04e5b9bd0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2017 THALES GLOBAL SERVICES and others.
* 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.common.tools.api.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.sirius.common.tools.internal.util.FastInverseCrossReferencesList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* {@link ECrossReferenceAdapter} that provides the capability to resolve all proxy cross reference to a given resource.
*
* @noextend This class is not intended to be subclassed by clients.
* @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
*/
public class ECrossReferenceAdapterWithUnproxyCapability extends SiriusCrossReferenceAdapterImpl {
/**
* InverseCrossReferencer to allow access to {@link #removeProxies(URI)} in '@link InternalCrossReferencer}.
*
* @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
*/
protected class LocalInverseCrossReferencer extends InverseCrossReferencer {
private static final long serialVersionUID = 1L;
@Override
protected List<EObject> removeProxies(URI uri) {
return super.removeProxies(uri);
}
/**
* Check if the proxyMap is null or not. Since change in ECrossReferenceAdapter (bugzilla 400891), the proxyMap
* is no longer used if the resolve() method returns true. In this case, we must iterate on all crossReferences
* to retrieve corresponding proxies.
*
* @return true if the proxy map is null.
*/
public boolean isNullMapProxy() {
return proxyMap == null;
}
/**
* Get all proxy {@link EObject EObjects} that have URI corresponding to the <code>resourceURI</code>. Warning:
* this map is computed at each call and can be costly.
*
* @param resourceURI
* The URI of the resource for which we want to get the proxy EObjects.
* @return map of proxies with the URI as key.
*/
public Map<URI, List<EObject>> getProxiesOf(URI resourceURI) {
Map<URI, List<EObject>> result = Maps.newHashMap();
if (resourceURI != null) {
for (Iterator<EObject> iterator = keySet().iterator(); iterator.hasNext(); /* */) {
EObject eObject = iterator.next();
if (eObject.eIsProxy()) {
URI keyURI = EcoreUtil.getURI(eObject);
if (resourceURI.equals(keyURI.trimFragment())) {
List<EObject> correspondingEObjects = result.get(keyURI);
if (correspondingEObjects == null) {
correspondingEObjects = Lists.newArrayList();
}
correspondingEObjects.add(eObject);
result.put(keyURI, correspondingEObjects);
}
}
}
}
return result;
}
@Override
protected Collection<EStructuralFeature.Setting> newCollection() {
return new FastInverseCrossReferencesList(() -> !ECrossReferenceAdapterWithUnproxyCapability.this.settingTargets || ECrossReferenceAdapterWithUnproxyCapability.this.resolve());
}
}
@Override
protected InverseCrossReferencer createInverseCrossReferencer() {
return new LocalInverseCrossReferencer();
}
/**
* Look at all EObjects of the specified resource and resolve proxy cross reference that reference these
* EObjects.<BR>
* A part of {@link #resolveAll(EObject)} has been duplicated to avoid the time consumption of accessing to
* resourceURI for each objects of the same resource.
*
* @param resource
* Each cross reference pointing to a proxy of this <code>resource</code> will be resolved.
*/
public void resolveProxyCrossReferences(Resource resource) {
if (resource != null) {
URI resourceURI = resource.getURI();
if (resourceURI != null) {
ResourceSet resourceSet = resource.getResourceSet();
if (resourceSet != null) {
resourceURI = resourceSet.getURIConverter().normalize(resourceURI);
}
}
final Iterator<EObject> it = resource.getAllContents();
// Since change in ECrossReferenceAdapter (bugzilla 400891),
// the proxyMap is no longer used if the resolve() method
// returns true. In this case, we must iterate on all
// crossReferences to retrieve corresponding proxies.
if (((LocalInverseCrossReferencer) inverseCrossReferencer).isNullMapProxy()) {
// Get proxies of this resource
Map<URI, List<EObject>> proxies = ((LocalInverseCrossReferencer) inverseCrossReferencer).getProxiesOf(resourceURI);
// Iterate on all EObject of the resource to find proxy cross
// references and resolve them.
while (it.hasNext()) {
EObject eObject = it.next();
URI eObjectURI = getURI(eObject, resource, resourceURI);
resolveProxyCrossReferences(eObject, resource, proxies.get(eObjectURI));
}
} else {
// Code used for Juno
while (it.hasNext()) {
EObject eObject = it.next();
URI eObjectURI = getURI(eObject, resource, resourceURI);
List<EObject> proxies = ((LocalInverseCrossReferencer) inverseCrossReferencer).removeProxies(eObjectURI);
resolveProxyCrossReferences(eObject, resource, proxies);
}
}
}
}
/**
* Get URI of this <code>eObject</code>.
*
* @param eObject
* Concerned {@link EObject}
* @param resource
* Resource containing the <code>eObject</code>
* @param resourceURI
* URI of <code>resource</code>
*
* @return the URI of this <code>eObject</code>.
*/
private URI getURI(EObject eObject, Resource resource, URI resourceURI) {
URI eObjectURI;
if (resourceURI != null) {
eObjectURI = resourceURI.appendFragment(resource.getURIFragment(eObject));
} else {
eObjectURI = URI.createHierarchicalURI(null, null, resource.getURIFragment(eObject));
}
return eObjectURI;
}
/**
* Resolve the proxy cross references of the current <code>eObject</code>.
*
* @param eObject
* Current EObject (not a proxy)
* @param resource
* Resource containing the <code>eObject</code>
* @param proxies
* Corresponding proxies of eObject
*/
private void resolveProxyCrossReferences(EObject eObject, Resource resource, List<EObject> proxies) {
if (proxies != null) {
for (int i = 0, size = proxies.size(); i < size; ++i) {
EObject proxy = proxies.get(i);
for (EStructuralFeature.Setting setting : getInverseReferences(proxy, false)) {
resolveProxy(resource, eObject, proxy, setting);
}
}
}
}
}