ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / java / debugger / impl / src / com / intellij / debugger / ui / breakpoints / LineBreakpoint.java
blob3449a440ffba82d854b322230927571279368650
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Class LineBreakpoint
19 * @author Jeka
21 package com.intellij.debugger.ui.breakpoints;
23 import com.intellij.debugger.DebuggerBundle;
24 import com.intellij.debugger.DebuggerManagerEx;
25 import com.intellij.debugger.SourcePosition;
26 import com.intellij.debugger.engine.DebugProcessImpl;
27 import com.intellij.debugger.engine.evaluation.EvaluateException;
28 import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
29 import com.intellij.debugger.impl.DebuggerUtilsEx;
30 import com.intellij.debugger.impl.PositionUtil;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.diagnostic.Logger;
33 import com.intellij.openapi.editor.Document;
34 import com.intellij.openapi.editor.markup.RangeHighlighter;
35 import com.intellij.openapi.fileEditor.FileDocumentManager;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.util.Computable;
38 import com.intellij.openapi.util.IconLoader;
39 import com.intellij.openapi.util.Key;
40 import com.intellij.openapi.vfs.LocalFileSystem;
41 import com.intellij.openapi.vfs.VirtualFile;
42 import com.intellij.psi.*;
43 import com.intellij.psi.jsp.JspFile;
44 import com.intellij.psi.search.GlobalSearchScope;
45 import com.intellij.psi.util.PsiTreeUtil;
46 import com.intellij.ui.classFilter.ClassFilter;
47 import com.intellij.util.Processor;
48 import com.intellij.util.StringBuilderSpinAllocator;
49 import com.intellij.xdebugger.XDebuggerUtil;
50 import com.intellij.xdebugger.ui.DebuggerIcons;
51 import com.sun.jdi.*;
52 import com.sun.jdi.event.LocatableEvent;
53 import com.sun.jdi.request.BreakpointRequest;
54 import org.jetbrains.annotations.NonNls;
55 import org.jetbrains.annotations.NotNull;
56 import org.jetbrains.annotations.Nullable;
58 import javax.swing.*;
59 import java.util.ArrayList;
60 import java.util.Collection;
61 import java.util.Collections;
62 import java.util.List;
64 public class LineBreakpoint extends BreakpointWithHighlighter {
65 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.LineBreakpoint");
67 // icons
68 public static Icon ICON = DebuggerIcons.ENABLED_BREAKPOINT_ICON;
69 public static final Icon DISABLED_ICON = DebuggerIcons.DISABLED_BREAKPOINT_ICON;
70 private static final Icon ourVerifiedWarningsIcon = IconLoader.getIcon("/debugger/db_verified_warning_breakpoint.png");
72 private String myMethodName;
73 public static final @NonNls Key<LineBreakpoint> CATEGORY = BreakpointCategory.lookup("line_breakpoints");
75 protected LineBreakpoint(Project project) {
76 super(project);
79 protected LineBreakpoint(Project project, RangeHighlighter highlighter) {
80 super(project, highlighter);
83 protected Icon getDisabledIcon() {
84 final Breakpoint master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this);
85 return master == null? DISABLED_ICON : DebuggerIcons.DISABLED_DEPENDENT_BREAKPOINT_ICON;
88 protected Icon getSetIcon() {
89 return ICON;
92 protected Icon getInvalidIcon() {
93 return DebuggerIcons.INVALID_BREAKPOINT_ICON;
96 protected Icon getVerifiedIcon() {
97 return DebuggerIcons.VERIFIED_BREAKPOINT_ICON;
100 protected Icon getVerifiedWarningsIcon() {
101 return ourVerifiedWarningsIcon;
104 public Key<LineBreakpoint> getCategory() {
105 return CATEGORY;
108 protected void reload(PsiFile file) {
109 super.reload(file);
110 myMethodName = findMethodName(file, getHighlighter().getStartOffset());
113 protected void createOrWaitPrepare(DebugProcessImpl debugProcess, String classToBeLoaded) {
114 if (isInScopeOf(debugProcess, classToBeLoaded)) {
115 super.createOrWaitPrepare(debugProcess, classToBeLoaded);
119 protected void createRequestForPreparedClass(final DebugProcessImpl debugProcess, final ReferenceType classType) {
120 if (!isInScopeOf(debugProcess, classType.name())) {
121 return;
123 ApplicationManager.getApplication().runReadAction(new Runnable() {
124 public void run() {
125 try {
126 List<Location> locs = debugProcess.getPositionManager().locationsOfLine(classType, getSourcePosition());
127 if (locs.size() > 0) {
128 for (final Location location : locs) {
129 if (LOG.isDebugEnabled()) {
130 LOG.debug("Found location for reference type " + classType.name() + " at line " + getLineIndex() + "; isObsolete: " + (debugProcess.getVirtualMachineProxy().versionHigher("1.4") && location.method().isObsolete()));
132 BreakpointRequest request = debugProcess.getRequestsManager().createBreakpointRequest(LineBreakpoint.this, location);
133 debugProcess.getRequestsManager().enableRequest(request);
134 if (LOG.isDebugEnabled()) {
135 LOG.debug("Created breakpoint request for reference type " + classType.name() + " at line " + getLineIndex());
139 else {
140 // there's no executable code in this class
141 debugProcess.getRequestsManager().setInvalid(LineBreakpoint.this, DebuggerBundle.message(
142 "error.invalid.breakpoint.no.executable.code", (getLineIndex() + 1), classType.name())
144 if (LOG.isDebugEnabled()) {
145 LOG.debug("No locations of type " + classType.name() + " found at line " + getLineIndex());
149 catch (ClassNotPreparedException ex) {
150 if (LOG.isDebugEnabled()) {
151 LOG.debug("ClassNotPreparedException: " + ex.getMessage());
153 // there's a chance to add a breakpoint when the class is prepared
155 catch (ObjectCollectedException ex) {
156 if (LOG.isDebugEnabled()) {
157 LOG.debug("ObjectCollectedException: " + ex.getMessage());
159 // there's a chance to add a breakpoint when the class is prepared
161 catch (InvalidLineNumberException ex) {
162 if (LOG.isDebugEnabled()) {
163 LOG.debug("InvalidLineNumberException: " + ex.getMessage());
165 debugProcess.getRequestsManager().setInvalid(LineBreakpoint.this, DebuggerBundle.message("error.invalid.breakpoint.bad.line.number"));
167 catch (InternalException ex) {
168 LOG.info(ex);
170 catch(Exception ex) {
171 LOG.info(ex);
173 updateUI();
178 private boolean isInScopeOf(DebugProcessImpl debugProcess, String className) {
179 final SourcePosition position = getSourcePosition();
180 if (position != null) {
181 final VirtualFile breakpointFile = position.getFile().getVirtualFile();
182 if (breakpointFile != null) {
183 final Collection<VirtualFile> candidates = findClassFileCandidates(className, debugProcess.getSearchScope());
184 if (!candidates.isEmpty()) {
185 for (VirtualFile classFile : candidates) {
186 if (breakpointFile.equals(classFile)) {
187 return true;
190 return false;
194 return true;
197 @NotNull
198 private Collection<VirtualFile> findClassFileCandidates(final String className, final GlobalSearchScope scope) {
199 final int dollarIndex = className.indexOf("$");
200 final String topLevelClassName = dollarIndex >= 0? className.substring(0, dollarIndex) : className;
201 return ApplicationManager.getApplication().runReadAction(new Computable<Collection<VirtualFile>>() {
202 public Collection<VirtualFile> compute() {
203 final PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(topLevelClassName, scope);
204 if (classes.length == 0) {
205 return Collections.emptyList();
207 final List<VirtualFile> list = new ArrayList<VirtualFile>(classes.length);
208 for (PsiClass aClass : classes) {
209 final PsiFile psiFile = aClass.getContainingFile();
210 if (psiFile != null) {
211 final VirtualFile vFile = psiFile.getVirtualFile();
212 if (vFile != null && vFile.getFileSystem() instanceof LocalFileSystem) {
213 list.add(vFile);
217 return list;
222 public boolean evaluateCondition(EvaluationContextImpl context, LocatableEvent event) throws EvaluateException {
223 if(CLASS_FILTERS_ENABLED){
224 Value value = context.getThisObject();
225 ObjectReference thisObject = (ObjectReference)value;
226 if(thisObject == null) {
227 return false;
229 String name = DebuggerUtilsEx.getQualifiedClassName(thisObject.referenceType().name(), getProject());
230 if(name == null) {
231 return false;
233 ClassFilter [] filters = getClassFilters();
234 boolean matches = false;
235 for (ClassFilter classFilter : filters) {
236 if (classFilter.isEnabled() && classFilter.matches(name)) {
237 matches = true;
238 break;
241 if(!matches) {
242 return false;
245 ClassFilter [] ifilters = getClassExclusionFilters();
246 for (ClassFilter classFilter : ifilters) {
247 if (classFilter.isEnabled() && classFilter.matches(name)) {
248 return false;
252 return super.evaluateCondition(context, event);
255 public String toString() {
256 return getDescription();
260 public String getDisplayName() {
261 final int lineNumber = (getHighlighter().getDocument().getLineNumber(getHighlighter().getStartOffset()) + 1);
262 if(isValid()) {
263 final String className = getClassName();
264 final boolean hasClassInfo = className != null && className.length() > 0;
265 final boolean hasMethodInfo = myMethodName != null && myMethodName.length() > 0;
266 if (hasClassInfo || hasMethodInfo) {
267 final StringBuilder info = StringBuilderSpinAllocator.alloc();
268 try {
269 String packageName = null;
270 if (hasClassInfo) {
271 final int dotIndex = className.lastIndexOf(".");
272 if (dotIndex >= 0) {
273 info.append(className.substring(dotIndex + 1));
274 packageName = className.substring(0, dotIndex);
276 else {
277 info.append(className);
280 if(hasMethodInfo) {
281 if (hasClassInfo) {
282 info.append(".");
284 info.append(myMethodName);
286 if (packageName != null) {
287 info.append(" (").append(packageName).append(")");
289 return DebuggerBundle.message("line.breakpoint.display.name.with.class.or.method", lineNumber, info.toString());
291 finally {
292 StringBuilderSpinAllocator.dispose(info);
295 return DebuggerBundle.message("line.breakpoint.display.name", lineNumber);
297 return DebuggerBundle.message("status.breakpoint.invalid");
300 private static @Nullable String findMethodName(final PsiFile file, final int offset) {
301 if (file instanceof JspFile) {
302 return null;
304 if (file instanceof PsiJavaFile) {
305 return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
306 public String compute() {
307 final PsiMethod method = DebuggerUtilsEx.findPsiMethod(file, offset);
308 return method != null? method.getName() + "()" : null;
312 return null;
315 public String getEventMessage(LocatableEvent event) {
316 final Location location = event.location();
317 try {
318 return DebuggerBundle.message(
319 "status.line.breakpoint.reached",
320 location.declaringType().name() + "." + location.method().name(),
321 location.sourceName(),
322 getLineIndex() + 1
325 catch (AbsentInformationException e) {
326 final String sourceName = getSourcePosition().getFile().getName();
327 return DebuggerBundle.message(
328 "status.line.breakpoint.reached",
329 location.declaringType().name() + "." + location.method().name(),
330 sourceName,
331 getLineIndex() + 1
336 public PsiElement getEvaluationElement() {
337 return PositionUtil.getContextElement(getSourcePosition());
340 protected static LineBreakpoint create(Project project, Document document, int lineIndex) {
341 VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
342 if (virtualFile == null) return null;
344 LineBreakpoint breakpoint = new LineBreakpoint(project, createHighlighter(project, document, lineIndex));
345 return (LineBreakpoint)breakpoint.init();
348 public boolean canMoveTo(SourcePosition position) {
349 if (!super.canMoveTo(position)) {
350 return false;
352 final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(position.getFile());
353 return canAddLineBreakpoint(myProject, document, position.getLine());
356 public static boolean canAddLineBreakpoint(Project project, final Document document, final int lineIndex) {
357 if (lineIndex < 0 || lineIndex >= document.getLineCount()) {
358 return false;
360 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager();
361 final LineBreakpoint breakpointAtLine = breakpointManager.findBreakpoint( document, document.getLineStartOffset(lineIndex), CATEGORY);
362 if (breakpointAtLine != null) {
363 // there already exists a line breakpoint at this line
364 return false;
366 PsiDocumentManager.getInstance(project).commitDocument(document);
368 final boolean[] canAdd = new boolean[]{false};
369 XDebuggerUtil.getInstance().iterateLine(project, document, lineIndex, new Processor<PsiElement>() {
370 public boolean process(PsiElement element) {
371 if ((element instanceof PsiWhiteSpace) || (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null)) {
372 return true;
374 PsiElement child = element;
375 while(element != null) {
377 final int offset = element.getTextOffset();
378 if (offset >= 0) {
379 if (document.getLineNumber(offset) != lineIndex) {
380 break;
383 child = element;
384 element = element.getParent();
387 if(child instanceof PsiMethod && child.getTextRange().getEndOffset() >= document.getLineEndOffset(lineIndex)) {
388 PsiCodeBlock body = ((PsiMethod)child).getBody();
389 if(body == null) {
390 canAdd[0] = false;
392 else {
393 PsiStatement[] statements = body.getStatements();
394 canAdd[0] = statements.length > 0 && document.getLineNumber(statements[0].getTextOffset()) == lineIndex;
397 else {
398 canAdd[0] = true;
400 return false;
404 return canAdd[0];
407 public @Nullable String getMethodName() {
408 return myMethodName;