package org.fandev.debugger; import com.intellij.debugger.NoDataException; import com.intellij.debugger.PositionManager; import com.intellij.debugger.SourcePosition; import com.intellij.debugger.engine.CompoundPositionManager; import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.Location; import com.sun.jdi.ReferenceType; import com.sun.jdi.request.ClassPrepareRequest; import org.fandev.index.FanIndex; import org.fandev.lang.fan.psi.FanFile; import org.fandev.lang.fan.psi.api.statements.typeDefs.FanTypeDefinition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; /** * Date: Sep 2, 2009 * Time: 11:40:51 PM * * @author Dror Bereznitsky */ public class FanPositionManager implements PositionManager { private final DebugProcess myDebugProcess; public FanPositionManager(final DebugProcess debugProcess) { myDebugProcess = debugProcess; } public DebugProcess getDebugProcess() { return myDebugProcess; } public SourcePosition getSourcePosition(final Location location) throws NoDataException { if (location == null) { throw new NoDataException(); } final PsiFile psiFile = getPsiFileByLocation(getDebugProcess().getProject(), location); if (psiFile == null) { throw new NoDataException(); } int lineNumber = calcLineIndex(location); if (lineNumber < 0) { throw new NoDataException(); } return SourcePosition.createFromLine(psiFile, lineNumber); } @NotNull public List<ReferenceType> getAllClasses(final SourcePosition classPosition) throws NoDataException { final List<ReferenceType> result = ApplicationManager.getApplication().runReadAction(new Computable<List<ReferenceType>>() { public List<ReferenceType> compute() { final List<ReferenceType> result = new ArrayList<ReferenceType>(); final PsiFile file = classPosition.getFile(); if (file instanceof FanFile) { final FanTypeDefinition[] typeDefinitions = ((FanFile)file).getTypeDefinitions(); for (final FanTypeDefinition def : typeDefinitions) { final String enclosingName = def.getJavaQualifiedName(); result.addAll(myDebugProcess.getVirtualMachineProxy().classesByName(enclosingName)); } } return result; } }); return result; } @NotNull public List<Location> locationsOfLine(final ReferenceType type, final SourcePosition position) throws NoDataException { try { int line = position.getLine() + 1; final List<Location> locations = getDebugProcess().getVirtualMachineProxy().versionHigher("1.4") ? type.locationsOfLine(DebugProcessImpl.JAVA_STRATUM, null, line) : type.locationsOfLine(line); if (locations == null || locations.isEmpty()) { throw new NoDataException(); } return locations; } catch (AbsentInformationException e) { throw new NoDataException(); } } public ClassPrepareRequest createPrepareRequest(final ClassPrepareRequestor requestor, final SourcePosition position) throws NoDataException { String qName; ClassPrepareRequestor waitRequestor; String waitPrepareFor = ""; final FanTypeDefinition typeDefinition = findEnclosingTypeDefinition(position); if (typeDefinition == null){ return null; } qName = typeDefinition.getJavaQualifiedName(); waitPrepareFor = qName; waitRequestor = new ClassPrepareRequestor() { public void processClassPrepare(final DebugProcess debuggerProcess, final ReferenceType referenceType) { final CompoundPositionManager positionManager = ((DebugProcessImpl)debuggerProcess).getPositionManager(); if (positionManager.locationsOfLine(referenceType, position).size() > 0) { requestor.processClassPrepare(debuggerProcess, referenceType); } else { final List<ReferenceType> positionClasses = positionManager.getAllClasses(position); if (positionClasses.contains(referenceType)) { requestor.processClassPrepare(debuggerProcess, referenceType); } } } }; return myDebugProcess.getRequestsManager().createClassPrepareRequest(waitRequestor, waitPrepareFor); } @Nullable private PsiFile getPsiFileByLocation( final Project project, final Location location) { if (location == null) { return null; } final ReferenceType refType = location.declaringType(); if (refType == null) { return null; } // Currently deal only with Fan types if (!refType.name().startsWith("fan")) { return null; } String name = refType.name().substring(refType.name().lastIndexOf(".") + 1); final String[] names = name.split("\\$"); //Closures and functions name = names[0]; return ((FanIndex)project.getComponent(FanIndex.COMPONENT_NAME)).getFanFileByTypeName(name); } private int calcLineIndex(final Location location) { if (location == null) { return -1; } try { return location.lineNumber() - 1; } catch (InternalError e) { return -1; } } private FanTypeDefinition findEnclosingTypeDefinition(final SourcePosition position) { final PsiFile file = position.getFile(); if (!(file instanceof FanFile)) { return null; } final PsiElement element = file.findElementAt(position.getOffset()); if (element == null) { return null; } return PsiTreeUtil.getParentOfType(element, FanTypeDefinition.class); } }