package com.intellij.debugger.engine; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.NoDataException; import com.intellij.debugger.PositionManager; import com.intellij.debugger.SourcePosition; import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.lang.StdLanguages; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.sun.jdi.Location; import com.sun.jdi.ReferenceType; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.request.ClassPrepareRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; public class JavaRebelPositionManager implements PositionManager { private final DebugProcess myDebugProcess; public JavaRebelPositionManager(DebugProcess debugProcess) { myDebugProcess = debugProcess; } public DebugProcess getDebugProcess() { return myDebugProcess; } @NotNull public List locationsOfLine(ReferenceType type, SourcePosition position) throws NoDataException { throw new NoDataException(); } public ClassPrepareRequest createPrepareRequest(final ClassPrepareRequestor requestor, final SourcePosition position) { PsiClass psiClass = getClassAt(position); if(psiClass == null) { return null; } final PsiClass parent = getTopLevelParentClass(psiClass); final String baseQName = (parent != null) ? DebuggerManager.getInstance(parent.getProject()).getVMClassQualifiedName(parent) : null; if (baseQName == null) { return null; // no suitable class found for this name } final ClassPrepareRequestor waitRequestor = new ClassPrepareRequestor() { public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) { final PositionManager rootPositionManager = debuggerProcess.getPositionManager(); try { if (rootPositionManager.locationsOfLine(referenceType, position).size() > 0) { requestor.processClassPrepare(debuggerProcess, referenceType); return; } } catch (NoDataException ignored) { } try { final List positionClasses = rootPositionManager.getAllClasses(position); if (positionClasses.contains(referenceType)) { requestor.processClassPrepare(debuggerProcess, referenceType); } } catch (NoDataException ignored) { } } }; return myDebugProcess.getRequestsManager().createClassPrepareRequest(waitRequestor, baseQName + "*"); } public SourcePosition getSourcePosition(final Location location) throws NoDataException{ throw new NoDataException(); } private boolean myEnabled = true; @NotNull public List getAllClasses(final SourcePosition classPosition) throws NoDataException{ if (!myEnabled) { throw new NoDataException(); } myEnabled = false; try { final List classes = myDebugProcess.getPositionManager().getAllClasses(classPosition); final List result = new ArrayList(classes.size()); // throw out all reference types with non-skewed (original) names for (final ReferenceType refType : classes) { if (isSkewedClass(refType)) { result.add(refType); } else { // replace the original class with its skewed version final int lineNumber = classPosition.getLine() + 1; // class position's line number is zero-based, while JDI's line numbers are not final String prefix = refType.name() + "$$"; for (ReferenceType nested : refType.nestedTypes()) { try { if (nested.name().startsWith(prefix) && nested.locationsOfLine(lineNumber).size() > 0) { result.add(nested); break; } } catch (AbsentInformationException ignored) { } } } } return result; } finally { myEnabled = true; } } private static boolean isSkewedClass(final ReferenceType refType) { // possibly too general, ok to replace it with more appropriate check return refType.name().contains("$$"); } private static PsiClass getTopLevelParentClass(PsiClass psiClass) { PsiClass enclosing = PsiTreeUtil.getParentOfType(psiClass, PsiClass.class, true); while (enclosing != null) { psiClass = enclosing; enclosing = PsiTreeUtil.getParentOfType(enclosing, PsiClass.class, true); } return psiClass; } private static @Nullable PsiClass getClassAt(SourcePosition position) { final int offset = position.getOffset(); if (offset < 0) { return null; } final PsiFile psiFile = position.getFile(); final PsiElement element; if (psiFile instanceof PsiCompiledElement) { element = psiFile.findElementAt(offset); } else { final FileViewProvider viewProvider = psiFile.getViewProvider(); element = viewProvider.findElementAt(offset, StdLanguages.JAVA); } return PsiTreeUtil.getParentOfType(element, PsiClass.class, false); } }