Search code examples
eclipseeclipse-plugineclipse-cdt

Eclipse CDT extend AdapterFactory


I try to override the functionality of CDT ResumeAtLine, MoveToLine, RunToLine. For this reason I created a custom SuspendResumeAdapterFactory but it isn't loaded but compiles and runs without error. Do I maybe need a custom adaptableType too?

Here is the content of my plugin.xml.

  <extension point="org.eclipse.core.runtime.adapters">
      <factory 
            class="my.package.CustomSuspendResumeAdapterFactory" 
            adaptableType="org.eclipse.cdt.dsf.ui.viewmodel.IVMContext">
         <adapter type="org.eclipse.debug.core.model.ISuspendResume"/>
      </factory>
   </extension> 

And here my CustomSuspendResumeAdapterFactory this class is reconstructed from memory not 100% sure if the syntax is correct, but I think it should be clear to see what I want to do.

package my.package;

import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.debug.internal.ui.actions.MoveToLine;
import org.eclipse.cdt.dsf.debug.internal.ui.actions.ResumeAtLine;
import org.eclipse.cdt.dsf.debug.internal.ui.actions.RunToLine;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.ISuspendResume;

public class CustomSuspendResumeAdapterFactory implements IAdapterFactory {

    static class SuspendResume implements ISuspendResume, IAdaptable {

        private final CustomRunToLine fRunToLine;
        private final CustomMoveToLine fMoveToLine;
        private final CustomResumeAtLine fResumeAtLine;

        SuspendResume(IExecutionDMContext execCtx) {
            fRunToLine = new CustomRunToLine(execCtx);
            fMoveToLine = new CustomMoveToLine(execCtx);
            fResumeAtLine = new CustomResumeAtLine(execCtx);
        }

        @SuppressWarnings("unchecked")
        @Override
        public <T> T getAdapter(Class<T> adapter) {
            if (adapter.isInstance(RunToLine.class)) {
                System.out.println("CUSTOM RUNTOLINE");
                return (T)fRunToLine;
            }
            if (adapter.isInstance(MoveToLine.class)) {
                System.out.println("CUSTOM MOVETOLINE");
                return (T)fMoveToLine;
            }
            if (adapter.isInstance(ResumeToLine.class)) {
                System.out.println("CUSTOM RESUMEATLINE");
                return (T)fResumeAtLine;
            }
            return null;
        }

        @Override
        public boolean canResume() { return false; }
        @Override
        public boolean canSuspend() { return false; }
        // This must return true because the platform
        // RunToLineActionDelegate will only enable the
        // action if we are suspended
        @Override
        public boolean isSuspended() { return true; }
        @Override
        public void resume() throws DebugException {}
        @Override
        public void suspend() throws DebugException {}
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
        if (ISuspendResume.class.equals(adapterType)) {
            if (adaptableObject instanceof IDMVMContext) {
                IExecutionDMContext execDmc = DMContexts.getAncestorOfType(
                    ((IDMVMContext)adaptableObject).getDMContext(),
                    IExecutionDMContext.class);
                // It only makes sense to RunToLine, MoveToLine or
                // ResumeAtLine if we are dealing with a thread, not a container
                if (execDmc != null && !(execDmc instanceof IContainerDMContext)) {
                    return (T)new SuspendResume(execDmc);
                }
            }
        }
        return null;
    }

    @Override
    public Class<?>[] getAdapterList() {
        return new Class[] { ISuspendResume.class };
    }
}

Solution

  • Why your code is not run

    You have provided a new adapter factory that converts object types that are already handled. i.e. your plugin.xml says you can convert IVMContext to ISuspendResume. But the DSF plug-in already provides such an adapter factory. If you have a new target type (like IMySpecialRunToLine) you could install a factory for that, it would take IVMContext and convert it to a IMySpecialRunToLine).

    Although dated, the Eclipse Corner Article on Adapter Pattern may be useful if this is a new concept.

    How to do custom Run To Line implementation

    If you want to provide different implementation of Run To Line, you need to provide your own version of org.eclipse.cdt.dsf.debug.service.IRunControl2.runToLine(IExecutionDMContext, String, int, boolean, RequestMonitor). The org.eclipse.cdt.dsf.debug.internal.ui.actions.RunToLine class is simply glue to connect UI features (such as buttons/etc some provided directly, some by the core eclipse debug) to the DSF backend. i.e. if you look at what RunToLine does, all it actually does is get the IRunControl2 service and call runToLine on it.

    The way to provider your own implementation of IRunControl2 is override org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory.createRunControlService(DsfSession) and provide your own GdbDebugServicesFactory in your custom launch delegate by overriding org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate.newServiceFactory(ILaunchConfiguration, String)

    RunToLine will be triggered when the user select Run To Line from the popup menu in the editor, as per this screenshot:

    runtoline