| /*************************************************************************************************** |
| * Copyright (c) 2003, 2005 IBM Corporation 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: IBM Corporation - initial API and implementation |
| **************************************************************************************************/ |
| package org.eclipse.wst.common.frameworks.internal; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Set; |
| import java.util.Stack; |
| import java.util.Vector; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.wst.common.environment.IEnvironment; |
| import org.eclipse.wst.common.frameworks.datamodel.AbstractDataModelOperation; |
| import org.eclipse.wst.common.frameworks.datamodel.IDataModel; |
| import org.eclipse.wst.common.frameworks.datamodel.IDataModelOperation; |
| import org.eclipse.wst.common.frameworks.internal.datamodel.IWorkspaceRunnableWithStatus; |
| import org.eclipse.wst.common.frameworks.internal.operations.ComposedExtendedOperationHolder; |
| import org.eclipse.wst.common.frameworks.internal.operations.OperationStatus; |
| |
| public class OperationManager { |
| private IDataModel dataModel; |
| private DataModelManager dataModelManager; |
| private TableEntry rootOperation; |
| private HashMap operationTable; |
| private Stack runStopList; |
| private IProgressMonitor monitor; |
| private IAdaptable adaptable; |
| private OperationStatus status; |
| private OperationListener preExecuteListener; |
| private OperationListener postExecuteListener; |
| private OperationListener undoExecuteListener; |
| private IEnvironment environment; |
| |
| public OperationManager(DataModelManager aDataModelManager, IDataModelOperation aRootOperation, IEnvironment aEnvironment) { |
| if (aRootOperation == null) |
| aRootOperation = new NullOperation(); |
| |
| TableEntry entry = new TableEntry(aRootOperation); |
| |
| dataModelManager = aDataModelManager; |
| dataModel = dataModelManager.getDataModel(); |
| rootOperation = entry; |
| operationTable = new HashMap(); |
| runStopList = new Stack(); |
| operationTable.put(aRootOperation.getID(), entry); |
| environment = aEnvironment; |
| addExtendedOperations(aRootOperation); |
| |
| OperationListener defaultListener = new OperationListener() { |
| public boolean notify(IDataModelOperation operation) { |
| return true; |
| } |
| }; |
| |
| preExecuteListener = defaultListener; |
| postExecuteListener = defaultListener; |
| undoExecuteListener = defaultListener; |
| } |
| |
| public void setProgressMonitor(IProgressMonitor monitor) { |
| this.monitor = monitor; |
| } |
| |
| public void setAdaptable(IAdaptable adaptable) { |
| this.adaptable = adaptable; |
| } |
| |
| public void addPreOperation(String operationId, IDataModelOperation insertOperation) { |
| TableEntry entry = (TableEntry) operationTable.get(operationId); |
| |
| if (entry != null) { |
| TableEntry newEntry = new TableEntry(insertOperation); |
| |
| entry.preOperations.add(newEntry); |
| operationTable.put(insertOperation.getID(), newEntry); |
| } |
| } |
| |
| public void addPostOperation(String operationId, IDataModelOperation insertOperation) { |
| TableEntry entry = (TableEntry) operationTable.get(operationId); |
| |
| if (entry != null) { |
| TableEntry newEntry = new TableEntry(insertOperation); |
| |
| entry.postOperations.add(newEntry); |
| operationTable.put(insertOperation.getID(), newEntry); |
| } |
| } |
| |
| public void setPreExecuteListener(OperationListener listener) { |
| if (listener != null) |
| preExecuteListener = listener; |
| } |
| |
| public void setPostExecuteListener(OperationListener listener) { |
| if (listener != null) |
| postExecuteListener = listener; |
| } |
| |
| public void setUndoExecuteListener(OperationListener listener) { |
| if (listener != null) |
| undoExecuteListener = listener; |
| } |
| |
| public IStatus runOperations() { |
| boolean continueRun = true; |
| RunListEntry runEntry = startNewRun(); |
| |
| status = null; |
| |
| // All operations have already been run so just return OK. |
| if (runEntry.stackEntries.empty()) |
| return Status.OK_STATUS; |
| |
| while (continueRun) { |
| continueRun = runOperationsUntilStopped(runEntry) && !runEntry.stackEntries.empty(); |
| } |
| |
| if (status != null && status.getSeverity() == IStatus.ERROR) { |
| undoLastRun(); |
| } |
| return status; |
| } |
| |
| public void undoLastRun() { |
| if (!runStopList.empty()) { |
| RunListEntry runListEntry = (RunListEntry) runStopList.pop(); |
| |
| for (int index = runListEntry.executedOperations.size() - 1; index >= 0; index--) { |
| IDataModelOperation operation = (IDataModelOperation) runListEntry.executedOperations.elementAt(index); |
| Set dataModelIDs = operation.getDataModelIDs(); |
| |
| if( dataModelIDs != null && dataModelIDs.size() > 0 ) |
| { |
| Iterator ids = dataModelIDs.iterator(); |
| |
| while( ids.hasNext() ) |
| { |
| String dataModelID = (String)ids.next(); |
| dataModelManager.removeNestedDataModel(dataModelID); |
| } |
| } |
| |
| try { |
| undoExecuteListener.notify(operation); |
| } catch (Throwable exc) { |
| // TODO report undo notify exception. |
| } |
| |
| if (operation.canUndo()) { |
| try { |
| runOperation(operation, true); |
| } catch (Throwable exc) { |
| // TODO report an undo exception here. |
| } |
| } |
| } |
| } |
| } |
| |
| private RunListEntry startNewRun() { |
| RunListEntry newEntry = null; |
| |
| if (runStopList.empty()) { |
| newEntry = new RunListEntry(rootOperation); |
| } else { |
| RunListEntry topRunList = (RunListEntry) runStopList.peek(); |
| newEntry = new RunListEntry(topRunList); |
| } |
| |
| runStopList.push(newEntry); |
| |
| return newEntry; |
| } |
| |
| private boolean runOperationsUntilStopped(RunListEntry runListEntry) { |
| StackEntry stackEntry = (StackEntry) runListEntry.stackEntries.peek(); |
| boolean continueRun = true; |
| |
| // Run the pre operations. |
| for (int index = stackEntry.preOperationIndex + 1; continueRun && index < stackEntry.tableEntry.preOperations.size(); index++) { |
| TableEntry tableEntry = (TableEntry) stackEntry.tableEntry.preOperations.elementAt(index); |
| |
| runListEntry.stackEntries.push(new StackEntry(tableEntry)); |
| stackEntry.preOperationIndex = index; |
| continueRun = runOperationsUntilStopped(runListEntry); |
| } |
| |
| if (continueRun && !stackEntry.operationExecuted) { |
| IDataModelOperation operation = stackEntry.tableEntry.operation; |
| |
| try { |
| continueRun = preExecuteListener.notify(operation); |
| |
| if (continueRun) { |
| Set dataModelIDs = operation.getDataModelIDs(); |
| |
| if (dataModelIDs != null && dataModelIDs.size() > 0 ) |
| { |
| Iterator ids = dataModelIDs.iterator(); |
| |
| while( ids.hasNext() ) |
| { |
| String dataModelID = (String)ids.next(); |
| dataModelManager.addNestedDataModel(dataModelID); |
| } |
| } |
| |
| operation.setDataModel(dataModel); |
| operation.setEnvironment( environment ); |
| setStatus(runOperation(operation, false)); |
| runListEntry.executedOperations.add(operation); |
| stackEntry.operationExecuted = true; |
| continueRun = postExecuteListener.notify(operation); |
| } |
| } catch (Throwable exc) { |
| setStatus(new Status(IStatus.ERROR, "id", 0, exc.getMessage() == null ? exc.toString() : exc.getMessage(), exc)); |
| } |
| |
| if (status != null && status.getSeverity() == IStatus.ERROR) { |
| // This isn't really true, but it will cause the run operations to stop. |
| continueRun = false; |
| } |
| } |
| |
| // Run post operations. |
| for (int index = stackEntry.postOperationIndex + 1; continueRun && index < stackEntry.tableEntry.postOperations.size(); index++) { |
| TableEntry tableEntry = (TableEntry) stackEntry.tableEntry.postOperations.elementAt(index); |
| |
| stackEntry.postOperationIndex = index; |
| runListEntry.stackEntries.push(new StackEntry(tableEntry)); |
| continueRun = runOperationsUntilStopped(runListEntry); |
| } |
| |
| // If we are have run the pre ops, this operation, and |
| // the post ops, we should pop this entry off the stack. |
| // Also, if continueRun is false we don't want to pop the stack since we will want to come |
| // back to this entry later. |
| if (continueRun) { |
| runListEntry.stackEntries.pop(); |
| } |
| |
| return continueRun; |
| } |
| |
| private IStatus runOperation(final IDataModelOperation operation, final boolean isUndo) { |
| IWorkspaceRunnableWithStatus workspaceRunnable = new IWorkspaceRunnableWithStatus(adaptable) { |
| public void run(IProgressMonitor pm) throws CoreException { |
| try { |
| if (isUndo) { |
| this.setStatus(operation.undo(monitor, getInfo())); |
| } else { |
| this.setStatus(operation.execute(monitor, getInfo())); |
| } |
| } catch (Throwable exc) { |
| exc.printStackTrace(); |
| } |
| } |
| }; |
| |
| ISchedulingRule rule = operation.getSchedulingRule(); |
| |
| try { |
| if (rule == null) { |
| ResourcesPlugin.getWorkspace().run(workspaceRunnable, monitor); |
| } else { |
| ResourcesPlugin.getWorkspace().run(workspaceRunnable, rule, operation.getOperationExecutionFlags(), monitor); |
| } |
| } catch (CoreException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| return workspaceRunnable.getStatus(); |
| } |
| |
| private void setStatus(IStatus newStatus) { |
| if (status == null) { |
| status = new OperationStatus(newStatus.getMessage(), newStatus.getException()); |
| status.setSeverity(newStatus.getSeverity()); |
| status.add(newStatus); |
| } else { |
| status.add(newStatus); |
| } |
| } |
| |
| private void addExtendedOperations(IDataModelOperation operation) { |
| ComposedExtendedOperationHolder extendedOps = ComposedExtendedOperationHolder.createExtendedOperationHolder(operation.getID()); |
| |
| ArrayList preOps = null; |
| ArrayList postOps = null; |
| |
| if (extendedOps != null) { |
| preOps = extendedOps.getPreOps(); |
| postOps = extendedOps.getPostOps(); |
| } |
| |
| if (preOps == null) |
| preOps = new ArrayList(); |
| if (postOps == null) |
| postOps = new ArrayList(); |
| |
| for (int index = 0; index < preOps.size(); index++) { |
| IDataModelOperation newOperation = (IDataModelOperation) preOps.get(index); |
| |
| addPreOperation(operation.getID(), newOperation); |
| addExtendedOperations(newOperation); |
| } |
| |
| for (int index = 0; index < postOps.size(); index++) { |
| IDataModelOperation newOperation = (IDataModelOperation) postOps.get(index); |
| |
| addPostOperation(operation.getID(), newOperation); |
| addExtendedOperations(newOperation); |
| } |
| } |
| |
| private class RunListEntry { |
| public Stack stackEntries; |
| public Vector executedOperations; |
| |
| public RunListEntry(TableEntry newEntry) { |
| stackEntries = new Stack(); |
| executedOperations = new Vector(); |
| stackEntries.push(new StackEntry(newEntry)); |
| } |
| |
| public RunListEntry(RunListEntry oldList) { |
| stackEntries = new Stack(); |
| executedOperations = new Vector(); |
| |
| for (int index = 0; index < oldList.stackEntries.size(); index++) { |
| StackEntry oldEntry = (StackEntry) oldList.stackEntries.elementAt(index); |
| stackEntries.add(new StackEntry(oldEntry)); |
| } |
| } |
| } |
| |
| private class StackEntry { |
| public int preOperationIndex; |
| public int postOperationIndex; |
| public boolean operationExecuted; |
| public TableEntry tableEntry; |
| |
| public StackEntry(TableEntry newTableEntry) { |
| preOperationIndex = -1; |
| postOperationIndex = -1; |
| operationExecuted = false; |
| tableEntry = newTableEntry; |
| } |
| |
| public StackEntry(StackEntry newStackEntry) { |
| preOperationIndex = newStackEntry.preOperationIndex; |
| postOperationIndex = newStackEntry.postOperationIndex; |
| operationExecuted = newStackEntry.operationExecuted; |
| tableEntry = newStackEntry.tableEntry; |
| } |
| } |
| |
| private class TableEntry { |
| public IDataModelOperation operation; |
| public Vector preOperations; |
| public Vector postOperations; |
| |
| public TableEntry(IDataModelOperation newOperation) { |
| operation = newOperation; |
| preOperations = new Vector(3); |
| postOperations = new Vector(3); |
| } |
| } |
| |
| private class NullOperation extends AbstractDataModelOperation { |
| public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { |
| return Status.OK_STATUS; |
| } |
| |
| public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { |
| return Status.OK_STATUS; |
| } |
| |
| public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { |
| return Status.OK_STATUS; |
| } |
| |
| } |
| } |