/******************************************************************************* * Copyright (c) 2004 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.command.internal.env.core.fragment; import java.util.Stack; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.wst.command.internal.env.core.data.DataFlowManager; import org.eclipse.wst.command.internal.provisional.env.core.CommandFactory; import org.eclipse.wst.command.internal.provisional.env.core.CommandManager; import org.eclipse.wst.command.internal.provisional.env.core.common.MessageUtils; import org.eclipse.wst.command.internal.provisional.env.core.common.StatusUtils; import org.eclipse.wst.command.internal.provisional.env.core.data.DataMappingRegistry; import org.eclipse.wst.common.environment.Environment; import org.eclipse.wst.common.environment.Log; import org.eclipse.wst.common.frameworks.datamodel.AbstractDataModelOperation; /** * The CommandFragmentEngine provides a convienent way to traverse CommandFragments * and possiblity execute its associate Command. */ public class CommandFragmentEngine implements CommandManager { private Stack commandStack_; private FragmentListener undoFragmentListener_; private FragmentListener nextFragmentListener_; private FragmentListener peekFragmentListener_; private DataFlowManager dataManager_; private Environment environment_; private IStatus lastStatus_; /** * Creates a CommandFragmentEngine. * * @param startFragment the root fragment where traversal will begin. * @param dataManager the data manager containing all of the data mappings. * @param environment the environment. */ public CommandFragmentEngine( CommandFragment startFragment, DataFlowManager dataManager, Environment environment ) { SequenceFragment root = new SequenceFragment(); root.add( startFragment ); commandStack_ = new Stack(); addToStack( root, -1 ); addToStack( startFragment, 0 ); dataManager_ = dataManager; environment_ = environment; } /** * @return returns the Data mapping registry. */ public DataMappingRegistry getMappingRegistry() { return dataManager_.getMappingRegistry(); } /** * * @return the Status from the last Command executed. */ public IStatus getLastStatus() { return lastStatus_; } /** */ public boolean isUndoEnabled() { return true; } /** * Sets the next fragment listener for this engine. This listener will be * called for each fragment that is traversed in moveForwardToNextStop operation. * * @param listener the fragment listener. */ public void setNextFragmentListener( FragmentListener listener ) { nextFragmentListener_ = listener; } /** * Sets the next fragment listener for this engine. This listener will be * called for each fragment that is traversed in peekForwardToNextStop operation. * * @param listener the fragment listener. */ public void setPeekFragmentListener( FragmentListener listener ) { peekFragmentListener_ = listener; } /** * Sets the peek fragment listener for this engine. This listener will be * called for each fragment that is traversed in undoToLastStop operation. * * @param listener the fragment listener. */ public void setUndoFragmentListener( FragmentListener listener ) { undoFragmentListener_ = listener; } /** * Traverse the CommandFragments starting with the CommandFragment on the * top of the command fragment stack. The operation does NOT change the * command fragment stack and does not execute any command associated with * a CommandFragment. */ public void peekForwardToNextStop() { CommandListEntry topEntry = (CommandListEntry)commandStack_.lastElement(); int parentIndex = topEntry.parentIndex_; CommandFragment childFragment = topEntry.fragment_; boolean continueLoop = navigateChildFragments( childFragment, false ); while( parentIndex != -1 && continueLoop ) { CommandListEntry parentEntry = (CommandListEntry)commandStack_.elementAt( parentIndex ); CommandFragment parentFragment = parentEntry.fragment_; CommandFragment nextFragment = parentFragment.getNextSubFragment( childFragment ); if( nextFragment == null ) { // There are no more sibling fragments to navigate so we will pop up to the parent // an continue navigation there. parentIndex = parentEntry.parentIndex_; childFragment = parentFragment; } else { if( navigateChildFragments( nextFragment, true ) ) { // We are continuing to navigate. This time we want to traverse the sibling // of nextFragment. childFragment = nextFragment; } else { // We are stopping our navigation. continueLoop = false; } } } } /** * Traverse the CommandFragments starting with the CommandFragment on the * top of the command fragment stack. This operation does change the * command fragment stack and does execute any command associated with * a CommandFragment. */ public void moveForwardToNextStop( IProgressMonitor monitor ) { CommandListEntry topEntry = (CommandListEntry)commandStack_.lastElement(); int parentIndex = topEntry.parentIndex_; CommandFragment currentFragment = topEntry.fragment_; boolean continueExecute = visitTop( monitor ); CommandFragment childFragment = currentFragment.getFirstSubFragment(); // If the current fragment has child fragments we need to traverse these children. while( childFragment != null && continueExecute ) { parentIndex = commandStack_.size() - 1; addToStack( childFragment, parentIndex ); continueExecute = visitTop( monitor ); currentFragment = childFragment; childFragment = currentFragment.getFirstSubFragment(); } // The previous while loop has guaranteed that currentFragment has no // child fragments. This while loop assumes this to be the case. while( parentIndex != -1 && continueExecute ) { CommandListEntry parentEntry = (CommandListEntry)commandStack_.elementAt( parentIndex ); CommandFragment parentFragment = parentEntry.fragment_; CommandFragment nextFragment = null; if( currentFragment == null ) { nextFragment = parentFragment.getFirstSubFragment(); } else { nextFragment = parentFragment.getNextSubFragment( currentFragment ); } if( nextFragment == null ) { // There are no more sibling fragments to navigate so we will pop up to the parent // and continue navigation there. parentIndex = parentEntry.parentIndex_; currentFragment = parentFragment; } else { // We have a new fragment that we need to add to the top of the stack. addToStack( nextFragment, parentIndex ); parentIndex = commandStack_.size() - 1; continueExecute = visitTop( monitor ); currentFragment = null; } } } /** * * @return returns true if the there is not longer any elements on the stack. Note: * that last two entries are always left on the stack. */ public boolean undoToLastStop() { CommandListEntry topEntry = (CommandListEntry)commandStack_.lastElement(); // Always undo the top element. performUndo( topEntry ); while( topEntry.parentIndex_ != 0 ) { commandStack_.pop(); topEntry = (CommandListEntry)commandStack_.lastElement(); performUndo( topEntry ); if( topEntry.fragmentStopped_ ) { break; } } return topEntry.parentIndex_ == 0; } private void performUndo( CommandListEntry entry ) { if( entry.parentIndex_ == 0 ) return; AbstractDataModelOperation cmd = entry.command_; if( cmd != null && cmd.canUndo() && !entry.beforeExecute_ ) { try { cmd.undo( null, null ); } catch( Exception exc ) { exc.printStackTrace(); } entry.beforeExecute_ = true; } undoFragmentListener_.notify( entry.fragment_ ); } private boolean navigateChildFragments( CommandFragment fragment, boolean visitCurrent ) { boolean continueNavigate = true; CommandFragment childFrag = null; if( visitCurrent ) { continueNavigate = peekFragmentListener_.notify( fragment ); dataManager_.process( fragment ); } childFrag = fragment.getFirstSubFragment(); while( childFrag != null && continueNavigate ) { continueNavigate = navigateChildFragments( childFrag, true ); childFrag = fragment.getNextSubFragment( childFrag ); } return continueNavigate; } private boolean visitTop( IProgressMonitor monitor ) { CommandListEntry entry = (CommandListEntry)commandStack_.lastElement(); boolean continueNavigate = nextFragmentListener_.notify( entry.fragment_ ); if( continueNavigate ) { // Call the setters for this fragment. dataManager_.process( entry.fragment_ ); // Add any rules to the mapping registry before we execute the command. entry.fragment_.registerDataMappings( dataManager_.getMappingRegistry() ); lastStatus_ = runCommand( entry, monitor ); if( lastStatus_.getSeverity() == IStatus.ERROR ) continueNavigate = false; } if( !continueNavigate ) entry.fragmentStopped_ = true; return continueNavigate; } private void addToStack( CommandFragment fragment, int parentIndex ) { CommandListEntry entry = new CommandListEntry( fragment, parentIndex ); commandStack_.push( entry ); } private IStatus runCommand( CommandListEntry entry, IProgressMonitor monitor ) { CommandFactory factory = entry.fragment_.getCommandFactory(); IStatus status = Status.OK_STATUS; if( factory != null ) { AbstractDataModelOperation cmd = factory.create(); entry.command_ = cmd; if( cmd != null ) { dataManager_.process( cmd ); try { environment_.getLog().log(Log.INFO, "command", 5001, this, "runCommand", "Executing: " + cmd.getClass().getName()); cmd.setEnvironment( environment_ ); status = cmd.execute( monitor, null ); entry.beforeExecute_ = false; } catch( Throwable exc ) { MessageUtils utils = new MessageUtils( "org.eclipse.wst.command.env.core.environment", this ); IStatus unexpectedError = StatusUtils.errorStatus( exc ); MultiStatus parentStatus = new MultiStatus( "id", 0, new IStatus[]{unexpectedError}, utils.getMessage( "MSG_ERROR_UNEXPECTED_ERROR" ), null ); environment_.getStatusHandler().reportError( parentStatus ); } finally { String message = "Ok"; if( status.getSeverity() == Status.ERROR ) { message = "Error: " + status.getMessage(); } environment_.getLog().log(Log.INFO, "command", 5001, this, "runCommand", "Execution status: " + message ); } } } return status; } private class CommandListEntry { public CommandListEntry( CommandFragment fragment, int parentIndex ) { fragment_ = fragment; parentIndex_ = parentIndex; fragmentStopped_ = false; beforeExecute_ = true; } public AbstractDataModelOperation command_; public CommandFragment fragment_; public int parentIndex_; public boolean fragmentStopped_; public boolean beforeExecute_; } }