/******************************************************************************* * Copyright (c) 2010, 2011 Texas Instruments, Inc. 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: * Dobrin Alexiev (Texas Instruments) - initial API and implementation (bug 240208) ********************************************************************************/ package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; import java.util.ArrayList; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.datamodel.IDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; /** * This class is a base class of AbstractThreadVMNode and AbstractContainerVMNode. * It contains common functionality between these classes. * * The main reason this class is introduce is to allow the debug view to * show multiple levels of execution containers and properly handle the delta generation. * * Longer term we would like to merge the classes AbstractContainerVMNode and * AbstractThreadVMNode. That will make the implementation of both classes * more generic and robust in the case of recursive containers. * * Having this class as a base for both AbstractContainerVMNode and * AbstractThreadVMNode enables us to merge them in the future. * * Originally DefaultVMModelProxyStrategy didn't accept recursive container for * generating deltas, even though they are accepted and supported by * AbstractDMVMProvider for viewing. * The approach I took to support recursive container in delta generation is to have * the VMNodes to generate level by level its deltas instead of one the whole delta at once. * That required changes in identifying which is the correct context for each of the events. * * See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=240208 * * @since 2.2 * @experimental */ public abstract class AbstractExecutionContextVMNode extends AbstractDMVMNode { /** * List that keeps track of which events are considered leave events for * delta creation. */ protected ArrayList<Class<?>> leafEventTypes = new ArrayList<Class<?>>(); /** * List that keeps track of which events are considered container events for * delta creation. */ protected ArrayList<Class<?>> containerEventTypes = new ArrayList<Class<?>>(); /** * Constructor. * Pass the parameter to the base class's constructor. * * @param session * @param dmcClassType * * @see #setChildNodes(IVMNode[]) */ public AbstractExecutionContextVMNode(AbstractDMVMProvider provider, DsfSession session, Class<? extends IDMContext> dmcClassType) { super(provider, session, dmcClassType); } /** * Adds the events that common DSF classes relay on. */ protected void addCommonEventTypes() { // non container events. addEventType(ISuspendedDMEvent.class,false); addEventType(IResumedDMEvent.class, false); addEventType(FullStackRefreshEvent.class, false); addEventType(SteppingTimedOutEvent.class, false); addEventType(ExpandStackEvent.class, false); // container events. addEventType(IContainerSuspendedDMEvent.class,true); addEventType(IContainerResumedDMEvent.class, true); } /** * When DSF debugger define custom events for which the containers and threads * nodes needs to be update they can need to register these events using this * function, so the proper recursive deltas are being created. * * @param eventClass */ protected void addEventType( Class<? extends IDMEvent<?>> eventClass, boolean containerEvent) { if( containerEvent) containerEventTypes.add( eventClass); else leafEventTypes.add( eventClass); } /** * If DSF debuggers overrides the behavior of the AbstractThreadVMNode * or AbstractContainerVMNode, some event are no longer needed the derived * VMNdoe can call this method to remove some events. * * @param eventClass * @param containerEvent */ protected void removeEventType( Class<?> eventClass, boolean containerEvent) { if( containerEvent) containerEventTypes.remove( eventClass); else leafEventTypes.remove( eventClass); } /** * When we support recursive containers we want to make sure the immediate parent is returned only. * * @param parentDelta * @param e * @param rm - request monitor * @return true if the context is set by the method. */ protected boolean getContextsForRecursiveVMNode(VMDelta parentDelta, Object e, final DataRequestMonitor<IVMContext[]> rm) { IExecutionDMContext leafContext = null; if( isExecutionContainerEvent(e)) { leafContext = getLeafContextForContainerEvent( e); } else if( isExecutionLeafEvent(e)) { leafContext = getLeafContextForLeafEvent( e); } if( leafContext != null) { setImmediateParentAsContexts(leafContext,parentDelta,rm); return true; } return false; } /** * Make sure we build the delta for the recursive containers one level at a time. * * @param e - the events. * @param parentDelta * @param nodeOffset * @param requestMonitor * @return true if the delta is built by this method. */ protected boolean buildDeltaForRecursiveVMNode(Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { IExecutionDMContext leafContext = null; if( isExecutionContainerEvent(e)) { leafContext = getLeafContextForContainerEvent( e); } else if( isExecutionLeafEvent(e)) { leafContext = getLeafContextForLeafEvent( e); } if( leafContext != null) { addOneLevelToDelta( leafContext, parentDelta, requestMonitor); return true; } return false; } /** * When the deltas are generated one level at a time we needs to distinguish * between container and regular event to return the proper context for the event. * * @param event * @return */ protected IExecutionDMContext getLeafContextForContainerEvent( Object event) { IExecutionDMContext leafEC = null; IExecutionDMContext[] triggeringContext = null; if( isExecutionContainerEvent(event)) { if( event instanceof IContainerSuspendedDMEvent) { IContainerSuspendedDMEvent typedEvent = (IContainerSuspendedDMEvent)event; triggeringContext = typedEvent.getTriggeringContexts(); } if( event instanceof IContainerResumedDMEvent) { IContainerResumedDMEvent typedEvent = (IContainerResumedDMEvent)event; triggeringContext = typedEvent.getTriggeringContexts(); } } if( triggeringContext != null && triggeringContext.length > 0){ leafEC = triggeringContext[0]; } return leafEC; } /** * When the deltas are generated one level at a time we needs to distinguish * between container and regular event to return the proper context for the event. * * @param event * @return */ protected IExecutionDMContext getLeafContextForLeafEvent( Object event) { IExecutionDMContext leafEC = null; if( event instanceof IDMEvent<?>) if( isExecutionLeafEvent( event)) { IDMEvent<?> typedEvent = (IDMEvent<?>)event; IDMContext dmContext = typedEvent.getDMContext(); if( dmContext instanceof IExecutionDMContext) leafEC = (IExecutionDMContext)dmContext; } return leafEC; } /** * Considers the parent delta when we construct the next level. * * @param leafContext * @param parentDelta * @param requestMonitor */ protected void addOneLevelToDelta( IExecutionDMContext leafContext, VMDelta parentDelta, RequestMonitor requestMonitor) { assert leafContext != null; if( parentDelta.getElement() instanceof ILaunch) { IContainerDMContext topContainer = DMContexts.getTopMostAncestorOfType( leafContext, IContainerDMContext.class); // It is possible for a thread node to be an immediate child of a launch node // with no container node in between. if( topContainer != null) parentDelta.addNode(createVMContext(topContainer), 0, IModelDelta.NO_CHANGE); } else if( parentDelta.getElement() instanceof IDMVMContext) { IDMVMContext vmContext = (IDMVMContext)parentDelta.getElement(); IDMContext dmContext = vmContext.getDMContext(); IExecutionDMContext current = DMContexts.getParentOfType(leafContext, IContainerDMContext.class); while( current != null) { IContainerDMContext parent = DMContexts.getParentOfType(current, IContainerDMContext.class); if( dmContext.equals(parent)) { parentDelta.addNode(createVMContext(current), 0, IModelDelta.NO_CHANGE); break; } current = parent; } } requestMonitor.done(); } /** * Based on the event (container or not) set the proper context that is the immediate * parent one level at a time. * * @param leafContext * @param parentDelta * @param rm */ protected void setImmediateParentAsContexts( IExecutionDMContext leafContext, VMDelta parentDelta, DataRequestMonitor<IVMContext[]> rm){ assert leafContext != null; IVMContext[] all = null; if( parentDelta.getElement() instanceof ILaunch) { IContainerDMContext topContainer = DMContexts.getTopMostAncestorOfType( leafContext, IContainerDMContext.class); if( topContainer != null) { all = new IVMContext[] { createVMContext(topContainer) }; } else { // the thread is directly a child node of the launch node (no container in the middle). all = new IVMContext[] { createVMContext(leafContext) }; } } else if( parentDelta.getElement() instanceof IDMVMContext) { IDMVMContext vmContext = (IDMVMContext)parentDelta.getElement(); IDMContext dmContext = vmContext.getDMContext(); IExecutionDMContext current = leafContext; while( current != null) { IContainerDMContext parent = DMContexts.getParentOfType(current, IContainerDMContext.class); if( dmContext.equals(parent)) { all = new IVMContext[] { createVMContext(current)}; break; } current = parent; } } if( all == null) all = new IVMContext[0]; rm.setData( all ); rm.done(); } /** * Returns whether the event should be considered a container event or not. * * @param event * @return */ protected boolean isExecutionContainerEvent( Object event) { if( event != null) for( Class<?> clazz : containerEventTypes) if( clazz.isAssignableFrom(event.getClass())) return true; return false; } /** * Returns whether the event should be use to generate delta for each of the levels. * * @param event * @return */ protected boolean isExecutionLeafEvent( Object event) { if( event != null) for( Class<?> clazz : leafEventTypes) if( clazz.isAssignableFrom(event.getClass())) return true; return false; } }