blob: 74d9d65c15b7a5a52b7b7866b23de9a89b9163c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* 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:
* Otto von Wesendonk, Edgar Mueller, Maximilian Koegel - initial API and implementation
* Johannes Faltermeier - adaptions for independent storage
******************************************************************************/
package org.eclipse.emf.emfstore.internal.client.model.impl;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.commons.io.FileUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
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.ECrossReferenceAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil.UsageCrossReferencer;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.emfstore.client.ESUsersession;
import org.eclipse.emf.emfstore.client.callbacks.ESCommitCallback;
import org.eclipse.emf.emfstore.client.callbacks.ESUpdateCallback;
import org.eclipse.emf.emfstore.client.changetracking.ESCommandStack;
import org.eclipse.emf.emfstore.client.handler.ESRunnableContext;
import org.eclipse.emf.emfstore.client.observer.ESLoginObserver;
import org.eclipse.emf.emfstore.client.observer.ESMergeObserver;
import org.eclipse.emf.emfstore.client.util.ESClientURIUtil;
import org.eclipse.emf.emfstore.client.util.ESVoidCallable;
import org.eclipse.emf.emfstore.client.util.RunESCommand;
import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionElement;
import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionPoint;
import org.eclipse.emf.emfstore.internal.client.importexport.impl.ExportChangesController;
import org.eclipse.emf.emfstore.internal.client.importexport.impl.ExportProjectController;
import org.eclipse.emf.emfstore.internal.client.model.CompositeOperationHandle;
import org.eclipse.emf.emfstore.internal.client.model.Configuration;
import org.eclipse.emf.emfstore.internal.client.model.ESWorkspaceProviderImpl;
import org.eclipse.emf.emfstore.internal.client.model.ProjectSpace;
import org.eclipse.emf.emfstore.internal.client.model.Usersession;
import org.eclipse.emf.emfstore.internal.client.model.changeTracking.merging.ConflictResolver;
import org.eclipse.emf.emfstore.internal.client.model.changeTracking.notification.recording.NotificationRecorder;
import org.eclipse.emf.emfstore.internal.client.model.connectionmanager.ConnectionManager;
import org.eclipse.emf.emfstore.internal.client.model.connectionmanager.ServerCall;
import org.eclipse.emf.emfstore.internal.client.model.controller.CommitController;
import org.eclipse.emf.emfstore.internal.client.model.controller.ShareController;
import org.eclipse.emf.emfstore.internal.client.model.controller.UpdateController;
import org.eclipse.emf.emfstore.internal.client.model.exceptions.ChangeConflictException;
import org.eclipse.emf.emfstore.internal.client.model.exceptions.IllegalProjectSpaceStateException;
import org.eclipse.emf.emfstore.internal.client.model.exceptions.MEUrlResolutionException;
import org.eclipse.emf.emfstore.internal.client.model.exceptions.PropertyNotFoundException;
import org.eclipse.emf.emfstore.internal.client.model.filetransfer.FileDownloadStatus;
import org.eclipse.emf.emfstore.internal.client.model.filetransfer.FileInformation;
import org.eclipse.emf.emfstore.internal.client.model.filetransfer.FileTransferManager;
import org.eclipse.emf.emfstore.internal.client.model.impl.api.ESLocalProjectImpl;
import org.eclipse.emf.emfstore.internal.client.model.util.WorkspaceUtil;
import org.eclipse.emf.emfstore.internal.client.observers.DeleteProjectSpaceObserver;
import org.eclipse.emf.emfstore.internal.client.properties.PropertyManager;
import org.eclipse.emf.emfstore.internal.common.ESDisposable;
import org.eclipse.emf.emfstore.internal.common.ExtensionRegistry;
import org.eclipse.emf.emfstore.internal.common.model.ModelElementId;
import org.eclipse.emf.emfstore.internal.common.model.Project;
import org.eclipse.emf.emfstore.internal.common.model.impl.IdentifiableElementImpl;
import org.eclipse.emf.emfstore.internal.common.model.impl.ProjectImpl;
import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.internal.common.model.util.SerializationException;
import org.eclipse.emf.emfstore.internal.server.conflictDetection.ChangeConflictSet;
import org.eclipse.emf.emfstore.internal.server.conflictDetection.ConflictBucket;
import org.eclipse.emf.emfstore.internal.server.conflictDetection.ConflictDetector;
import org.eclipse.emf.emfstore.internal.server.exceptions.FileTransferException;
import org.eclipse.emf.emfstore.internal.server.exceptions.InvalidVersionSpecException;
import org.eclipse.emf.emfstore.internal.server.model.FileIdentifier;
import org.eclipse.emf.emfstore.internal.server.model.ProjectInfo;
import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACUser;
import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.OrgUnitProperty;
import org.eclipse.emf.emfstore.internal.server.model.url.ModelElementUrlFragment;
import org.eclipse.emf.emfstore.internal.server.model.versioning.AbstractChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.BranchInfo;
import org.eclipse.emf.emfstore.internal.server.model.versioning.BranchVersionSpec;
import org.eclipse.emf.emfstore.internal.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.FileBasedChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.LogMessage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.PrimaryVersionSpec;
import org.eclipse.emf.emfstore.internal.server.model.versioning.TagVersionSpec;
import org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec;
import org.eclipse.emf.emfstore.internal.server.model.versioning.VersioningFactory;
import org.eclipse.emf.emfstore.internal.server.model.versioning.Versions;
import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.FileBasedChangePackageImpl;
import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent.ChangePackageContainer;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CreateDeleteOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.util.ChangePackageUtil;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.util.OperationUtil;
import org.eclipse.emf.emfstore.server.ESCloseableIterable;
import org.eclipse.emf.emfstore.server.exceptions.ESException;
import org.eclipse.emf.emfstore.server.model.ESChangePackage;
/**
* Project space base class that contains custom user methods.
*
* @author koegel
* @author wesendon
* @author emueller
* @author jfaltermeier
*
*/
public abstract class ProjectSpaceBase extends IdentifiableElementImpl
implements ProjectSpace, ESLoginObserver, ESDisposable, ChangePackageContainer {
private ESLocalProjectImpl esLocalProjectImpl;
private boolean initCompleted;
private boolean isTransient;
private boolean disposed;
private FileTransferManager fileTransferManager;
private OperationManager operationManager;
private PropertyManager propertyManager;
private final Map<String, OrgUnitProperty> propertyMap;
private ResourceSet resourceSet;
private ResourcePersister resourcePersister;
private ECrossReferenceAdapter crossReferenceAdapter;
private ESRunnableContext runnableContext;
/**
* Constructor.
*/
public ProjectSpaceBase() {
propertyMap = new LinkedHashMap<String, OrgUnitProperty>();
initRunnableContext();
}
/**
* <p>
* Provides a context in which a {@link Runnable} is executed.
* </p>
* <p>
* This may be used to provide a context while applying operations on a
* {@link org.eclipse.emf.emfstore.client.ESLocalProject}.
* </p>
*
* @param runnableContext
* the runnable context to be set
*/
public void setRunnableContext(ESRunnableContext runnableContext) {
this.runnableContext = runnableContext;
}
private void initRunnableContext() {
runnableContext = ExtensionRegistry.INSTANCE.get(
RUNNABLE_CONTEXT_ID,
ESRunnableContext.class,
new DefaultRunnableContext(), true);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#addFile(java.io.File)
*/
public FileIdentifier addFile(File file) throws FileTransferException {
return fileTransferManager.addFile(file);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#addFile(java.io.File, java.lang.String)
*/
public FileIdentifier addFile(File file, String fileIdentifier) throws FileTransferException {
return fileTransferManager.addFile(file, fileIdentifier);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#addOperations(java.util.List)
*/
public void addOperations(List<? extends AbstractOperation> operations) {
final List<AbstractOperation> ops = new ArrayList<AbstractOperation>();
for (final AbstractOperation operation : operations) {
ops.add(operation);
}
getLocalChangePackage().addAll(ops);
updateDirtyState();
for (final AbstractOperation op : operations) {
operationManager.notifyOperationExecuted(op);
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#addTag(org.eclipse.emf.emfstore.internal.server.model.versioning.PrimaryVersionSpec,
* org.eclipse.emf.emfstore.internal.server.model.versioning.TagVersionSpec)
*/
public void addTag(PrimaryVersionSpec versionSpec, TagVersionSpec tag) throws ESException {
final ConnectionManager cm = ESWorkspaceProviderImpl.getInstance().getConnectionManager();
cm.addTag(getUsersession().getSessionId(), getProjectId(), versionSpec, tag);
}
/**
* Helper method which applies merged changes on the ProjectSpace. This
* method is used by merge mechanisms in update as well as branch merging.
*
* @param baseSpec
* new base version
* @param incomingChangePackages
* changes from the current branch
* @param myChanges
* merged changes
* @param progressMonitor
* an {@link IProgressMonitor} to inform about the progress of the UpdateCallback in case it is called
* @param runChecksumCheckOnBaseSpec
* whether the checksum check is performed while applying the changes
*
* @throws ESException in case the checksum comparison failed and the activated IChecksumErrorHandler
* also failed
*/
public void applyChanges(PrimaryVersionSpec baseSpec, List<AbstractChangePackage> incomingChangePackages,
AbstractChangePackage myChanges, IProgressMonitor progressMonitor, boolean runChecksumCheckOnBaseSpec)
throws ESException {
// revert local changes
notifyPreRevertMyChanges(getLocalChangePackage());
revert();
notifyPostRevertMyChanges();
// apply changes from repo. incoming (aka theirs)
applyChangePackages(incomingChangePackages, false);
if (runChecksumCheckOnBaseSpec) {
runChecksumTests(baseSpec, incomingChangePackages, progressMonitor);
}
notifyPostApplyTheirChanges(incomingChangePackages);
reapplyLocalChanges(myChanges);
notifyPostApplyMergedChanges(myChanges);
setBaseVersion(baseSpec);
save();
}
private void reapplyLocalChanges(AbstractChangePackage myChangePackage) {
final ESCloseableIterable<AbstractOperation> operations = myChangePackage.operations();
try {
if (Configuration.getClientBehavior().isRerecordingActivated()) {
applyOperationsWithRerecording(operations.iterable());
} else {
applyOperations(operations.iterable(), true);
}
} finally {
operations.close();
}
}
private void runChecksumTests(PrimaryVersionSpec baseSpec, List<AbstractChangePackage> incomingChangePackages,
IProgressMonitor progressMonitor)
throws ESException {
progressMonitor.subTask(Messages.ProjectSpaceBase_Computing_Checksum);
if (!performChecksumCheck(baseSpec, getProject())) {
progressMonitor.subTask(Messages.ProjectSpaceBase_Activate_ChecksumErrorHandler_Invalid_Chekcum);
final boolean errorHandled = Configuration.getClientBehavior()
.getChecksumErrorHandler()
.execute(toAPI(), baseSpec.toAPI(), progressMonitor);
if (!errorHandled) {
// rollback
for (int i = incomingChangePackages.size() - 1; i >= 0; i--) {
final ESCloseableIterable<AbstractOperation> reversedOperations = incomingChangePackages.get(i)
.reversedOperations();
try {
applyChangePackage(reversedOperations.iterable(), false);
} finally {
reversedOperations.close();
}
}
// TODO
// applyChangePackage(getLocalChangePackage2().iterator(), true);
throw new ESException(Messages.ProjectSpaceBase_Update_Cancelled_Invalid_Checksum);
}
}
}
// FIXME: rename
private void applyChangePackage(Iterable<AbstractOperation> operations, boolean addOperations) {
applyOperations(operations, addOperations);
}
private void applyChangePackages(Iterable<AbstractChangePackage> changePackages, boolean addOperations) {
for (final AbstractChangePackage changePackage : changePackages) {
final ESCloseableIterable<AbstractOperation> operations = changePackage.operations();
try {
applyChangePackage(operations.iterable(), addOperations);
} finally {
operations.close();
}
}
}
private boolean performChecksumCheck(PrimaryVersionSpec baseVersion, Project project) {
if (Configuration.getClientBehavior().isChecksumCheckActive()) {
final long expectedChecksum = baseVersion.getProjectStateChecksum();
try {
final long computedChecksum = ModelUtil.computeChecksum(project);
return expectedChecksum == computedChecksum;
} catch (final SerializationException e) {
WorkspaceUtil.logWarning(Messages.ProjectSpaceBase_Cannot_Compute_Checksum, e);
}
}
return true;
}
/**
* Applies a list of operations to the project. The change tracking will be
* stopped meanwhile.
*
*
* @param operations
* the list of operations to be applied upon the project space
* @param addOperations
* whether the operations should be saved in project space
*
*/
public void applyOperations(Iterable<AbstractOperation> operations, boolean addOperations) {
executeRunnable(new ApplyOperationsRunnable(this, operations, addOperations));
}
/**
* Applies a list of operations to the project. The change tracking will be
* stopped meanwhile.
*
*
* @param operations
* the list of operations to be applied upon the project space
*
*/
public void applyOperationsWithRerecording(Iterable<AbstractOperation> operations) {
executeRunnable(new ApplyOperationsAndRecordRunnable(this, operations));
}
/**
* Executes a given {@link Runnable} in the context of this {@link ProjectSpace}.<br>
* The {@link Runnable} usually modifies the Project contained in the {@link ProjectSpace}.
*
* @param runnable
* the {@link Runnable} to be executed in the context of this {@link ProjectSpace}
*/
public void executeRunnable(Runnable runnable) {
getRunnableContext().executeRunnable(runnable);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#beginCompositeOperation()
*/
public CompositeOperationHandle beginCompositeOperation() {
return operationManager.beginCompositeOperation();
}
/**
* Removes the elements that are marked as cutted from the project.
*/
public void cleanCutElements() {
final List<EObject> cutElements = new ArrayList<EObject>(getProject().getCutElements());
for (final EObject cutElement : cutElements) {
getProject().deleteModelElement(cutElement);
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#commit(org.eclipse.core.runtime.IProgressMonitor)
*/
public PrimaryVersionSpec commit(IProgressMonitor monitor) throws ESException {
return new CommitController(this, null, null, monitor).execute();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#commit(java.lang.String,
* org.eclipse.emf.emfstore.client.callbacks.ESCommitCallback, org.eclipse.core.runtime.IProgressMonitor)
*/
public PrimaryVersionSpec commit(String logMessage, ESCommitCallback callback, IProgressMonitor monitor)
throws ESException {
return new CommitController(this, logMessage, callback, monitor).execute();
}
/**
* {@inheritDoc}
*/
public PrimaryVersionSpec commitToBranch(BranchVersionSpec branch, String logMessage,
ESCommitCallback callback,
IProgressMonitor monitor) throws ESException {
return new CommitController(this, branch, logMessage, callback, monitor).execute();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#exportLocalChanges(java.io.File,
* org.eclipse.core.runtime.IProgressMonitor)
*/
public void exportLocalChanges(File file, IProgressMonitor progressMonitor) throws IOException {
new ExportChangesController(this).execute(file, progressMonitor);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#exportLocalChanges(java.io.File)
*/
public void exportLocalChanges(File file) throws IOException {
new ExportChangesController(this).execute(file, new NullProgressMonitor());
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#exportProject(java.io.File,
* org.eclipse.core.runtime.IProgressMonitor)
*/
public void exportProject(File file, IProgressMonitor progressMonitor) throws IOException {
new ExportProjectController(this).execute(file, progressMonitor);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#exportProject(java.io.File)
*/
public void exportProject(File file) throws IOException {
new ExportProjectController(this).execute(file, new NullProgressMonitor());
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getChanges(org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec,
* org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec)
*/
public List<AbstractChangePackage> getChanges(VersionSpec sourceVersion, VersionSpec targetVersion)
throws InvalidVersionSpecException, ESException {
// TODO: is this a server call?
final ConnectionManager connectionManager = ESWorkspaceProviderImpl.getInstance().getConnectionManager();
final List<AbstractChangePackage> changes = connectionManager.getChanges(getUsersession().getSessionId(),
getProjectId(),
sourceVersion, targetVersion);
return changes;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getFile(org.eclipse.emf.emfstore.internal.server.model.FileIdentifier)
*/
public FileDownloadStatus getFile(FileIdentifier fileIdentifier) throws FileTransferException {
return fileTransferManager.getFile(fileIdentifier, false);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getFileInfo(org.eclipse.emf.emfstore.internal.server.model.FileIdentifier)
*/
public FileInformation getFileInfo(FileIdentifier fileIdentifier) {
return fileTransferManager.getFileInfo(fileIdentifier);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getLocalChangePackage(boolean)
*/
public AbstractChangePackage getLocalChangePackage(boolean canonize) {
final AbstractChangePackage changePackage = ChangePackageUtil.createChangePackage(
Configuration.getClientBehavior().useInMemoryChangePackage());
// copy operations from ProjectSpace
final ESCloseableIterable<AbstractOperation> operations = getLocalChangePackage().operations();
try {
for (final AbstractOperation operation : operations.iterable()) {
final AbstractOperation clonedOperation = ModelUtil.clone(operation);
changePackage.add(clonedOperation);
}
} finally {
operations.close();
}
final LogMessage logMessage = VersioningFactory.eINSTANCE.createLogMessage();
if (getUsersession() != null) {
logMessage.setAuthor(getUsersession().getUsername());
} else {
logMessage.setAuthor(Messages.ProjectSpaceBase_Unknown_Author);
}
logMessage.setClientDate(new Date());
changePackage.setLogMessage(logMessage);
return changePackage;
}
/**
* Get the current notification recorder.
*
* @return the recorder
*/
public NotificationRecorder getNotificationRecorder() {
return operationManager.getNotificationRecorder();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getOperationManager()
*/
public OperationManager getOperationManager() {
return operationManager;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getProjectInfo()
*/
public ProjectInfo getProjectInfo() {
final ProjectInfo projectInfo = org.eclipse.emf.emfstore.internal.server.model.ModelFactory.eINSTANCE
.createProjectInfo();
projectInfo.setProjectId(ModelUtil.clone(getProjectId()));
projectInfo.setName(getProjectName());
projectInfo.setDescription(getProjectDescription());
projectInfo.setVersion(ModelUtil.clone(getBaseVersion()));
return projectInfo;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getPropertyManager()
*/
public PropertyManager getPropertyManager() {
if (propertyManager == null) {
propertyManager = new PropertyManager(this);
}
return propertyManager;
}
/**
* getter for a string argument - see {@link #setProperty(OrgUnitProperty)}.
*/
private OrgUnitProperty getProperty(String name) throws PropertyNotFoundException {
// sanity checks
if (getUsersession() != null && getUsersession().getACUser() != null) {
final OrgUnitProperty orgUnitProperty = propertyMap.get(name);
if (orgUnitProperty != null) {
return orgUnitProperty;
}
}
throw new PropertyNotFoundException(MessageFormat.format(
Messages.ProjectSpaceBase_Property_Not_Found, name));
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#importLocalChanges(java.lang.String)
*/
public void importLocalChanges(String fileName) throws IOException {
final ResourceSetImpl resourceSet = new ResourceSetImpl();
final Resource resource = resourceSet.getResource(URI.createFileURI(fileName), true);
final EList<EObject> directContents = resource.getContents();
// sanity check
if (directContents.size() != 1 && !(directContents.get(0) instanceof ChangePackage)) {
throw new IOException(Messages.ProjectSpaceBase_Corrupt_File);
}
final AbstractChangePackage changePackage = (AbstractChangePackage) directContents.get(0);
if (!initCompleted) {
init();
}
final ESCloseableIterable<AbstractOperation> operations = changePackage.operations();
try {
applyOperations(operations.iterable(), true);
} finally {
operations.close();
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#init()
*/
public void init() {
initCrossReferenceAdapter();
final ESCommandStack commandStack = (ESCommandStack) ESWorkspaceProviderImpl.getInstance().getEditingDomain()
.getCommandStack();
fileTransferManager = new FileTransferManager(this);
operationManager = new OperationManager(this);
// initialization order is important!
initPropertyMap();
final URI localChangePackageUri = ESClientURIUtil.createOperationsURI(this);
final AbstractChangePackage localChangePackage = getLocalChangePackage();
if (localChangePackage == null) {
setChangePackage(createChangePackage(localChangePackageUri, true));
} else if (localChangePackage.eIsProxy()) {
setChangePackage(createChangePackage(localChangePackageUri, false));
} else {
if (!Configuration.getClientBehavior().useInMemoryChangePackage()) {
final FileBasedChangePackage changePackage = (FileBasedChangePackage) getLocalChangePackage();
if (changePackage.eResource() == eResource()) {
migrateFileBasedChangePackageIntoDedicatedResource(localChangePackageUri, changePackage);
} else {
// TODO: move to FileBasedChangePackage
try {
FileUtils.copyFile(
new File(changePackage.getFilePath()),
new File(changePackage.getTempFilePath()));
} catch (final IOException ex) {
WorkspaceUtil.logException(ex.getMessage(), ex);
}
}
}
}
initResourcePersister();
// TODO: use ObserverBug to register observers
commandStack.addCommandStackObserver(resourcePersister);
commandStack.addCommandStackObserver(operationManager);
getProject().addIdEObjectCollectionChangeObserver(operationManager);
getProject().addIdEObjectCollectionChangeObserver(resourcePersister);
if (getProject() instanceof ProjectImpl) {
((ProjectImpl) getProject()).setUndetachable(operationManager);
((ProjectImpl) getProject()).setUndetachable(resourcePersister);
}
initCompleted = true;
startChangeRecording();
cleanCutElements();
}
private AbstractChangePackage createChangePackage(URI localChangePackageUri, boolean initialize) {
AbstractChangePackage localChangePackage;
if (Configuration.getClientBehavior().useInMemoryChangePackage()) {
localChangePackage = VersioningFactory.eINSTANCE.createChangePackage();
final Resource resource = getResourceSet().createResource(localChangePackageUri);
resource.getContents().add(localChangePackage);
} else {
final URI normalizedUri = getResourceSet().getURIConverter().normalize(localChangePackageUri);
final String filePath = normalizedUri.toFileString();
localChangePackage = VersioningFactory.eINSTANCE.createFileBasedChangePackage();
if (initialize) {
((FileBasedChangePackage) localChangePackage)
.initialize(filePath + FileBasedChangePackageImpl.FILE_OP_INDEX);
} else {
((FileBasedChangePackage) localChangePackage).setFilePath(
filePath + FileBasedChangePackageImpl.FILE_OP_INDEX);
}
final Resource resource = getResourceSet().createResource(localChangePackageUri);
resource.getContents().add(localChangePackage);
try {
resource.save(ModelUtil.getResourceSaveOptions());
} catch (final IOException ex) {
WorkspaceUtil.logException(ex.getMessage(), ex);
}
}
return localChangePackage;
}
private void migrateFileBasedChangePackageIntoDedicatedResource(final URI localChangePackageUri,
final FileBasedChangePackage changePackage) {
final URI normalizedUri = getResourceSet().getURIConverter().normalize(localChangePackageUri);
final String filePath = normalizedUri.toFileString();
final String tempPath = changePackage.getTempFilePath();
final String path = changePackage.getFilePath();
RunESCommand.run(new ESVoidCallable() {
@Override
public void run() {
changePackage.setFilePath(filePath + ".1"); //$NON-NLS-1$
}
});
if (path != null) {
// temp file depends on path
try {
FileUtils.moveFile(new File(tempPath), new File(changePackage.getTempFilePath()));
FileUtils.moveFile(new File(path), new File(changePackage.getFilePath()));
} catch (final IOException ex) {
WorkspaceUtil.logException(ex.getMessage(), ex);
}
}
Resource resource = getResourceSet().getResource(localChangePackageUri, true);
if (resource == null) {
resource = getResourceSet().createResource(localChangePackageUri);
}
final Resource r = resource;
// move change package into its own resource
RunESCommand.run(new ESVoidCallable() {
@Override
public void run() {
r.getContents().add(changePackage);
setChangePackage(changePackage);
}
});
try {
eResource().save(ModelUtil.getResourceSaveOptions());
resource.save(ModelUtil.getResourceSaveOptions());
} catch (final IOException ex) {
WorkspaceUtil.logException(ex.getMessage(), ex);
}
}
@SuppressWarnings("unchecked")
private void initPropertyMap() {
// TODO: deprecated, OrgUnitPropertiy will be removed soon
if (getUsersession() != null) {
ESWorkspaceProviderImpl.getObserverBus().register(this, ESLoginObserver.class);
final ACUser acUser = getUsersession().getACUser();
if (acUser != null) {
for (final OrgUnitProperty p : acUser.getProperties()) {
if (p.getProject() != null && p.getProject().equals(getProjectId())) {
propertyMap.put(p.getName(), p);
}
}
}
}
}
private void initCrossReferenceAdapter() {
// default
boolean useCrossReferenceAdapter = true;
for (final ESExtensionElement element : new ESExtensionPoint(
"org.eclipse.emf.emfstore.client.inverseCrossReferenceCache") //$NON-NLS-1$
.getExtensionElements()) {
useCrossReferenceAdapter &= element.getBoolean("activated"); //$NON-NLS-1$
}
if (useCrossReferenceAdapter) {
crossReferenceAdapter = new ECrossReferenceAdapter();
getProject().eAdapters().add(crossReferenceAdapter);
}
}
private void initResourcePersister() {
resourcePersister = new ResourcePersister(toAPI());
if (!isTransient) {
resourcePersister.addResource(eResource());
resourcePersister.addResource(getLocalChangePackage().eResource());
resourcePersister.addResource(getProject().eResource());
resourcePersister.addDirtyStateChangeLister(new ESLocalProjectSaveStateNotifier(toAPI()));
ESWorkspaceProviderImpl.getObserverBus().register(resourcePersister);
}
}
/**
* Returns the file transfer manager.
*
* @return the file transfer manager
*/
public FileTransferManager getFileTransferManager() {
return fileTransferManager;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#initResources(org.eclipse.emf.ecore.resource.ResourceSet)
*/
public void initResources(ResourceSet resourceSet) {
this.resourceSet = resourceSet;
initCompleted = true;
final URI projectSpaceURI = ESClientURIUtil.createProjectSpaceURI(this);
final URI operationsURI = ESClientURIUtil.createOperationsURI(this);
final URI projectURI = ESClientURIUtil.createProjectURI(this);
setResourceCount(0);
final List<Resource> resources = new ArrayList<Resource>();
final Resource resource = resourceSet.createResource(projectURI);
// if resource splitting fails, we need a reference to the old resource
resource.getContents().add(getProject());
resources.add(resource);
setResourceCount(getResourceCount() + 1);
for (final EObject modelElement : getProject().getAllModelElements()) {
((XMIResource) resource).setID(modelElement, getProject().getModelElementId(modelElement).getId());
}
final Resource localChangePackageResource = resourceSet.createResource(operationsURI);
// TODO: LCP
resources.add(localChangePackageResource);
final Resource projectSpaceResource = resourceSet.createResource(projectSpaceURI);
projectSpaceResource.getContents().add(this);
resources.add(projectSpaceResource);
// save all resources that have been created
for (final Resource currentResource : resources) {
try {
ModelUtil.saveResource(currentResource, WorkspaceUtil.getResourceLogger());
} catch (final IOException e) {
WorkspaceUtil.logException(Messages.ProjectSpaceBase_Resource_Init_Failed, e);
}
}
init();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#delete(org.eclipse.core.runtime.IProgressMonitor)
*/
public void delete(IProgressMonitor monitor) throws IOException {
ESWorkspaceProviderImpl.getObserverBus().notify(DeleteProjectSpaceObserver.class).projectSpaceDeleted(this);
// delete project to notify listeners
getProject().delete();
// remove resources from resource set and delete them
deleteResource(getProject().eResource());
deleteResource(eResource());
if (FileBasedChangePackage.class.isInstance(getLocalChangePackage())) {
FileBasedChangePackageImpl.class.cast(getLocalChangePackage()).delete();
} else {
// LCP - no change package in memory anymore, hence no resource available
final URI localChangePackageUri = ESClientURIUtil.createOperationsURI(this);
final URI normalizedUri = getResourceSet().getURIConverter().normalize(localChangePackageUri);
final String fileString = normalizedUri.toFileString();
final File operationsFile = new File(fileString);
operationsFile.delete();
boolean isDeleted = !operationsFile.exists();
int retries = 0;
while (!isDeleted && retries < 3) {
operationsFile.delete();
isDeleted = !operationsFile.exists();
retries++;
}
}
// TODO: remove project space from workspace, this is not the case if delete
// is performed via Workspace#deleteProjectSpace
ESWorkspaceProviderImpl.getInstance().getInternalWorkspace().getProjectSpaces().remove(this);
dispose();
}
private void deleteResource(Resource resource) throws IOException {
if (resource != null) {
resource.delete(null);
}
}
/**
* Returns the {@link ECrossReferenceAdapter}, if available.
*
* @param modelElement
* the model element for which to find inverse cross references
*
* @return the {@link ECrossReferenceAdapter}
*/
public Collection<Setting> findInverseCrossReferences(EObject modelElement) {
if (crossReferenceAdapter != null) {
return crossReferenceAdapter.getInverseReferences(modelElement);
}
return UsageCrossReferencer.find(modelElement, resourceSet);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getResourceSet()
*/
public ResourceSet getResourceSet() {
return resourceSet;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#setResourceSet(org.eclipse.emf.ecore.resource.ResourceSet)
*/
public void setResourceSet(ResourceSet resourceSet) {
this.resourceSet = resourceSet;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#isTransient()
*/
public boolean isTransient() {
return isTransient;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#isUpdated()
*/
public boolean isUpdated() throws ESException {
final PrimaryVersionSpec headVersion = resolveVersionSpec(Versions.createHEAD(getBaseVersion()),
new NullProgressMonitor());
return getBaseVersion().equals(headVersion);
}
/**
* {@inheritDoc}
*/
public void loginCompleted(ESUsersession session) {
// TODO Implement possibility in observerbus to register only for
// certain notifier
if (getUsersession() == null || !getUsersession().toAPI().equals(session)) {
return;
}
try {
transmitProperties();
// BEGIN SUPRESS CATCH EXCEPTION
} catch (final RuntimeException e) {
// END SUPRESS CATCH EXCEPTION
WorkspaceUtil.logException(Messages.ProjectSpaceBase_Transmit_Properties_Failed, e);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#makeTransient()
*/
public void makeTransient() {
if (initCompleted) {
throw new IllegalAccessError(Messages.ProjectSpaceBase_Make_Transient_Error);
}
isTransient = true;
}
/**
* {@inheritDoc}
*/
public void mergeBranch(final PrimaryVersionSpec branchSpec, final ConflictResolver conflictResolver,
final IProgressMonitor monitor)
throws ESException {
if (branchSpec == null || conflictResolver == null) {
throw new IllegalArgumentException(Messages.ProjectSpaceBase_Arguments_Must_Not_Be_Null);
}
if (Versions.isSameBranch(getBaseVersion(), branchSpec)) {
throw new InvalidVersionSpecException(Messages.ProjectSpaceBase_Cannot_Merge_Branch_With_Itself);
}
final PrimaryVersionSpec commonAncestor = new ServerCall<PrimaryVersionSpec>(this) {
@Override
protected PrimaryVersionSpec run() throws ESException {
return resolveVersionSpec(Versions.createANCESTOR(getBaseVersion(),
branchSpec), monitor);
}
}.execute();
final List<AbstractChangePackage> baseChanges = getChanges(commonAncestor, getBaseVersion());
final List<AbstractChangePackage> branchChanges = getChanges(commonAncestor, branchSpec);
final ChangeConflictSet conflictSet = new ConflictDetector().calculateConflicts(branchChanges,
baseChanges, getProject());
if (conflictResolver.resolveConflicts(getProject(), conflictSet)) {
final AbstractChangePackage copyOfResolvedConflicts = mergeResolvedConflicts(conflictSet, branchChanges,
baseChanges);
RunESCommand.WithException.run(ESException.class, new Callable<Void>() {
public Void call() throws Exception {
applyChanges(getBaseVersion(), baseChanges, copyOfResolvedConflicts, monitor, false);
setMergedVersion(ModelUtil.clone(branchSpec));
return null;
}
});
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#mergeResolvedConflicts(org.eclipse.emf.emfstore.internal.server.conflictDetection.ChangeConflictSet,
* java.util.List, java.util.List)
*/
public AbstractChangePackage mergeResolvedConflicts(ChangeConflictSet conflictSet,
List<AbstractChangePackage> myChangePackages, List<AbstractChangePackage> theirChangePackages)
throws ChangeConflictException {
final Set<AbstractOperation> accceptedMineSet = new LinkedHashSet<AbstractOperation>();
final Set<AbstractOperation> rejectedTheirsSet = new LinkedHashSet<AbstractOperation>();
for (final ConflictBucket conflict : conflictSet.getConflictBuckets()) {
if (!conflict.isResolved()) {
throw new ChangeConflictException(
Messages.ProjectSpaceBase_Conflict_During_Update_No_Resolution,
conflictSet);
}
accceptedMineSet.addAll(conflict.getAcceptedLocalOperations());
rejectedTheirsSet.addAll(conflict.getRejectedRemoteOperations());
}
final List<AbstractOperation> acceptedMineList = new LinkedList<AbstractOperation>();
for (final AbstractChangePackage locChangePackage : myChangePackages) {
final ESCloseableIterable<AbstractOperation> operations = locChangePackage.operations();
try {
for (final AbstractOperation myOperation : operations.iterable()) {
final Set<AbstractOperation> notInvolvedInConflict = conflictSet.getNotInvolvedInConflict();
final List<AbstractOperation> ops = new ArrayList<AbstractOperation>(notInvolvedInConflict);
if (containsOp(ops, myOperation)) {
acceptedMineList.add(myOperation);
} else if (containsOp(accceptedMineSet, myOperation)) {
acceptedMineList.add(myOperation);
}
final Iterator<AbstractOperation> iterator = accceptedMineSet.iterator();
while (iterator.hasNext()) {
final AbstractOperation operation = iterator.next();
if (operation.getIdentifier().equals(myOperation.getIdentifier())) {
iterator.remove();
break;
}
}
}
} finally {
operations.close();
}
}
// add all remaining operations in acceptedMineSet (they have been generated during merge)
acceptedMineList.addAll(accceptedMineSet);
final List<AbstractOperation> rejectedTheirsList = new LinkedList<AbstractOperation>();
for (final AbstractChangePackage theirCP : theirChangePackages) {
final ESCloseableIterable<AbstractOperation> operations = theirCP.operations();
try {
for (final AbstractOperation theirOperation : operations.iterable()) {
if (containsOp(rejectedTheirsSet, theirOperation)) {
rejectedTheirsList.add(theirOperation);
}
}
} finally {
operations.close();
}
}
final List<AbstractOperation> mergeResult = new ArrayList<AbstractOperation>(rejectedTheirsList.size()
+ acceptedMineList.size());
for (final AbstractOperation operationToReverse : rejectedTheirsList) {
mergeResult.add(0, operationToReverse.reverse());
}
mergeResult.addAll(acceptedMineList);
final AbstractChangePackage result = ChangePackageUtil.createChangePackage(
Configuration.getClientBehavior().useInMemoryChangePackage());
// dup op in mergeResult
result.addAll(mergeResult);
return result;
}
// TODO LCP: do we elsewhere compare operations?
private static boolean containsOp(Collection<AbstractOperation> ops, AbstractOperation op) {
for (final AbstractOperation abstractOperation : ops) {
if (OperationUtil.isCreateDelete(abstractOperation) &&
OperationUtil.isCreateDelete(op)) {
final CreateDeleteOperation createDeleteOperation = CreateDeleteOperation.class.cast(abstractOperation);
final CreateDeleteOperation otherCreateDeleteOperation = CreateDeleteOperation.class.cast(op);
if (createDeleteOperation.getOperationId().equals(otherCreateDeleteOperation.getOperationId())
&& createDeleteOperation.getModelElementId()
.equals(otherCreateDeleteOperation.getModelElementId())) {
return true;
}
} else if (abstractOperation.getOperationId().equals(op.getOperationId())) {
return true;
}
}
return false;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#getBranches()
*/
public List<BranchInfo> getBranches() throws ESException {
return new ServerCall<List<BranchInfo>>(this) {
@Override
protected List<BranchInfo> run() throws ESException {
final ConnectionManager cm = ESWorkspaceProviderImpl.getInstance().getConnectionManager();
return cm.getBranches(getSessionId(), getProjectId());
}
}.execute();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#removeTag(org.eclipse.emf.emfstore.internal.server.model.versioning.PrimaryVersionSpec,
* org.eclipse.emf.emfstore.internal.server.model.versioning.TagVersionSpec)
*/
public void removeTag(PrimaryVersionSpec versionSpec, TagVersionSpec tag) throws ESException {
final ConnectionManager cm = ESWorkspaceProviderImpl.getInstance().getConnectionManager();
cm.removeTag(getUsersession().getSessionId(), getProjectId(), versionSpec, tag);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#resolve(org.eclipse.emf.emfstore.internal.server.model.url.ModelElementUrlFragment)
*/
public EObject resolve(ModelElementUrlFragment modelElementUrlFragment) throws MEUrlResolutionException {
final ModelElementId modelElementId = modelElementUrlFragment.getModelElementId();
final EObject modelElement = getProject().getModelElement(modelElementId);
if (modelElement == null) {
throw new MEUrlResolutionException();
}
return modelElement;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#resolveVersionSpec(org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec,
* org.eclipse.core.runtime.IProgressMonitor)
*/
public PrimaryVersionSpec resolveVersionSpec(final VersionSpec versionSpec, IProgressMonitor monitor)
throws InvalidVersionSpecException, ESException {
return new ServerCall<PrimaryVersionSpec>(this, monitor) {
@Override
protected PrimaryVersionSpec run() throws ESException {
return getConnectionManager().resolveVersionSpec(
getSessionId(),
getProjectId(),
versionSpec);
}
}.execute();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#revert()
*/
public void revert() {
while (!getLocalChangePackage().isEmpty()) {
undoLastOperation();
}
updateDirtyState();
}
/**
* Saves the project space itself only, no containment children.
*/
public void saveProjectSpaceOnly() {
saveResource(eResource());
}
/**
* Saves the project space.
*/
public void save() {
saveProjectSpaceOnly();
saveChangePackage();
resourcePersister.saveDirtyResources(true);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#hasUnsavedChanges()
*/
public boolean hasUnsavedChanges() {
if (resourcePersister != null) {
return resourcePersister.isDirty();
}
// in case the project space has not been initialized yet
return false;
}
private void saveChangePackage() {
try {
getLocalChangePackage().save();
} catch (final IOException e) {
WorkspaceUtil.logException(Messages.ProjectSpaceBase_Error_During_Save
+ Messages.ProjectSpaceBase_Delete_Project_And_Checkout_Again, e);
}
}
/**
* Save the given resource that is part of the project space resource set.
*
* @param resource
* the resource
*/
public void saveResource(Resource resource) {
try {
if (resource == null) {
if (!isTransient) {
WorkspaceUtil.logException(Messages.ProjectSpaceBase_Resource_Not_Initialized,
new IllegalProjectSpaceStateException(Messages.ProjectSpaceBase_Resource_Is_Null));
}
return;
}
ModelUtil.saveResource(resource, WorkspaceUtil.getResourceLogger());
} catch (final IOException e) {
WorkspaceUtil.logException(Messages.ProjectSpaceBase_Error_During_Save
+ Messages.ProjectSpaceBase_Delete_Project_And_Checkout_Again, e);
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#setProperty(org.eclipse.emf.emfstore.internal.server.model.accesscontrol.OrgUnitProperty)
*/
public void setProperty(OrgUnitProperty property) {
// sanity checks
if (getUsersession() != null && getUsersession().getACUser() != null) {
try {
if (property.getProject() == null) {
property.setProject(ModelUtil.clone(getProjectId()));
} else if (!property.getProject().equals(getProjectId())) {
return;
}
final OrgUnitProperty prop = getProperty(property.getName());
prop.setValue(property.getValue());
} catch (final PropertyNotFoundException e) {
getUsersession().getACUser().getProperties().add(property);
propertyMap.put(property.getName(), property);
}
// the properties that have been altered are retained in a separate
// list
for (final OrgUnitProperty changedProperty : getUsersession().getChangedProperties()) {
if (changedProperty.getName().equals(property.getName())
&& changedProperty.getProject().equals(getProjectId())) {
changedProperty.setValue(property.getValue());
ESWorkspaceProviderImpl.getInstance().getWorkspace().toInternalAPI().save();
return;
}
}
getUsersession().getChangedProperties().add(property);
ESWorkspaceProviderImpl.getInstance().getWorkspace().toInternalAPI().save();
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#shareProject(org.eclipse.core.runtime.IProgressMonitor)
*/
public ProjectInfo shareProject(IProgressMonitor monitor) throws ESException {
return shareProject(null, monitor);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#shareProject(org.eclipse.emf.emfstore.internal.client.model.Usersession,
* org.eclipse.core.runtime.IProgressMonitor)
*/
public ProjectInfo shareProject(Usersession session, IProgressMonitor monitor) throws ESException {
return new ShareController(this, session, monitor).execute();
}
/**
* Starts change recording on this workspace, resumes previous recordings if
* there are any.
*/
public void startChangeRecording() {
operationManager.startChangeRecording();
updateDirtyState();
}
/**
* Stops current recording of changes and adds recorded changes to this
* project spaces changes.
*/
public void stopChangeRecording() {
if (operationManager != null) {
operationManager.stopChangeRecording();
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#transmitProperties()
*/
public void transmitProperties() {
final List<OrgUnitProperty> temp = new ArrayList<OrgUnitProperty>();
for (final OrgUnitProperty changedProperty : getUsersession().getChangedProperties()) {
if (changedProperty.getProject() != null && changedProperty.getProject().equals(getProjectId())) {
temp.add(changedProperty);
}
}
final ListIterator<OrgUnitProperty> iterator = temp.listIterator();
while (iterator.hasNext()) {
try {
ESWorkspaceProviderImpl
.getInstance()
.getConnectionManager()
.transmitProperty(getUsersession().getSessionId(), iterator.next(), getUsersession().getACUser(),
getProjectId());
iterator.remove();
} catch (final ESException e) {
WorkspaceUtil.logException(Messages.ProjectSpaceBase_Transmission_Of_Properties_Failed, e);
}
}
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#undoLastOperation()
*/
public void undoLastOperation() {
undoLastOperations(1);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#undoLastOperation()
*/
public void undoLastOperations(int numberOfOperations) {
if (numberOfOperations <= 0) {
return;
}
if (!getLocalChangePackage().isEmpty()) {
final List<AbstractOperation> operations = getLocalChangePackage().removeAtEnd(1);
final AbstractOperation lastOperation = operations.get(0);
final AbstractOperation reversedOperation = lastOperation.reverse();
final Iterable<AbstractOperation> iterable = Collections.singletonList(reversedOperation);
applyOperations(iterable, false);
operationManager.notifyOperationUndone(lastOperation);
undoLastOperations(--numberOfOperations);
}
updateDirtyState();
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#update(org.eclipse.core.runtime.IProgressMonitor)
*/
public PrimaryVersionSpec update(IProgressMonitor monitor) throws ESException {
return update(Versions.createHEAD(getBaseVersion()), null, monitor);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#update(org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec)
*/
public PrimaryVersionSpec update(final VersionSpec version) throws ESException {
return update(version, null, null);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#update(org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec,
* org.eclipse.emf.emfstore.client.callbacks.ESUpdateCallback, org.eclipse.core.runtime.IProgressMonitor)
*/
public PrimaryVersionSpec update(VersionSpec version, ESUpdateCallback callback, IProgressMonitor progress)
throws ESException {
return new UpdateController(this, version, callback, progress).execute();
}
/**
* Updates the dirty state of the project space.
*/
public void updateDirtyState() {
boolean isEmpty = true;
final AbstractChangePackage localChangePackage = getLocalChangePackage();
if (localChangePackage == null) {
return;
}
isEmpty = localChangePackage.isEmpty();
if (isDirty() == !isEmpty) {
return;
}
setDirty(!isEmpty);
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.common.ESDisposable#dispose()
*/
@SuppressWarnings("unchecked")
public void dispose() {
if (disposed) {
return;
}
stopChangeRecording();
if (crossReferenceAdapter != null) {
getProject().eAdapters().remove(crossReferenceAdapter);
}
final ESCommandStack commandStack = (ESCommandStack) ESWorkspaceProviderImpl.getInstance().getEditingDomain()
.getCommandStack();
commandStack.removeCommandStackObserver(operationManager);
commandStack.removeCommandStackObserver(resourcePersister);
getProject().removeIdEObjectCollectionChangeObserver(operationManager);
getProject().removeIdEObjectCollectionChangeObserver(resourcePersister);
ESWorkspaceProviderImpl.getObserverBus().unregister(resourcePersister);
ESWorkspaceProviderImpl.getObserverBus().unregister(this, ESLoginObserver.class);
ESWorkspaceProviderImpl.getObserverBus().unregister(this);
operationManager.dispose();
resourcePersister.dispose();
disposed = true;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.client.model.ProjectSpace#isShared()
*/
public boolean isShared() {
return getUsersession() != null && getBaseVersion() != null;
}
private void notifyPreRevertMyChanges(final AbstractChangePackage changePackage) {
ESWorkspaceProviderImpl.getObserverBus().notify(ESMergeObserver.class)
.preRevertMyChanges(toAPI(), changePackage.toAPI());
}
private void notifyPostRevertMyChanges() {
ESWorkspaceProviderImpl.getObserverBus().notify(ESMergeObserver.class).postRevertMyChanges(toAPI());
}
private void notifyPostApplyTheirChanges(List<AbstractChangePackage> theirChangePackages) {
final List<ESChangePackage> changePackages = new ArrayList<ESChangePackage>();
for (final AbstractChangePackage theirChangePackage : theirChangePackages) {
changePackages.add(theirChangePackage.toAPI());
}
// TODO ASYNC review this cancel
ESWorkspaceProviderImpl.getObserverBus().notify(ESMergeObserver.class)
.postApplyTheirChanges(toAPI(), changePackages);
}
private void notifyPostApplyMergedChanges(AbstractChangePackage changePackage) {
ESWorkspaceProviderImpl.getObserverBus().notify(ESMergeObserver.class)
.postApplyMergedChanges(
toAPI(), changePackage.toAPI());
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.common.api.APIDelegate#toAPI()
*/
public ESLocalProjectImpl toAPI() {
if (esLocalProjectImpl == null) {
esLocalProjectImpl = createAPI();
}
return esLocalProjectImpl;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.common.api.APIDelegate#createAPI()
*/
public ESLocalProjectImpl createAPI() {
return new ESLocalProjectImpl(this);
}
/**
* Returns the {@link ESRunnableContext} operations are applied in.
*
* @return the runnable context operations are executed in
*/
public ESRunnableContext getRunnableContext() {
return runnableContext;
}
}