/******************************************************************************* * Copyright (c) 2005 - 2007 committers of openArchitectureWare 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: * committers of openArchitectureWare - initial API and implementation *******************************************************************************/ package org.eclipse.emf.mwe.internal.core.debug.processing.handlers; import java.io.IOException; import java.util.Stack; import org.eclipse.emf.mwe.core.debug.processing.EventHandler; import org.eclipse.emf.mwe.internal.core.debug.communication.Connection; import org.eclipse.emf.mwe.internal.core.debug.communication.packages.CommandPackage; import org.eclipse.emf.mwe.internal.core.debug.processing.CommandListener; import org.eclipse.emf.mwe.internal.core.debug.processing.DebugMonitor; import org.eclipse.emf.mwe.internal.core.debug.processing.ProcessHandler; import org.eclipse.emf.mwe.internal.core.debug.processing.RuntimeHandler; /** * This class handles the communication of debug commands on the runtime side.<br> * It listens in an extra thread for commands and sets state values accordingly.<br> * <br> * The <code>DebugMonitor</code> uses this class to react according to the * process state settings when it needs to.<br> * The <code>ICommandListener</code> and <code>IProcessHandler</code> methods * are the active ones that communicate with the debug server.<br> * The <code>IEventHandler</code> methods react only internally on events. */ public class CommandRuntimeHandler implements RuntimeHandler, CommandListener, ProcessHandler, EventHandler, Runnable { public static final int STEP_INTO = 1; public static final int STEP_OVER = 2; public static final int STEP_RETURN = 3; public static final int RESUME = 4; public static final int SUSPEND = 5; public static final int TERMINATE = 6; private Connection connection; private DebugMonitor monitor; // ------------------------------------------------------------------------- private boolean stepping = false; private boolean forceSuspend = false; private boolean interrupt = false; private final Stack<Boolean> stackFrames = new Stack<Boolean>(); private int iterationLevel = 0; private int suspendBaseLevel = 1; private boolean continueOperation = false; private final Object[] syncObject = new Object[0]; // ------------------------------------------------------------------------- /** * @see org.eclipse.emf.mwe.internal.core.debug.processing.RuntimeHandler#init(org.eclipse.emf.mwe.internal.core.debug.processing.DebugMonitor, * org.eclipse.emf.mwe.internal.core.debug.communication.Connection) */ public void init(final DebugMonitor monitor, final Connection connection) { this.monitor = monitor; this.connection = connection; if (monitor != null) { monitor.setCommandListener(this); monitor.addProcessHandler(this); monitor.addEventHandler(this); } } /** * @see org.eclipse.emf.mwe.internal.core.debug.processing.RuntimeHandler#startListener() */ public void startListener() { Thread thread = new Thread(this, getClass().getSimpleName()); thread.setDaemon(true); thread.start(); } /** * @see java.lang.Runnable#run() */ public void run() { try { while (true) { listenAndDispatchCommand(); } } catch (IOException e) { doTerminate(); } } private void listenAndDispatchCommand() throws IOException { dispatch(((CommandPackage) connection.listenForPackage(CommandPackage.class)).command); } private void dispatch(final int cmd) { switch (cmd) { case STEP_INTO: case STEP_OVER: case STEP_RETURN: doStep(cmd); break; case RESUME: doResume(); break; case TERMINATE: doTerminate(); break; case SUSPEND: doSuspend(); break; default: break; } } // ------------------------------------------------------------------------- private void doStep(final int cmd) { stepping = true; switch (cmd) { case STEP_INTO: suspendBaseLevel = iterationLevel + 1; break; case STEP_OVER: suspendBaseLevel = iterationLevel; break; case STEP_RETURN: suspendBaseLevel = iterationLevel - 1; break; } continueDebugger(); } private void doResume() { stepping = false; continueDebugger(); } private void doSuspend() { forceSuspend = true; } private void doTerminate() { interrupt = true; continueDebugger(); } // ------------------------------------------------------------------------- // IProcessHandler implementation /** * the CommandRuntimeHandler shall have the last call * * @see org.eclipse.emf.mwe.internal.core.debug.processing.ProcessHandler#isLastCall() */ public boolean isLastCall() { return true; } /** * ask the suitable element adapter if the element shall be handled. * Remember that information for the pop call. * * @see org.eclipse.emf.mwe.internal.core.debug.processing.ProcessHandler#shallHandle(boolean, * java.lang.Object, int) */ public boolean shallHandle(final boolean lastState, final Object element, final int flag) { boolean result; if (flag == PUSH) { result = monitor.getAdapter(element).shallHandle(element); stackFrames.push(result); } else { // POP result = stackFrames.pop(); } return lastState || result; } /** * return true in case of a user's suspend request or dependend on the * current iteration level. * * @see org.eclipse.emf.mwe.internal.core.debug.processing.ProcessHandler#shallSuspend(boolean, * java.lang.Object, int) */ public boolean shallSuspend(final boolean lastState, final Object element, final int flag) { boolean shallSuspend = lastState || forceSuspend || (stepping && (suspendBaseLevel - (flag == NORMAL_FRAME ? 0 : 1) >= iterationLevel)); if (!monitor.getAdapter(element).shallSuspend(element, flag) && shallSuspend) { suspendBaseLevel++; return false; } return shallSuspend; } /** * return true in case of a user's terminate event or if the socket * connection is no longer open * * @see org.eclipse.emf.mwe.internal.core.debug.processing.ProcessHandler#shallInterrupt(boolean) */ public boolean shallInterrupt(final boolean lastState) { return lastState || interrupt || !connection.isConnected(); } // ------------------------------------------------------------------------- // IEventHandler implementation /** * no contribution here * * @see org.eclipse.emf.mwe.internal.core.debug.processing.EventHandler#started() */ public void started() { } /** * increment the iteration level * * @see org.eclipse.emf.mwe.internal.core.debug.processing.EventHandler#preTask(java.lang.Object, * int) */ public void preTask(final Object element, final Object context, final int state) { iterationLevel++; } /** * decrement the iteration level * * @see org.eclipse.emf.mwe.internal.core.debug.processing.EventHandler#preTask(java.lang.Object, * int) */ public void postTask(final Object context) { iterationLevel--; } /** * reset a potential forceSuspend request * * @see org.eclipse.emf.mwe.internal.core.debug.processing.EventHandler#suspended() */ public void suspended() { forceSuspend = false; } /** * no contribution here * * @see org.eclipse.emf.mwe.internal.core.debug.processing.EventHandler#started() */ public void resumed() { } /** * no contribution here * * @see org.eclipse.emf.mwe.internal.core.debug.processing.EventHandler#started() */ public void terminated() { } // ------------------------------------------------------------------------- // ICommandListener implementation public void listenCommand() { if (!continueOperation) { synchronized (syncObject) { try { syncObject.wait(); } catch (InterruptedException e) { // } } } continueOperation = false; } // set the continue flag so that the debugMonitor doesn't need to stop if // the next command is already there private void continueDebugger() { continueOperation = true; synchronized (syncObject) { syncObject.notifyAll(); } } }