/* * Copyright 2007 Sascha Weinreuter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.intellij.plugins.xsltDebugger; import com.intellij.execution.process.ProcessHandler; import com.intellij.openapi.Disposable; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.util.EventDispatcher; import com.intellij.xdebugger.XSourcePosition; import org.intellij.plugins.xsltDebugger.impl.XsltBreakpointHandler; import org.intellij.plugins.xsltDebugger.impl.XsltDebugProcess; import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.net.MalformedURLException; import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.rmi.RemoteException; import java.util.EventListener; /** * This is the main place that interacts with the debugged XSLT processor. Waits until the processor * hits a breakpoint, resumes execution, etc. */ public class XsltDebuggerSession implements Disposable { private static final Key<XsltDebuggerSession> DEBUGGER_SESSION = Key.create("DEBUGGER_SESSION"); private final Project myProject; private final ProcessHandler myProcess; private final Debugger myClient; private final EventDispatcher<Listener> myEventDispatcher = EventDispatcher.create(Listener.class); private Breakpoint myTempBreakpoint; private volatile Debugger.State myState; private boolean myClosed; private XsltDebuggerSession(Project project, ProcessHandler process, Debugger client) { myProject = project; myProcess = process; myClient = client; Disposer.register(XsltDebugProcess.getInstance(process), this); } public void start() { myClient.start(); myState = Debugger.State.RUNNING; final BreakpointManager breakpointManager = myClient.getBreakpointManager(); final Listener multicaster = myEventDispatcher.getMulticaster(); try { if (!myClient.waitForDebuggee()) { multicaster.debuggerStopped(); return; } myState = Debugger.State.SUSPENDED; do { if (myState == Debugger.State.SUSPENDED) { if (myTempBreakpoint != null) { breakpointManager.removeBreakpoint(myTempBreakpoint); myTempBreakpoint = null; } multicaster.debuggerSuspended(); } else if (myState == Debugger.State.RUNNING) { multicaster.debuggerResumed(); } else if (myState == Debugger.State.STOPPED) { break; } } while ((myState = myClient.waitForStateChange(myState)) != null); multicaster.debuggerStopped(); } catch (DebuggerStoppedException e) { multicaster.debuggerStopped(); } catch (RuntimeException e) { if (e.getCause() instanceof RemoteException) { if (e.getCause().getCause() instanceof SocketException) { multicaster.debuggerStopped(); return; } } throw e; } finally { myState = Debugger.State.STOPPED; close(); } } public void addListener(Listener listener) { myEventDispatcher.addListener(listener); } public void removeListener(Listener listener) { myEventDispatcher.removeListener(listener); } public Debugger getClient() { return myClient; } public Debugger.State getCurrentState() { return myState; } public void pause() { myClient.pause(); } public void resume() { myClient.resume(); } public void stop() { try { myClient.stop(false); } catch (DebuggerStoppedException ignore) { } } public void stepOver() { myClient.step(); } public void stepInto() { myClient.stepInto(); } public boolean canRunTo(final XSourcePosition position) { return XsltBreakpointHandler.getActualLineNumber(myProject, position) != -1; } public void runTo(final PsiFile file, final XSourcePosition position) { assert myTempBreakpoint == null; final int lineNumber = XsltBreakpointHandler.getActualLineNumber(myProject, position); final String uri = XsltBreakpointHandler.getFileURL(file.getVirtualFile()); myTempBreakpoint = myClient.getBreakpointManager().setBreakpoint(uri, lineNumber); resume(); } @Nullable public static Editor openLocation(Project project, @NotNull String uri, int lineNumber) { try { final VirtualFile file = VfsUtil.findFileByURL(new URI(uri).toURL()); final OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file, lineNumber, 0); descriptor.navigate(true); return FileEditorManager.getInstance(project).openTextEditor(descriptor, true); } catch (MalformedURLException | URISyntaxException e) { e.printStackTrace(); return null; } } public synchronized void close() { if (myClosed) return; myClosed = true; try { myClient.stop(true); } catch (DebuggerStoppedException e) { // OK } finally { myProcess.destroyProcess(); } } @Override public void dispose() { detach(myProcess); } @NotNull public static XsltDebuggerSession create(Project project, @NotNull ProcessHandler process, Debugger client) { final XsltDebuggerSession session = new XsltDebuggerSession(project, process, client); process.putUserData(DEBUGGER_SESSION, session); return session; } public static XsltDebuggerSession getInstance(@NotNull ProcessHandler process) { return process.getUserData(DEBUGGER_SESSION); } public static void detach(ProcessHandler processHandler) { processHandler.putUserData(DEBUGGER_SESSION, null); } public interface Listener extends EventListener { void debuggerSuspended(); void debuggerResumed(); void debuggerStopped(); } }