blob: 139f7b6485338b3de9bdd8a7186b3ed076855ae3 [file] [log] [blame]
package org.eclipse.emf.emfstore.standalone.ui;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.emfstore.client.ui.dialogs.merge.DecisionManager;
import org.eclipse.emf.emfstore.client.ui.dialogs.merge.MergeWizard;
import org.eclipse.emf.emfstore.common.model.ModelElementId;
import org.eclipse.emf.emfstore.common.model.Project;
import org.eclipse.emf.emfstore.common.model.impl.ProjectImpl;
import org.eclipse.emf.emfstore.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.common.model.util.SerializationException;
import org.eclipse.emf.emfstore.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.server.model.versioning.PrimaryVersionSpec;
import org.eclipse.emf.emfstore.server.model.versioning.VersioningFactory;
import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.standalone.core.artifacts.Artifact;
import org.eclipse.emf.emfstore.standalone.core.artifacts.ArtifactRegistry;
import org.eclipse.emf.emfstore.standalone.core.util.FileUtil;
import org.eclipse.emf.emfstore.standalone.core.vcs.IVCSProvider;
import org.eclipse.emf.emfstore.standalone.core.workspace.ResourceFactoryRegistry;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.xml.sax.InputSource;
public class ArtifactMerger {
private IResource resource;
// private Project project;
// private XMIResource historyResource;
// private final XMIResource xmiResource;
private IVCSProvider provider;
private Artifact artifact;
public ArtifactMerger(IVCSProvider provider, IResource resource,
Artifact artifact) {
this.resource = resource;
this.artifact = artifact;
// this.xmiResource = artifact.getModelResource();
// this.historyResource = artifact.getHistoryResource();
// this.project = artifact.getCollection();
this.provider = provider;
}
public void merge() {
artifact.getProject().getChangeNotifier().disableNotifications(true);
try {
// restore project state
String artifactFileContent = provider.getMyRevision(
FileUtil.getFile(artifact.getModelResource().getURI()), new NullProgressMonitor());
artifact.setProject(stringToEObjectIntoProject(artifactFileContent));
writeToXMIResource(artifact.getProject().getModelElements(), artifact.getModelResource());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SerializationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
List theirOperations = provider.getTheirOperations(resource, new NullProgressMonitor());
List myOperations = provider.getMyOperations(resource, new NullProgressMonitor());
List<AbstractOperation> mergeResult = callMergeDialog(myOperations, theirOperations);
// if mergeResult is empty, file have been auto-merged
try {
Artifact artifact = ArtifactRegistry.getInstance().getArtifact(ResourceFactoryRegistry.CURRENT_URI);
applyMergeResult(myOperations, theirOperations, mergeResult);
// resolve conflict on ecore
IFile artifactFile = FileUtil.getFile(artifact.getModelResource().getURI());
provider.resolveConflict(artifactFile);
artifact.getHistoryResource().getContents().clear();
artifact.getHistoryResource().getContents().addAll(mergeResult);
artifact.getHistoryResource().save(null);
provider.resolveConflict(FileUtil.getHistoryFile(artifactFile));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
artifact.getProject().getChangeNotifier().disableNotifications(false);
}
/**
* Applies a list of operations to the project. The change tracking is
* stopped and the operations are added to the projectspace.
*
* @see #applyOperationsWithRecording(List, boolean)
* @param operations
* list of operations
* @param addOperation
* true if operation should be saved in project space.
*/
public void applyOperations(List<AbstractOperation> operations) {
// TODO: API
// ((ProjectImpl) artifact.getProject()).getChangeNotifier().disableNotifications(true);
// try {
for (AbstractOperation operation : operations) {
operation.apply(artifact.getProject());
}
// } finally {
// ((ProjectImpl) project).getChangeNotifier().disableNotifications(false);
// }
}
private ChangePackage createChangePackage(List<AbstractOperation> operations) {
ChangePackage changePackage = VersioningFactory.eINSTANCE.createChangePackage();
for (EObject o : operations) {
AbstractOperation copy = (AbstractOperation) EcoreUtil.copy(o);
changePackage.getOperations().add(copy);
}
return changePackage;
}
private List<AbstractOperation> callMergeDialog(List<AbstractOperation> myOperations,
List<AbstractOperation> theirOperations) {
ChangePackage myChangePackage = createChangePackage(myOperations);
ChangePackage theirChangePackage = createChangePackage(theirOperations);
PrimaryVersionSpec dummy = VersioningFactory.eINSTANCE.createPrimaryVersionSpec();
DecisionManager mgr = new DecisionManager(artifact.getProject(),
myChangePackage, Collections.singletonList(theirChangePackage),
dummy, dummy);
mgr.calcResult();
List<AbstractOperation> mergeResult = new ArrayList<AbstractOperation>();
if (mgr.getConflicts().size() > 0 ) {
MergeWizard mergeWizard = new MergeWizard(mgr);
WizardDialog mergeDialog = new WizardDialog(Display.getCurrent().getActiveShell(), mergeWizard);
mergeDialog.create();
// TODO: handle cancel case
mergeDialog.open();
// generate merge result and apply to local workspace
List<AbstractOperation> acceptedMine = mgr.getAcceptedMine();
List<AbstractOperation> rejectedTheirs = mgr.getRejectedTheirs();
for (AbstractOperation operationToReverse : rejectedTheirs) {
mergeResult.add(0, operationToReverse.reverse());
}
mergeResult.addAll(acceptedMine);
}
return mergeResult;
}
/**
* Applies the given merge result upon the given {@link Artifact}.
* @param artifact the {@link Artifact} upon which to apply the merge result
* @param myOperations the local operations
* @param theirOperations the incoming operations
* @param mergeResult the merge result as determined by the user
* @throws IOException
*/
private void applyMergeResult(List<AbstractOperation> myOperations,
List<AbstractOperation> theirOperations, List<AbstractOperation> mergeResult)
throws IOException {
ChangePackage myChangePackage = createChangePackage(myOperations);
ChangePackage theirChangePackage = createChangePackage(theirOperations);
// revert our changes
revert(myChangePackage.getOperations());
applyOperations(theirChangePackage.getOperations());
applyOperations(mergeResult);
// write merge result back to model resource
// List<EObject> modelElements = ModelUtil.clone(project.getModelElements());
try {
writeToXMIResource(mergeResult, artifact.getHistoryResource());
} catch (IOException e) {
throw new IOException(String.format("Can not write merge result {0} to history resource {1}. Reason: {2}",
mergeResult, artifact.getHistoryResource(), e.getMessage()));
}
try {
writeToXMIResource(artifact.getProject().getModelElements(), artifact.getModelResource());
} catch (IOException e) {
throw new IOException(String.format("Can not write model elements {0} to resource {1}. Reason: {2}",
artifact.getProject().getModelElements(), artifact.getModelResource(), e.getMessage()));
}
}
/**
* Reverts the
* @param operations
*/
public void revert(List<AbstractOperation> operations) {
while (!operations.isEmpty()) {
AbstractOperation lastOperation = operations
.get(operations.size() - 1);
((ProjectImpl) artifact.getProject()).getChangeNotifier().disableNotifications(true);
try {
lastOperation.reverse().apply(artifact.getProject());
} finally {
((ProjectImpl) artifact.getProject()).getChangeNotifier().disableNotifications(false);
}
operations.remove(lastOperation);
}
}
/**
* Write
* @param modelElements
* @param resource
* @throws IOException
*/
private void writeToXMIResource(List<? extends EObject> modelElements, XMIResource resource) throws IOException {
// resource.getContents().s
// TODO: will be slow
// int index = 0;
for (EObject me : modelElements) {
List<EObject> allContainedModelElements = ModelUtil.getAllContainedModelElementsAsList(me, false);
allContainedModelElements.add(me);
EObject copiedElement = EcoreUtil.copy(me);
List<EObject> copiedAllContainedModelElements = ModelUtil.getAllContainedModelElementsAsList(copiedElement, false);
copiedAllContainedModelElements.add(copiedElement);
resource.getContents().add(copiedElement);
for (int i = 0; i < allContainedModelElements.size(); i++) {
EObject child = allContainedModelElements.get(i);
EObject copiedChild = copiedAllContainedModelElements.get(i);
ModelElementId modelElementId = artifact.getProject().getModelElementId(child);
if (modelElementId == null) {
modelElementId = artifact.getProject().getDeletedModelElementId(child);
}
if (modelElementId != null) {
resource.setID(copiedChild, modelElementId.getId());
}
}
}
for (int i = 0; i < modelElements.size(); i++) {
resource.getContents().remove(0);
}
resource.save(null);
}
/**
* Converts a {@link String} to an {@link EObject}. <b>Note</b>:
* {@link String} must be the result of
* {@link ModelUtil#eObjectToString(EObject)}
*
* @param object
* the {@link String} representation of the {@link EObject}
* @return the deserialized {@link EObject}
* @throws SerializationException
* if deserialization fails
*/
public static Project stringToEObjectIntoProject(String object)
throws SerializationException {
if (object == null) {
return null;
}
XMIResource res = (XMIResource) (new ResourceSetImpl())
.createResource(ModelUtil.VIRTUAL_URI);
try {
res.load(new InputSource(new StringReader(object)),
null);
} catch (UnsupportedEncodingException e) {
throw new SerializationException(e);
} catch (IOException e) {
throw new SerializationException(e);
}
EObject result = res.getContents().get(0);
if (result instanceof Project) {
return (Project) result;
}
// TODO: init
Project project = null;
try {
project = new ProjectImpl(res);
} catch (IOException e) {
// TODO Auto-generated catch block
// Do NOT catch all Exceptions ("catch (Exception e)")
// Log AND handle Exceptions if possible
//
// You can just uncomment one of the lines below to log an
// exception:
// logException will show the logged excpetion to the user
// ModelUtil.logException(e);
// ModelUtil.logException("YOUR MESSAGE HERE", e);
// logWarning will only add the message to the error log
// ModelUtil.logWarning("YOUR MESSAGE HERE", e);
// ModelUtil.logWarning("YOUR MESSAGE HERE");
//
// If handling is not possible declare and rethrow Exception
e.printStackTrace();
}
return project;
}
}