blob: 59f012d545f4efe0e5cc893905b5bef44a368ccf [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2015 CEA LIST.
*
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST) - bug 496176
*****************************************************************************/
package org.eclipse.papyrus.interoperability.common.transformation;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.interoperability.common.MigrationParameters.AdvancedConfig;
import org.eclipse.papyrus.interoperability.common.MigrationParameters.MappingParameters;
import org.eclipse.papyrus.interoperability.common.MigrationParameters.MigrationParametersFactory;
import org.eclipse.papyrus.interoperability.common.MigrationParameters.ThreadConfig;
import org.eclipse.papyrus.interoperability.common.MigrationParameters.URIMapping;
public abstract class AbstractDependencyAnalysisHelper implements IDependencyAnalysisHelper {
protected final ThreadConfig config;
protected final Set<String> softwareExtensions;
protected final String softwareProfileExtension;
// ResourceSet used to load and explore Static Libraries
// protected final ResourceSet localResourceSet = new ThreadSafeResourceSet();
protected final ResourceSet localResourceSet = new ResourceSetImpl();
// Store the broken URIs without trying to resolve them. We don't have enough information to resolve them during the first phase of the model import
// The Key is the resource URI, the value is the Set of each individual EObject Fragment (We need the EObject fragments to find potential matches)
protected final Map<URI, Set<String>> brokenUris = new HashMap<URI, Set<String>>();
protected final Set<URI> brokenProfiles = new HashSet<URI>();
public AbstractDependencyAnalysisHelper(ThreadConfig config, Set<String> softwareExtensions, String softwareProfileExtension) {
this.config = config;
this.softwareExtensions = softwareExtensions;
this.softwareProfileExtension = softwareProfileExtension;
configureResourceSet();
}
protected void configureResourceSet() {
localResourceSet.getLoadOptions().put(XMLResource.OPTION_DEFER_ATTACHMENT, true);
localResourceSet.getLoadOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true);
localResourceSet.getLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, Boolean.TRUE);
localResourceSet.getLoadOptions().put(XMLResource.OPTION_USE_PACKAGE_NS_URI_AS_LOCATION, Boolean.FALSE);
}
protected void unloadResourceSet() {
EMFHelper.unload(localResourceSet);
}
public void computeURIMappings(Collection<Resource> sourceModels) {
for (Resource sourceModel : sourceModels) {
doComputeURIMappings(sourceModel);
}
}
public synchronized void resolveAllMappings(Map<URI, URI> urisToReplace, Map<URI, URI> profileUrisToReplace) {
if (config instanceof AdvancedConfig) {
if (((AdvancedConfig) config).getMappingParameters() == null) {
((AdvancedConfig) config).setMappingParameters(MigrationParametersFactory.eINSTANCE.createMappingParameters());
}
try {
for (Entry<URI, Set<String>> resourceToRepair : brokenUris.entrySet()) {
// Already known mapping
if (urisToReplace.containsKey(resourceToRepair.getKey())) {
continue;
}
findMatch(resourceToRepair.getKey(), resourceToRepair.getValue());
}
for (URI profileDefinition : brokenProfiles) {
// Already known mapping
if (profileUrisToReplace.containsKey(profileDefinition.trimFragment().trimQuery())) {
continue;
}
findMatch(profileDefinition);
}
} finally {
unloadResourceSet();
brokenUris.clear();
brokenProfiles.clear();
}
}
}
protected void findMatch(URI resourceURI, Set<String> fragments) {
if (config instanceof AdvancedConfig) {
URIMapping mapping = null;
for (String fragment : fragments) {
URI eObjectURI = resourceURI.appendFragment(fragment);
mapping = findExistingMapping(eObjectURI, localResourceSet);
if (mapping != null) {
break;
}
}
if (mapping == null) {
mapping = MigrationParametersFactory.eINSTANCE.createURIMapping();
mapping.setSourceURI(resourceURI.toString());
mapping.setTargetURI(mapping.getSourceURI());
}
((AdvancedConfig) config).getMappingParameters().getUriMappings().add(mapping);
}
}
protected void findMatch(URI profileDefinitionURI) {
if (this.config instanceof AdvancedConfig) {
URIMapping match = findExistingProfileMapping(profileDefinitionURI, localResourceSet);
if (match == null) {
match = MigrationParametersFactory.eINSTANCE.createURIMapping();
match.setSourceURI(profileDefinitionURI.trimFragment().trimQuery().toString());
match.setTargetURI(match.getSourceURI());
}
((AdvancedConfig) config).getMappingParameters().getProfileUriMappings().add(match);
}
}
protected void doComputeURIMappings(Resource sourceModel) {
doComputeProfileURIMappings(sourceModel);
TreeIterator<EObject> resourceContents = sourceModel.getAllContents();
ResourceSet resourceSet = sourceModel.getResourceSet();
while (resourceContents.hasNext()) {
EObject next = resourceContents.next();
for (EReference reference : next.eClass().getEAllReferences()) {
if (reference.isContainer() || reference.isContainment() || reference.isDerived() || reference.isTransient()) {
continue;
}
Object value = next.eGet(reference, false);
if (value instanceof EObject) {
handleURIMapping((EObject) value, resourceSet);
} else if (value instanceof Collection<?>) {
for (Object element : (Collection<?>) value) {
if (element instanceof EObject) {
handleURIMapping((EObject) element, resourceSet);
}
}
}
}
}
}
protected void doComputeProfileURIMappings(Resource sourceModel) {
ResourceSet resourceSet = sourceModel.getResourceSet();
for (EObject rootObject : sourceModel.getContents()) {
if (isInvalidStereotypeApplication(rootObject)) {
handleProfileURIMapping(rootObject, resourceSet);
}
}
}
protected abstract boolean isInvalidStereotypeApplication(EObject eObject);
protected boolean isSoftwareModelElement(EObject eObject) {
return isSoftwareModelElement(EcoreUtil.getURI(eObject));
}
protected boolean isSoftwareModelElement(URI objectURI) {
String fileExtension = objectURI.fileExtension();
return softwareExtensions.contains(fileExtension) || softwareProfileExtension.equals(fileExtension);
}
protected synchronized Set<String> getFragments(URI resourceURI) {
if (!brokenUris.containsKey(resourceURI)) {
brokenUris.put(resourceURI, new HashSet<String>());
}
return brokenUris.get(resourceURI);
}
protected synchronized void handleBrokenReference(EObject proxy) {
URI proxyURI = EcoreUtil.getURI(proxy);
URI resourceURI = proxyURI.trimFragment().trimQuery();
String fragment = proxyURI.fragment();
Set<String> fragments = getFragments(resourceURI);
fragments.add(fragment);
}
protected synchronized void addBrokenProfileDefinition(URI packageURI) {
brokenProfiles.add(packageURI);
}
protected void handleProfileURIMapping(EObject stereotypeApplication, ResourceSet resourceSet) {
EPackage profileDefinition = stereotypeApplication.eClass().getEPackage();
URI packageURI = EcoreUtil.getURI(profileDefinition);
if (packageURI.trimFragment().isEmpty()) {
packageURI = URI.createURI(profileDefinition.getNsURI());
}
addBrokenProfileDefinition(packageURI);
}
protected void handleURIMapping(EObject eObject, ResourceSet resourceSet) {
if (isSoftwareModelElement(eObject)) {
handleBrokenReference(eObject);
return;
}
if (eObject.eIsProxy()) {
eObject = EcoreUtil.resolve(eObject, resourceSet);
if (eObject.eIsProxy()) {
handleBrokenReference(eObject);
return;
}
}
}
protected URIMapping findExistingProfileMapping(URI profileDefinitionURI, ResourceSet resourceSet) {
throw new UnsupportedOperationException(); // TODO I don't know how to implements it for Rpy Import
}
protected boolean isPathFragment(URI proxyURI) {
String uriFragment = proxyURI.fragment();
return uriFragment != null && uriFragment.charAt(0) == '/';
}
protected URIMapping findExistingMapping(URI proxyURI, ResourceSet resourceSet) {
throw new UnsupportedOperationException();// TODO required for RSA, but I don't know yet how to implements if for Rpy Import
}
/** Propagates the URI Mappings to all duplicates */
public void propagateURIMappings(List<URIMapping> allMappings, MappingParameters result) {
for (URIMapping mapping : allMappings) {
for (URIMapping uriMapping : result.getUriMappings()) {
if (uriMapping.getSourceURI().equals(mapping.getSourceURI())) {
uriMapping.setTargetURI(mapping.getTargetURI());
}
}
for (URIMapping profileURIMapping : result.getProfileUriMappings()) {
if (profileURIMapping.getSourceURI().equals(mapping.getSourceURI())) {
profileURIMapping.setTargetURI(mapping.getTargetURI());
}
}
}
}
public List<URIMapping> flattenURIMappings(MappingParameters result) {
List<URIMapping> allMappings = new LinkedList<URIMapping>();
allMappings.addAll(result.getUriMappings());
allMappings.addAll(result.getProfileUriMappings());
removeDuplicates(allMappings);
return allMappings;
}
/**
* Remove duplicate mappings. Mappings are duplicate if they have the same SourceURI.
* Less specific mappings will be discarded (Usually, the ones with the same Source and Target URI)
*/
protected void removeDuplicates(List<URIMapping> allMappings) {
List<URIMapping> mappingsCopy = new LinkedList<URIMapping>(allMappings);
for (URIMapping mapping : mappingsCopy) {
for (URIMapping m : allMappings) {
if (m == mapping) {
continue;
}
// This is a duplicate
if (mapping.getSourceURI().equals(m.getSourceURI())) {
// If both mappings are still present, remove one of them
if (allMappings.contains(mapping) && allMappings.contains(m)) {
URIMapping mappingToRemove = findLessSpecificMapping(mapping, m);
allMappings.remove(mappingToRemove);
break;
}
}
}
}
}
/**
* If 2 mappings have the same sourceURI but different targetURI, returns the less pertinent one
* (Usually, the one with the same Source and Target)
*
* @param mapping1
* @param mapping2
* @return
*/
protected URIMapping findLessSpecificMapping(URIMapping mapping1, URIMapping mapping2) {
if (!isUsefulMapping(mapping1)) {
return mapping1;
}
return mapping2;
}
protected boolean isUsefulMapping(URIMapping mapping) {
if (mapping.getTargetURI() == null || "".equals(mapping.getTargetURI()) || mapping.getTargetURI().equals(mapping.getSourceURI())) {
return false;
}
return true;
}
}