/*******************************************************************************
* Copyright (c) 2010 SAP AG.
* 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:
* Emil Simeonov - initial API and implementation.
* Dimitar Donchev - initial API and implementation.
* Dimitar Tenev - initial API and implementation.
* Nevena Manova - initial API and implementation.
* Georgi Konstantinov - initial API and implementation.
*******************************************************************************/
package org.eclipse.wst.sse.sieditor.model.utils;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.emf.workspace.AbstractEMFOperation;
import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.operations.IWorkbenchOperationSupport;
import org.eclipse.wst.sse.sieditor.command.common.AbstractNotificationOperation;
import org.eclipse.wst.sse.sieditor.command.common.CompositeTextOperationWrapper;
import org.eclipse.wst.sse.sieditor.command.common.TextCommandWrapper;
import org.eclipse.wst.sse.sieditor.core.common.ICommandStackListener;
import org.eclipse.wst.sse.sieditor.core.common.IDisposable;
import org.eclipse.wst.sse.sieditor.core.common.IEditValidator;
import org.eclipse.wst.sse.sieditor.core.common.IEnvironment;
import org.eclipse.wst.sse.sieditor.core.common.IModelReconcileRegistry;
import org.eclipse.wst.sse.sieditor.core.common.Logger;
import org.eclipse.wst.sse.sieditor.model.Activator;
import org.eclipse.wst.sse.sieditor.model.i18n.Messages;
/**
* A class, which is responsible for the creation of
* <code>IEnviroment</code> instances.
*/
public final class EnvironmentFactory {
public final static class EnvironmentImpl implements IEnvironment, Adapter {
private TransactionalEditingDomain editingDomain;
private CompositeTextOperationWrapper operationWrapper = null;
private final String id;
private IEditValidator editValidator;
private IUndoContext undoContext;
private IOperationHistory operationHistory;
private final IModelReconcileRegistry modelReconcileRegistry;
private List<IDisposable> registryOfDisposables = new LinkedList<IDisposable>();
private ResourceSet resourceSet;
private CommandStackWrapper commandStackWrapper;
private static IEnvironment createEnvironment(final String id, final ResourceSet resourceSet) {
for (final Adapter adapter : resourceSet.eAdapters()) {
if (adapter instanceof IEnvironment) {
return (IEnvironment) adapter;
}
}
final EnvironmentImpl env;
env = new EnvironmentImpl(id);
env.init(resourceSet);
return env;
}
private void init(final ResourceSet resourceSet) {
this.resourceSet = resourceSet;
editingDomain = TransactionUtil.getEditingDomain(resourceSet);
if (editingDomain == null) {
final IWorkbenchOperationSupport operationSupport = PlatformUI.getWorkbench().getOperationSupport();
operationHistory = operationSupport.getOperationHistory();
commandStackWrapper = new CommandStackWrapper(operationHistory);
addDisposable(commandStackWrapper);
final TransactionalEditingDomain result = new TransactionalEditingDomainImpl(new ComposedAdapterFactory(
ComposedAdapterFactory.Descriptor.Registry.INSTANCE), commandStackWrapper, resourceSet);
WorkspaceEditingDomainFactory.INSTANCE.mapResourceSet(result);
editingDomain = result;
editingDomain.setID(id);
}
else {
throw new IllegalStateException("TransactionalEditingDomain for the current file already exists. " + //$NON-NLS-1$
"Do close all editors for the current file before openening this one. "); //$NON-NLS-1$
}
resourceSet.eAdapters().add(this);
}
private EnvironmentImpl(final String id) {
if (id == null) {
throw new IllegalArgumentException("The id of the environment cannot be null"); //$NON-NLS-1$
}
this.modelReconcileRegistry = new ModelReconcileRegistry();
this.id = id;
}
public void addCommandStackListener(final ICommandStackListener listener) {
commandStackWrapper.addCommandStackListener(listener);
}
public void removeCommandStackListener(final ICommandStackListener listener) {
commandStackWrapper.removeCommandStackListener(listener);
}
public IStatus execute(final AbstractEMFOperation operation) throws ExecutionException {
return execute(operation, null);
}
public IStatus execute(final AbstractEMFOperation operation, final IProgressMonitor monitor) throws ExecutionException {
return execute(operation, monitor, null);
}
public IStatus execute(final AbstractEMFOperation operation, final IProgressMonitor monitor, final IAdaptable info)
throws ExecutionException {
IStatus status = Status.OK_STATUS;
if (editValidator != null) {
status = editValidator.canEdit();
}
if (status.isOK()) {
final RunWithBusyCursor runWithBusyCursorRunnable = new RunWithBusyCursor(operation, monitor, info);
BusyIndicator.showWhile(Display.getDefault(), runWithBusyCursorRunnable);
return runWithBusyCursorRunnable.getStatus();
}
return status;
}
private class RunWithBusyCursor implements Runnable {
private AbstractEMFOperation operation;
private final IProgressMonitor monitor;
private final IAdaptable info;
private IStatus status;
public RunWithBusyCursor(final AbstractEMFOperation operation, final IProgressMonitor monitor, final IAdaptable info) {
this.operation = operation;
this.monitor = monitor;
this.info = info;
}
public void run() {
if (operation == null) {
throw new IllegalArgumentException("Operation cannot be null"); //$NON-NLS-1$
}
if (undoContext == null) {
throw new IllegalStateException("Undo context not set - cannot proceed with operation execution"); //$NON-NLS-1$
}
try {
if (operationWrapper == null) {
// first operation in the chain to be executed
if (operation instanceof AbstractNotificationOperation) {
/*
* wrap the first operation from the chain
* execution. this operation is wrapped only once
* for the chain execution. subsequent operations
* have the operationWrapper already set.
*
* NOTE: the operation wrapper must be set to null
* after the operation chain execution has been
* completed with the
* #setCompositeTextOperationWrapper(null)
*/
operationWrapper = new CompositeTextOperationWrapper((AbstractNotificationOperation) operation);
operation = operationWrapper;
}
operation.addContext(undoContext);
status = operationHistory.execute(operation, monitor == null ? new NullProgressMonitor() : monitor, info);
} else {
// we are running subsequent operation
operation.addContext(undoContext);
operation.setReuseParentTransaction(true);
status = operation.execute(monitor, info);
if (operation instanceof TextCommandWrapper) {
/*
* explicitly add text commands to the wrapper,
* since we are interested in text commands only.
*
* Note: We are going to do undo/redo of the
* gathered text commands
*/
operationWrapper.addTextCommand((TextCommandWrapper) operation);
}
}
} catch (final ExecutionException e) {
status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Execution of operation failed", e); //$NON-NLS-1$
Logger.log(status);
}
}
public IStatus getStatus() {
return status;
}
}
public void setUndoContext(final IUndoContext undoContext) {
if (this.undoContext == null) {
this.undoContext = undoContext;
}
}
public IUndoContext getUndoContext() {
return undoContext;
}
public void setOperationHistory(final IOperationHistory operationHistory) {
this.operationHistory = operationHistory;
}
public IOperationHistory getOperationHistory() {
return operationHistory;
}
public String getId() {
return id;
}
public TransactionalEditingDomain getEditingDomain() {
return editingDomain;
}
public void dispose() {
for(IDisposable disposable : registryOfDisposables) {
disposable.doDispose();
}
registryOfDisposables.clear();
resourceSet.eAdapters().remove(this);
if (editingDomain != null) {
try {
editingDomain.dispose();
} finally {
editingDomain = null;
}
}
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof IEnvironment) {
final IEnvironment env = (IEnvironment) obj;
final String envId = env.getId();
if (envId == null) {
return false;
}
return id.equals(envId);
}
return false;
}
@Override
public int hashCode() {
return id.hashCode();
}
public Notifier getTarget() {
// TODO Auto-generated method stub
return null;
}
public boolean isAdapterForType(final Object type) {
// TODO Auto-generated method stub
return false;
}
public void notifyChanged(final Notification notification) {
// TODO Auto-generated method stub
}
public void setTarget(final Notifier newTarget) {
// TODO Auto-generated method stub
}
private void setCompositeTextOperationWrapper(final AbstractEMFOperation compositeTextOperationWrapper) {
operationWrapper = (CompositeTextOperationWrapper) compositeTextOperationWrapper;
}
@Override
public void finalizeWrapperCommandExecution() {
setCompositeTextOperationWrapper(null);
}
@Override
public CompositeTextOperationWrapper getCompositeTextOperationWrapper() {
return operationWrapper;
}
@Override
public IModelReconcileRegistry getModelReconcileRegistry() {
return modelReconcileRegistry;
}
public void setEditValidator(IEditValidator editValidator) {
this.editValidator = editValidator;
}
public IEditValidator getEditValidator() {
return this.editValidator;
}
@Override
public void addDisposable(IDisposable disposable) {
if(!registryOfDisposables.contains(disposable)) {
registryOfDisposables.add(disposable);
}
}
public List<IDisposable> getRegistryOfDisposables() {
return Collections.unmodifiableList(registryOfDisposables);
}
}
/**
* Creates an instance of <code>IEnvironment</code>. It always return a new
* instance.
*
* @param id
* environment unique id
* @param resourceSet
* emf resource set
* @return a new instance of <code>IEnvironment</code>
*/
public static IEnvironment createEnvironment(final String id, final ResourceSet resourceSet) {
return EnvironmentImpl.createEnvironment(id, resourceSet);
}
private EnvironmentFactory() {
}
}