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
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
;
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
;
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");
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
) {
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() {
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() {
108 protected void reload(PsiFile 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())) {
123 ApplicationManager
.getApplication().runReadAction(new Runnable() {
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());
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
) {
170 catch(Exception ex
) {
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
)) {
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
) {
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) {
229 String name
= DebuggerUtilsEx
.getQualifiedClassName(thisObject
.referenceType().name(), getProject());
233 ClassFilter
[] filters
= getClassFilters();
234 boolean matches
= false;
235 for (ClassFilter classFilter
: filters
) {
236 if (classFilter
.isEnabled() && classFilter
.matches(name
)) {
245 ClassFilter
[] ifilters
= getClassExclusionFilters();
246 for (ClassFilter classFilter
: ifilters
) {
247 if (classFilter
.isEnabled() && classFilter
.matches(name
)) {
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);
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();
269 String packageName
= null;
271 final int dotIndex
= className
.lastIndexOf(".");
273 info
.append(className
.substring(dotIndex
+ 1));
274 packageName
= className
.substring(0, dotIndex
);
277 info
.append(className
);
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());
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
) {
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;
315 public String
getEventMessage(LocatableEvent event
) {
316 final Location location
= event
.location();
318 return DebuggerBundle
.message(
319 "status.line.breakpoint.reached",
320 location
.declaringType().name() + "." + location
.method().name(),
321 location
.sourceName(),
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(),
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
)) {
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()) {
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
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)) {
374 PsiElement child
= element
;
375 while(element
!= null) {
377 final int offset
= element
.getTextOffset();
379 if (document
.getLineNumber(offset
) != lineIndex
) {
384 element
= element
.getParent();
387 if(child
instanceof PsiMethod
&& child
.getTextRange().getEndOffset() >= document
.getLineEndOffset(lineIndex
)) {
388 PsiCodeBlock body
= ((PsiMethod
)child
).getBody();
393 PsiStatement
[] statements
= body
.getStatements();
394 canAdd
[0] = statements
.length
> 0 && document
.getLineNumber(statements
[0].getTextOffset()) == lineIndex
;
407 public @Nullable String
getMethodName() {