| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * 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 com.intellij.debugger.ui.breakpoints; |
| |
| import com.intellij.CommonBundle; |
| import com.intellij.debugger.*; |
| import com.intellij.debugger.engine.*; |
| import com.intellij.debugger.engine.events.DebuggerCommandImpl; |
| import com.intellij.debugger.engine.requests.RequestManagerImpl; |
| import com.intellij.debugger.impl.DebuggerContextImpl; |
| import com.intellij.debugger.settings.DebuggerSettings; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.fileEditor.FileDocumentManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.InvalidDataException; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiClass; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.jsp.JspFile; |
| import com.intellij.ui.ColorUtil; |
| import com.intellij.ui.JBColor; |
| import com.intellij.ui.classFilter.ClassFilter; |
| import com.intellij.util.StringBuilderSpinAllocator; |
| import com.intellij.xdebugger.XDebuggerManager; |
| import com.intellij.xdebugger.XSourcePosition; |
| import com.intellij.xdebugger.breakpoints.XBreakpoint; |
| import com.intellij.xdebugger.breakpoints.XBreakpointManager; |
| import com.intellij.xdebugger.breakpoints.XLineBreakpoint; |
| import com.intellij.xml.util.XmlStringUtil; |
| import com.sun.jdi.ReferenceType; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties; |
| |
| import javax.swing.*; |
| |
| /** |
| * User: lex |
| * Date: Sep 2, 2003 |
| * Time: 3:22:55 PM |
| */ |
| public abstract class BreakpointWithHighlighter<P extends JavaBreakpointProperties> extends Breakpoint<P> { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter"); |
| |
| @Nullable |
| private SourcePosition mySourcePosition; |
| |
| private boolean myVisible = true; |
| private volatile Icon myIcon = getSetIcon(false); |
| @Nullable |
| private String myClassName; |
| @Nullable |
| private String myPackageName; |
| @Nullable |
| private String myInvalidMessage; |
| |
| protected abstract void createRequestForPreparedClass(final DebugProcessImpl debugProcess, final ReferenceType classType); |
| |
| protected abstract Icon getDisabledIcon(boolean isMuted); |
| |
| protected abstract Icon getInvalidIcon(boolean isMuted); |
| |
| protected abstract Icon getSetIcon(boolean isMuted); |
| |
| protected abstract Icon getVerifiedIcon(boolean isMuted); |
| |
| protected abstract Icon getVerifiedWarningsIcon(boolean isMuted); |
| |
| @Override |
| public Icon getIcon() { |
| return myIcon; |
| } |
| |
| @Nullable |
| @Override |
| public String getClassName() { |
| return myClassName; |
| } |
| |
| @Override |
| @Nullable |
| public String getShortClassName() { |
| final SourcePosition pos = getSourcePosition(); |
| if (pos != null) { |
| if (pos.getFile() instanceof JspFile) { |
| return getClassName(); |
| } |
| } |
| return super.getShortClassName(); |
| } |
| |
| @Nullable |
| @Override |
| public String getPackageName() { |
| return myPackageName; |
| } |
| |
| @Nullable |
| public BreakpointWithHighlighter init() { |
| if (!isValid()) { |
| return null; |
| } |
| |
| if (!ApplicationManager.getApplication().isUnitTestMode()) { |
| updateUI(); |
| updateGutter(); |
| } |
| |
| return this; |
| } |
| |
| private void updateCaches(DebugProcessImpl debugProcess) { |
| myIcon = calcIcon(debugProcess); |
| myClassName = JVMNameUtil.getSourcePositionClassDisplayName(debugProcess, getSourcePosition()); |
| myPackageName = JVMNameUtil.getSourcePositionPackageDisplayName(debugProcess, getSourcePosition()); |
| } |
| |
| private Icon calcIcon(@Nullable DebugProcessImpl debugProcess) { |
| final boolean muted = debugProcess != null && isMuted(debugProcess); |
| if (!isEnabled()) { |
| return getDisabledIcon(muted); |
| } |
| |
| myInvalidMessage = ""; |
| |
| if (!isValid()) { |
| return getInvalidIcon(muted); |
| } |
| |
| if (debugProcess == null) { |
| return getSetIcon(muted); |
| } |
| |
| final RequestManagerImpl requestsManager = debugProcess.getRequestsManager(); |
| |
| final boolean isVerified = myCachedVerifiedState || requestsManager.isVerified(this); |
| |
| final String warning = requestsManager.getWarning(this); |
| if (warning != null) { |
| myInvalidMessage = warning; |
| if (!isVerified) { |
| return getInvalidIcon(muted); |
| } |
| return getVerifiedWarningsIcon(muted); |
| } |
| |
| if (isVerified) { |
| return getVerifiedIcon(muted); |
| } |
| |
| return getSetIcon(muted); |
| } |
| |
| protected BreakpointWithHighlighter(@NotNull Project project, XBreakpoint xBreakpoint) { |
| //for persistency |
| super(project, xBreakpoint); |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| reload(); |
| } |
| }); |
| } |
| |
| @Override |
| public boolean isValid() { |
| return isPositionValid(myXBreakpoint.getSourcePosition()); |
| } |
| |
| protected static boolean isPositionValid(@Nullable final XSourcePosition sourcePosition) { |
| return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| return sourcePosition != null && sourcePosition.getFile().isValid() ? Boolean.TRUE : Boolean.FALSE; |
| } |
| }).booleanValue(); |
| } |
| |
| @Nullable |
| public SourcePosition getSourcePosition() { |
| return mySourcePosition; |
| } |
| |
| @SuppressWarnings("HardCodedStringLiteral") |
| @NotNull |
| public String getDescription() { |
| final StringBuilder buf = StringBuilderSpinAllocator.alloc(); |
| try { |
| buf.append("<html><body>"); |
| buf.append(getDisplayName()); |
| if (myInvalidMessage != null && !myInvalidMessage.isEmpty()) { |
| buf.append("<br><font color='#").append(ColorUtil.toHex(JBColor.RED)).append("'>"); |
| buf.append(DebuggerBundle.message("breakpoint.warning", myInvalidMessage)); |
| buf.append("</font>"); |
| } |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.suspend.policy")).append(" : "); |
| if (DebuggerSettings.SUSPEND_NONE.equals(getSuspendPolicy()) || !isSuspend()) { |
| buf.append(DebuggerBundle.message("breakpoint.properties.panel.option.suspend.none")); |
| } |
| else if (DebuggerSettings.SUSPEND_ALL.equals(getSuspendPolicy())) { |
| buf.append(DebuggerBundle.message("breakpoint.properties.panel.option.suspend.all")); |
| } |
| else if (DebuggerSettings.SUSPEND_THREAD.equals(getSuspendPolicy())) { |
| buf.append(DebuggerBundle.message("breakpoint.properties.panel.option.suspend.thread")); |
| } |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.log.message")).append(": "); |
| buf.append(isLogEnabled() ? CommonBundle.getYesButtonText() : CommonBundle.getNoButtonText()); |
| if (isLogExpressionEnabled()) { |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.log.expression")).append(": "); |
| buf.append(XmlStringUtil.escapeString(getLogMessage().getText())); |
| } |
| if (isConditionEnabled() && getCondition() != null && getCondition().getText() != null && !getCondition().getText().isEmpty()) { |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.condition")).append(": "); |
| buf.append(XmlStringUtil.escapeString(getCondition().getText())); |
| } |
| if (isCountFilterEnabled()) { |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.pass.count")).append(": "); |
| buf.append(getCountFilter()); |
| } |
| if (isClassFiltersEnabled()) { |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.class.filters")).append(": "); |
| ClassFilter[] classFilters = getClassFilters(); |
| for (ClassFilter classFilter : classFilters) { |
| buf.append(classFilter.getPattern()).append(" "); |
| } |
| } |
| if (isInstanceFiltersEnabled()) { |
| buf.append(" <br> "); |
| buf.append(DebuggerBundle.message("breakpoint.property.name.instance.filters")); |
| InstanceFilter[] instanceFilters = getInstanceFilters(); |
| for (InstanceFilter instanceFilter : instanceFilters) { |
| buf.append(Long.toString(instanceFilter.getId())).append(" "); |
| } |
| } |
| buf.append("</body></html>"); |
| return buf.toString(); |
| } |
| finally { |
| StringBuilderSpinAllocator.dispose(buf); |
| } |
| } |
| |
| @Override |
| public void reload() { |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| XSourcePosition position = myXBreakpoint.getSourcePosition(); |
| PsiFile psiFile = getPsiFile(); |
| if (position != null && psiFile != null) { |
| mySourcePosition = SourcePosition.createFromLine(psiFile, position.getLine()); |
| reload(psiFile); |
| } |
| else { |
| mySourcePosition = null; |
| } |
| } |
| |
| @Nullable |
| public PsiFile getPsiFile() { |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| XSourcePosition position = myXBreakpoint.getSourcePosition(); |
| if (position != null) { |
| VirtualFile file = position.getFile(); |
| if (file.isValid()) { |
| return PsiManager.getInstance(myProject).findFile(file); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void createRequest(@NotNull DebugProcessImpl debugProcess) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| // check is this breakpoint is enabled, vm reference is valid and there're no requests created yet |
| if (!shouldCreateRequest(debugProcess)) { |
| return; |
| } |
| |
| if (!isValid()) { |
| return; |
| } |
| |
| SourcePosition position = getSourcePosition(); |
| if (position != null) { |
| createOrWaitPrepare(debugProcess, position); |
| } |
| else { |
| LOG.error("Unable to create request for breakpoint with null position: " + getDisplayName() + " at " + myXBreakpoint.getSourcePosition()); |
| } |
| updateUI(); |
| } |
| |
| protected boolean isMuted(@NotNull final DebugProcessImpl debugProcess) { |
| return debugProcess.areBreakpointsMuted(); |
| } |
| |
| @Override |
| public void processClassPrepare(final DebugProcess debugProcess, final ReferenceType classType) { |
| if (!isEnabled() || !isValid()) { |
| return; |
| } |
| createRequestForPreparedClass((DebugProcessImpl)debugProcess, classType); |
| updateUI(); |
| } |
| |
| /** |
| * updates the state of breakpoint and all the related UI widgets etc |
| */ |
| @Override |
| public final void updateUI() { |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| return; |
| } |
| final Project project = getProject(); |
| DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() { |
| @Override |
| public void run() { |
| if (!isValid()) { |
| return; |
| } |
| |
| DebuggerContextImpl context = DebuggerManagerEx.getInstanceEx(project).getContext(); |
| final DebugProcessImpl debugProcess = context.getDebugProcess(); |
| if (debugProcess == null || !debugProcess.isAttached()) { |
| updateCaches(null); |
| updateGutter(); |
| } |
| else { |
| debugProcess.getManagerThread().invoke(new DebuggerCommandImpl() { |
| @Override |
| protected void action() throws Exception { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| updateCaches(debugProcess); |
| } |
| }); |
| DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() { |
| @Override |
| public void run() { |
| updateGutter(); |
| } |
| }); |
| } |
| }); |
| } |
| } |
| }); |
| } |
| |
| private void updateGutter() { |
| if (myVisible) { |
| if (isValid()) { |
| final XBreakpointManager breakpointManager = XDebuggerManager.getInstance(myProject).getBreakpointManager(); |
| breakpointManager.updateBreakpointPresentation((XLineBreakpoint)myXBreakpoint, getIcon(), null); |
| } |
| } |
| } |
| |
| public boolean isAt(@NotNull Document document, int offset) { |
| final VirtualFile file = FileDocumentManager.getInstance().getFile(document); |
| int line = document.getLineNumber(offset); |
| XSourcePosition position = myXBreakpoint.getSourcePosition(); |
| return position != null && position.getLine() == line && position.getFile().equals(file); |
| } |
| |
| protected void reload(PsiFile psiFile) { |
| } |
| |
| @Override |
| public PsiClass getPsiClass() { |
| final SourcePosition sourcePosition = getSourcePosition(); |
| return getPsiClassAt(sourcePosition); |
| } |
| |
| protected static PsiClass getPsiClassAt(@Nullable final SourcePosition sourcePosition) { |
| return ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() { |
| @Nullable |
| @Override |
| public PsiClass compute() { |
| return JVMNameUtil.getClassAt(sourcePosition); |
| } |
| }); |
| } |
| |
| @Override |
| public abstract Key<? extends BreakpointWithHighlighter> getCategory(); |
| |
| public boolean isVisible() { |
| return myVisible; |
| } |
| |
| public void setVisible(boolean visible) { |
| myVisible = visible; |
| } |
| |
| @Nullable |
| public Document getDocument() { |
| final PsiFile file = getPsiFile(); |
| if (file != null) { |
| return PsiDocumentManager.getInstance(getProject()).getDocument(file); |
| } |
| return null; |
| } |
| |
| public int getLineIndex() { |
| XSourcePosition sourcePosition = myXBreakpoint.getSourcePosition(); |
| return sourcePosition != null ? sourcePosition.getLine() : -1; |
| } |
| |
| protected String getFileName() { |
| XSourcePosition sourcePosition = myXBreakpoint.getSourcePosition(); |
| return sourcePosition != null ? sourcePosition.getFile().getName() : ""; |
| } |
| |
| @Override |
| public void readExternal(@NotNull Element breakpointNode) throws InvalidDataException { |
| super.readExternal(breakpointNode); |
| //noinspection HardCodedStringLiteral |
| //final String url = breakpointNode.getAttributeValue("url"); |
| |
| //noinspection HardCodedStringLiteral |
| final String className = breakpointNode.getAttributeValue("class"); |
| if (className != null) { |
| myClassName = className; |
| } |
| |
| //noinspection HardCodedStringLiteral |
| final String packageName = breakpointNode.getAttributeValue("package"); |
| if (packageName != null) { |
| myPackageName = packageName; |
| } |
| } |
| } |