package org.intellij.plugins.xsltDebugger.impl; import com.intellij.icons.AllIcons; import com.intellij.lang.ASTNode; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.breakpoints.XBreakpointHandler; import com.intellij.xdebugger.breakpoints.XBreakpointProperties; import com.intellij.xdebugger.breakpoints.XLineBreakpoint; import org.intellij.plugins.xsltDebugger.VMPausedException; import org.intellij.plugins.xsltDebugger.XsltBreakpointType; import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class XsltBreakpointHandler extends XBreakpointHandler<XLineBreakpoint<XBreakpointProperties>> { private XsltDebugProcess myXsltDebugProcess; public XsltBreakpointHandler(XsltDebugProcess xsltDebugProcess, final Class<? extends XsltBreakpointType> typeClass) { super(typeClass); myXsltDebugProcess = xsltDebugProcess; } @Override public void registerBreakpoint(@NotNull XLineBreakpoint<XBreakpointProperties> breakpoint) { final XSourcePosition sourcePosition = breakpoint.getSourcePosition(); if (sourcePosition == null || !sourcePosition.getFile().exists() || !sourcePosition.getFile().isValid()) { // ??? return; } final VirtualFile file = sourcePosition.getFile(); final Project project = myXsltDebugProcess.getSession().getProject(); final String fileURL = getFileURL(file); final int lineNumber = getActualLineNumber(breakpoint, project); if (lineNumber == -1) { final XDebugSession session = myXsltDebugProcess.getSession(); session.updateBreakpointPresentation(breakpoint, AllIcons.Debugger.Db_invalid_breakpoint, "Unsupported breakpoint position"); return; } try { final BreakpointManager manager = myXsltDebugProcess.getBreakpointManager(); Breakpoint bp; if ((bp = manager.getBreakpoint(fileURL, lineNumber)) != null) { bp.setEnabled(true); } else { manager.setBreakpoint(fileURL, lineNumber); } } catch (DebuggerStoppedException ignore) { } catch (VMPausedException e) { final XDebugSession session = myXsltDebugProcess.getSession(); session.reportMessage("Target VM is not responding. Breakpoint can not be set", MessageType.ERROR); session.updateBreakpointPresentation(breakpoint, AllIcons.Debugger.Db_invalid_breakpoint, "Target VM is not responding. Breakpoint can not be set"); } } public static String getFileURL(VirtualFile file) { return VfsUtil.virtualToIoFile(file).toURI().toASCIIString(); } @Override public void unregisterBreakpoint(@NotNull XLineBreakpoint<XBreakpointProperties> breakpoint, final boolean temporary) { final XSourcePosition sourcePosition = breakpoint.getSourcePosition(); if (sourcePosition == null || !sourcePosition.getFile().exists() || !sourcePosition.getFile().isValid()) { // ??? return; } final VirtualFile file = sourcePosition.getFile(); final Project project = myXsltDebugProcess.getSession().getProject(); final String fileURL = getFileURL(file); final int lineNumber = getActualLineNumber(breakpoint, project); try { final BreakpointManager manager = myXsltDebugProcess.getBreakpointManager(); if (temporary) { final Breakpoint bp = manager.getBreakpoint(fileURL, lineNumber); if (bp != null) { bp.setEnabled(false); } } else { manager.removeBreakpoint(fileURL, lineNumber); } } catch (DebuggerStoppedException ignore) { } catch (VMPausedException e) { myXsltDebugProcess.getSession().reportMessage("Target VM is not responding. Breakpoint can not be removed", MessageType.ERROR); } } public static int getActualLineNumber(XLineBreakpoint breakpoint, Project project) { return getActualLineNumber(project, breakpoint.getSourcePosition()); } public static int getActualLineNumber(Project project, @Nullable XSourcePosition position) { if (position == null) { return -1; } final PsiElement element = findContextElement(project, position); if (element == null) { return -1; } if (element instanceof XmlToken) { final IElementType tokenType = ((XmlToken)element).getTokenType(); if (tokenType == XmlTokenType.XML_START_TAG_START || tokenType == XmlTokenType.XML_NAME) { final PsiManager psiManager = PsiManager.getInstance(project); final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); final PsiFile psiFile = psiManager.findFile(position.getFile()); if (psiFile == null) { return -1; } final Document document = documentManager.getDocument(psiFile); if (document == null) { return -1; } if (document.getLineNumber(element.getTextRange().getStartOffset()) == position.getLine()) { final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class, false); if (tag != null) { final ASTNode node = tag.getNode(); assert node != null; // TODO: re-check if/when Xalan is supported final ASTNode end = XmlChildRole.START_TAG_END_FINDER.findChild(node); if (end != null) { return document.getLineNumber(end.getTextRange().getEndOffset()) + 1; } else { final ASTNode end2 = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(node); if (end2 != null) { return document.getLineNumber(end2.getTextRange().getEndOffset()) + 1; } } } } } } return -1; } @Nullable public static PsiElement findContextElement(Project project, @Nullable XSourcePosition position) { if (position == null) { return null; } final PsiFile file = PsiManager.getInstance(project).findFile(position.getFile()); if (file == null) { return null; } int offset = -1; final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document != null && document.getLineCount() > position.getLine() && position.getLine() >= 0) { offset = document.getLineStartOffset(position.getLine()); } if (offset < 0) { offset = position.getOffset(); } PsiElement contextElement = file.findElementAt(offset); while (contextElement != null && !(contextElement instanceof XmlElement)) { contextElement = PsiTreeUtil.nextLeaf(contextElement); } return contextElement; } }