blob: ca13d9583698cd8e168a53056272a7a9a370d19e [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2015 itemis 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:
* itemis - Initial API and implementation
* itemis - [474952] Replication of Ancestor Feature filters to much
*
* </copyright>
*/
package org.eclipse.sphinx.emf.splitting;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.sphinx.emf.resource.ModelResourceDescriptor;
public class ModelSplitProcessor {
protected static class AncestorCopier extends EcoreUtil.Copier {
private static final long serialVersionUID = 1L;
protected EObject ancestor;
protected IModelSplitDirective modelSplitDirective;
public AncestorCopier(EObject ancestor, IModelSplitDirective modelSplitDirective) {
Assert.isNotNull(ancestor);
Assert.isNotNull(modelSplitDirective);
this.ancestor = ancestor;
this.modelSplitDirective = modelSplitDirective;
}
@Override
public EObject copy(EObject eObject) {
if (eObject == null) {
return null;
}
EObject copiedEObject = createCopy(eObject);
if (copiedEObject != null) {
put(eObject, copiedEObject);
for (EStructuralFeature eStructuralFeature : eObject.eClass().getEAllStructuralFeatures()) {
if (eStructuralFeature.isChangeable() && !eStructuralFeature.isDerived()) {
if (eObject != ancestor || modelSplitDirective.shouldReplicateAncestorFeature(eObject, eStructuralFeature)) {
if (eStructuralFeature instanceof EAttribute) {
EAttribute eAttribute = (EAttribute) eStructuralFeature;
copyAttribute(eAttribute, eObject, copiedEObject);
} else {
EReference eReference = (EReference) eStructuralFeature;
if (eReference.isContainment()) {
copyContainment(eReference, eObject, copiedEObject);
}
}
}
}
}
copyProxyURI(eObject, copiedEObject);
}
return copiedEObject;
}
@Override
protected void copyFeatureMap(FeatureMap featureMap) {
for (int i = 0, size = featureMap.size(); i < size; ++i) {
EStructuralFeature feature = featureMap.getEStructuralFeature(i);
if (feature instanceof EReference && ((EReference) feature).isContainment()) {
if (modelSplitDirective.shouldReplicateAncestorFeature(((FeatureMap.Internal) featureMap).getEObject(), feature)) {
Object value = featureMap.getValue(i);
if (value != null) {
copy((EObject) value);
}
}
}
}
}
}
protected IModelSplitPolicy modelSplitPolicy;
private Collection<Resource> resourcesToSplit;
private Collection<EObject> eObjectsToSplit;
private Map<EObject, Map<URI, EObject>> originalToSplitEObjectsMap = new HashMap<EObject, Map<URI, EObject>>();
private Map<EObject, EObject> eObjectToOriginalContainerMap = new HashMap<EObject, EObject>();
private Map<URI, List<EObject>> targetResourceURIToContentsMap = new HashMap<URI, List<EObject>>();
public ModelSplitProcessor(IModelSplitPolicy modelSplitPolicy) {
Assert.isNotNull(modelSplitPolicy);
this.modelSplitPolicy = modelSplitPolicy;
}
protected EObject getSplitEObject(EObject originalEObject, URI targetResourceURI) {
Map<URI, EObject> targetResourceURIToSplitEObjectsMap = originalToSplitEObjectsMap.get(originalEObject);
if (targetResourceURIToSplitEObjectsMap != null) {
return targetResourceURIToSplitEObjectsMap.get(targetResourceURI);
}
return null;
}
protected void addSplitEObject(EObject originalEObject, EObject splitEObject, URI targetResourceURI) {
Map<URI, EObject> targetResourceURIToSplitEObjectsMap = originalToSplitEObjectsMap.get(originalEObject);
if (targetResourceURIToSplitEObjectsMap == null) {
targetResourceURIToSplitEObjectsMap = new HashMap<URI, EObject>();
originalToSplitEObjectsMap.put(originalEObject, targetResourceURIToSplitEObjectsMap);
}
targetResourceURIToSplitEObjectsMap.put(targetResourceURI, splitEObject);
}
protected void addOriginalContainer(EObject eObject, EObject originalContainer) {
eObjectToOriginalContainerMap.put(eObject, originalContainer);
}
protected EObject getOriginalContainer(EObject eObject) {
EObject originalContainer = eObjectToOriginalContainerMap.get(eObject);
return originalContainer != null ? originalContainer : ((InternalEObject) eObject).eInternalContainer();
}
protected List<EObject> getTargetResourceContents(URI targetResourceURI) {
List<EObject> targetResourceContents = targetResourceURIToContentsMap.get(targetResourceURI);
if (targetResourceContents == null) {
targetResourceContents = new ArrayList<EObject>();
targetResourceURIToContentsMap.put(targetResourceURI, targetResourceContents);
}
return targetResourceContents;
}
public Collection<Resource> getResourcesToSplit() {
if (resourcesToSplit == null) {
resourcesToSplit = new ArrayList<Resource>();
}
return resourcesToSplit;
}
public Collection<EObject> getEObjectsToSplit() {
if (eObjectsToSplit == null) {
eObjectsToSplit = new ArrayList<EObject>();
}
return eObjectsToSplit;
}
public void run(IProgressMonitor monitor) {
Collection<Resource> resourcesToSplit = getResourcesToSplit();
Collection<EObject> eObjectsToSplit = getEObjectsToSplit();
SubMonitor progress = SubMonitor.convert(monitor, resourcesToSplit.size() + eObjectsToSplit.size());
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
splitResources(resourcesToSplit, progress.newChild(resourcesToSplit.size()));
splitEObjects(eObjectsToSplit, progress.newChild(eObjectsToSplit.size()));
}
protected void splitResources(Collection<Resource> resources, IProgressMonitor monitor) {
Assert.isNotNull(resources);
SubMonitor progress = SubMonitor.convert(monitor, resources.size());
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
for (Resource resource : resources) {
splitEObjects(resource.getContents(), progress.newChild(1));
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
}
}
protected void splitEObjects(Collection<EObject> eObjects, IProgressMonitor monitor) {
Assert.isNotNull(eObjects);
SubMonitor progress = SubMonitor.convert(monitor, 100);
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
// Traverse the model objects' contents, present each model object to split policy and collect resulting split
// directives
List<IModelSplitDirective> directives = collectSplitDirectives(eObjects, progress.newChild(25));
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
// Process split directives and build split model content
processSplitDirectives(directives, progress.newChild(75));
}
public Map<URI, List<EObject>> getSplitModelContents() {
return Collections.unmodifiableMap(targetResourceURIToContentsMap);
}
public Collection<ModelResourceDescriptor> getSplitResourceDescriptors() {
List<ModelResourceDescriptor> descriptors = new ArrayList<ModelResourceDescriptor>(targetResourceURIToContentsMap.keySet().size());
for (URI uri : targetResourceURIToContentsMap.keySet()) {
List<EObject> contents = targetResourceURIToContentsMap.get(uri);
if (contents != null && !contents.isEmpty()) {
String contentTypeId = modelSplitPolicy.getContentTypeId(contents);
descriptors.add(new ModelResourceDescriptor(uri, contentTypeId, contents));
}
}
return Collections.unmodifiableList(descriptors);
}
protected List<IModelSplitDirective> collectSplitDirectives(Collection<EObject> eObjects, IProgressMonitor monitor) {
Assert.isNotNull(eObjects);
SubMonitor progress = SubMonitor.convert(monitor, 2 * eObjects.size());
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
// Determine split directives for given model objects as well as for all model objects that are directly and
// indirectly contained by the former
List<IModelSplitDirective> directives = new ArrayList<IModelSplitDirective>();
for (EObject eObject : eObjects) {
for (TreeIterator<EObject> iterator = eObject.eAllContents(); iterator.hasNext();) {
IModelSplitDirective directive = modelSplitPolicy.getSplitDirective(iterator.next());
if (directive != null) {
directives.add(directive);
}
}
progress.worked(1);
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
}
return directives;
}
protected void processSplitDirectives(List<IModelSplitDirective> directives, IProgressMonitor monitor) {
Assert.isNotNull(directives);
SubMonitor progress = SubMonitor.convert(monitor, directives.size());
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
for (IModelSplitDirective directive : directives) {
if (directive.isValid()) {
EObject eObject = directive.getEObject();
URI targetResourceURI = directive.getTargetResourceURI();
// Split given model object by just moving (rather than copying) it to the contents of the intended
// target resource and keep track of its original container
addSplitEObject(eObject, eObject, targetResourceURI);
addOriginalContainer(eObject, eObject.eContainer());
// Retrieve ancestor object branch - either up to first ancestor object that has already been split or
// entirely if none of them has
/*
* !! Important Note !! Always walk up to original container to avoid that subsequent splitting of
* ancestor objects is made from already split and therefore incomplete ancestor objects.
*/
List<EObject> ancestors = new ArrayList<EObject>();
for (EObject container = ((InternalEObject) eObject).eInternalContainer(); container != null; container = getOriginalContainer(
container)) {
// Use Setting
ancestors.add(container);
// Abort traversal if current ancestor object has already been split
if (getSplitEObject(container, targetResourceURI) != null) {
break;
}
}
// Split ancestor object branch as far as not already done so
EObject lastEObject = eObject;
EObject lastSplitEObject = eObject;
boolean newSplitAncestor = true;
for (EObject ancestor : ancestors) {
EObject splitAncestor = null;
// Split current ancestor object if not already done so
splitAncestor = getSplitEObject(ancestor, targetResourceURI);
if (splitAncestor == null) {
splitAncestor = copyAncestor(ancestor, directive);
addSplitEObject(ancestor, splitAncestor, targetResourceURI);
// Root container reached?
if (((InternalEObject) ancestor).eInternalContainer() == null) {
// Add split ancestor object as root container to the contents of the intended target
// resource
List<EObject> targetResourceContents = getTargetResourceContents(targetResourceURI);
targetResourceContents.add(splitAncestor);
}
} else {
newSplitAncestor = false;
}
// Connect split ancestor object to previously split ancestor or model object
EStructuralFeature containingFeature = lastEObject.eContainingFeature();
if (containingFeature == null) {
throw new RuntimeException("Containing feature of '" + lastEObject + "' not found"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (containingFeature.isMany()) {
@SuppressWarnings("unchecked")
List<Object> values = (List<Object>) splitAncestor.eGet(containingFeature);
values.add(lastSplitEObject);
} else {
splitAncestor.eSet(containingFeature, lastSplitEObject);
}
// Abort splitting of ancestor object branch when having reached at an already split ancestor object
if (!newSplitAncestor) {
break;
}
lastEObject = ancestor;
lastSplitEObject = splitAncestor;
}
}
progress.worked(1);
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
}
}
protected <T extends EObject> T copyAncestor(T ancestor, IModelSplitDirective directive) {
AncestorCopier copier = new AncestorCopier(ancestor, directive);
EObject copiedAncestor = copier.copy(ancestor);
copier.copyReferences();
@SuppressWarnings("unchecked")
T t = (T) copiedAncestor;
return t;
}
public void dispose() {
originalToSplitEObjectsMap.clear();
eObjectToOriginalContainerMap.clear();
targetResourceURIToContentsMap.clear();
}
}