| /***************************************************************************** |
| * 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; |
| } |
| } |