blob: 740ea082f33be642ee1c9ae7e76def3f2c0f7d2c [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2014-2017 itemis and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* itemis - Initial API and implementation
* itemis - [510664] Model unloading throws exceptions for derived references
* itemis - [510864] unresolveUnreachableCrossProjectReferencesInModel changes order in lists
* itemis - [510849] modelDescriptor.getEditingDomain().yield() in DefaultUnresolveUnreachableCrossProjectReferencesOperation
* Siemens - [574930] Model load manager extension
*
* </copyright>
*/
package org.eclipse.sphinx.emf.workspace.loading.operations;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.emf.common.util.BasicEList.UnmodifiableEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.Transaction;
import org.eclipse.osgi.util.NLS;
import org.eclipse.sphinx.emf.model.IModelDescriptor;
import org.eclipse.sphinx.emf.model.ModelDescriptorRegistry;
import org.eclipse.sphinx.emf.util.EObjectUtil;
import org.eclipse.sphinx.emf.util.WorkspaceTransactionUtil;
import org.eclipse.sphinx.emf.workspace.Activator;
import org.eclipse.sphinx.emf.workspace.internal.messages.Messages;
import org.eclipse.sphinx.emf.workspace.loading.SchedulingRuleFactory;
import org.eclipse.sphinx.platform.operations.AbstractWorkspaceOperation;
import org.eclipse.sphinx.platform.util.PlatformLogUtil;
public class DefaultUnresolveUnreachableCrossProjectReferencesOperation extends AbstractWorkspaceOperation
implements IUnresolveUnreachableCrossProjectReferencesOperation {
private Collection<IProject> projectsWithUnreachableCrossRefrencesToUnresolve;
public DefaultUnresolveUnreachableCrossProjectReferencesOperation(Collection<IProject> projectsWithUnreachableCrossRefrencesToUnresolve) {
super(Messages.job_unresolvingUnreachableCrossProjectReferences);
this.projectsWithUnreachableCrossRefrencesToUnresolve = projectsWithUnreachableCrossRefrencesToUnresolve;
}
@Override
public ISchedulingRule getRule() {
return new SchedulingRuleFactory().createLoadSchedulingRule(projectsWithUnreachableCrossRefrencesToUnresolve, false);
}
/*
* @see org.eclipse.sphinx.emf.workspace.loading.operations.IUnresolveUnreachableCrossProjectReferencesOperation#
* getProjectsWithUnreachableCrossRefrencesToUnresolve()
*/
@Override
public Collection<IProject> getProjectsWithUnreachableCrossRefrencesToUnresolve() {
return projectsWithUnreachableCrossRefrencesToUnresolve;
}
@Override
public void run(IProgressMonitor monitor) throws CoreException {
runUnresolveUnreachableCrossProjectReferences(projectsWithUnreachableCrossRefrencesToUnresolve, monitor);
}
protected void runUnresolveUnreachableCrossProjectReferences(final Collection<IProject> projects, IProgressMonitor monitor)
throws OperationCanceledException {
Assert.isNotNull(projects);
for (IProject project : projects) {
Collection<IModelDescriptor> modelsInProject = ModelDescriptorRegistry.INSTANCE.getModels(project);
SubMonitor progress = SubMonitor.convert(monitor,
NLS.bind(Messages.task_unresolvingUnreachableCrossProjectReferencesInProject, project.getName()), modelsInProject.size());
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
for (IModelDescriptor modelDescriptor : modelsInProject) {
unresolveUnreachableCrossProjectReferencesInModel(modelDescriptor, progress.newChild(1));
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
}
}
}
protected void unresolveUnreachableCrossProjectReferencesInModel(final IModelDescriptor modelDescriptor, final IProgressMonitor monitor)
throws OperationCanceledException {
Assert.isNotNull(modelDescriptor);
Runnable runnable = new Runnable() {
@Override
public void run() {
Collection<Resource> resources = modelDescriptor.getLoadedResources(true);
SubMonitor progress = SubMonitor.convert(monitor, resources.size());
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
for (Resource resource : resources) {
progress.subTask(NLS.bind(Messages.subtask_unresolvingUnreachableCrossProjectReferencesInResource, resource.getURI()));
try {
TreeIterator<EObject> allContents = resource.getAllContents();
while (allContents.hasNext()) {
EObject object = allContents.next();
for (EReference reference : object.eClass().getEAllReferences()) {
if (!reference.isContainment() && !reference.isContainer() && reference.isChangeable() && !reference.isDerived()) {
if (reference.isMany()) {
@SuppressWarnings("unchecked")
EList<EObject> referencedObjects = (EList<EObject>) object.eGet(reference);
// Be sure that referenced objects are not contained in an unmodifiable list
if (!(referencedObjects instanceof UnmodifiableEList<?>)) {
List<EObject> safeReferencedObjects = new ArrayList<EObject>(referencedObjects);
for (EObject referencedObject : safeReferencedObjects) {
if (referencedObject != null && !referencedObject.eIsProxy()) {
// Referenced object no longer part of same model as given object?
if (!modelDescriptor.getScope().belongsTo(referencedObject.eResource(), true)) {
int idx = referencedObjects.indexOf(referencedObject);
referencedObjects.set(idx, EObjectUtil.createProxyFrom(referencedObject, object.eResource()));
}
}
}
}
} else {
EObject referencedObject = (EObject) object.eGet(reference);
if (referencedObject != null && !referencedObject.eIsProxy()) {
// Referenced object no longer part of same model as given object?
if (!modelDescriptor.getScope().belongsTo(referencedObject.eResource(), true)) {
object.eSet(reference, EObjectUtil.createProxyFrom(referencedObject, object.eResource()));
}
}
}
}
}
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
}
} catch (Exception ex) {
PlatformLogUtil.logAsWarning(Activator.getPlugin(), ex);
}
progress.worked(1);
progress.subTask(""); //$NON-NLS-1$
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
}
}
};
try {
/*
* !! Important note !! There seems to be a highly critical bug in EMF Transaction: We have observed that
* when a write transaction and a read transaction are started at the same time on the same editing domain
* in two different threads, the editing domain is accessed concurrently but not exclusively by both
* transactions. As a consequence, they both try to start and shut down the editing domain's change recorder
* which may lead to unpredictable results ranging from unexpected rollbacks to deadlocks. As a workaround,
* we run the write transaction with a OPTION_NO_UNDO which avoids that the write transaction accesses the
* editing domain's change recorder. We would do so even in absence of this potential EMF Transaction bug
* because we don't want enable users to undo the unresolving of unreachable cross project references
* anyway.
*/
// FIXME File bug to EMF Transaction: Editing domain accessed concurrently when a write transaction and a
// read transaction are started at the same time by two different threads
IOperationHistory operationHistory = WorkspaceTransactionUtil.getOperationHistory(modelDescriptor.getEditingDomain());
Map<String, Object> options = WorkspaceTransactionUtil.getDefaultTransactionOptions();
options.put(Transaction.OPTION_NO_UNDO, Boolean.TRUE);
WorkspaceTransactionUtil.executeInWriteTransaction(modelDescriptor.getEditingDomain(), runnable,
Messages.operation_unresolvingUnreachableCrossProjectReferencesInModel, operationHistory, options);
} catch (ExecutionException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
}
}
}