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
.roots
.ProjectRootManager
;
38 import com
.intellij
.openapi
.util
.Computable
;
39 import com
.intellij
.openapi
.util
.IconLoader
;
40 import com
.intellij
.openapi
.util
.Key
;
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
.Nullable
;
58 import java
.util
.List
;
60 public class LineBreakpoint
extends BreakpointWithHighlighter
{
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.debugger.ui.breakpoints.LineBreakpoint");
64 public static Icon ICON
= DebuggerIcons
.ENABLED_BREAKPOINT_ICON
;
65 public static final Icon DISABLED_ICON
= DebuggerIcons
.DISABLED_BREAKPOINT_ICON
;
66 private static final Icon ourVerifiedWarningsIcon
= IconLoader
.getIcon("/debugger/db_verified_warning_breakpoint.png");
68 private String myMethodName
;
69 public static final @NonNls Key
<LineBreakpoint
> CATEGORY
= BreakpointCategory
.lookup("line_breakpoints");
71 protected LineBreakpoint(Project project
) {
75 protected LineBreakpoint(Project project
, RangeHighlighter highlighter
) {
76 super(project
, highlighter
);
79 protected Icon
getDisabledIcon() {
80 final Breakpoint master
= DebuggerManagerEx
.getInstanceEx(myProject
).getBreakpointManager().findMasterBreakpoint(this);
81 return master
== null? DISABLED_ICON
: DebuggerIcons
.DISABLED_DEPENDENT_BREAKPOINT_ICON
;
84 protected Icon
getSetIcon() {
88 protected Icon
getInvalidIcon() {
89 return DebuggerIcons
.INVALID_BREAKPOINT_ICON
;
92 protected Icon
getVerifiedIcon() {
93 return DebuggerIcons
.VERIFIED_BREAKPOINT_ICON
;
96 protected Icon
getVerifiedWarningsIcon() {
97 return ourVerifiedWarningsIcon
;
100 public Key
<LineBreakpoint
> getCategory() {
104 protected void reload(PsiFile file
) {
106 myMethodName
= findMethodName(file
, getHighlighter().getStartOffset());
109 protected void createOrWaitPrepare(DebugProcessImpl debugProcess
, String classToBeLoaded
) {
110 if (isInScopeOf(debugProcess
)) {
111 super.createOrWaitPrepare(debugProcess
, classToBeLoaded
);
115 protected void createRequestForPreparedClass(final DebugProcessImpl debugProcess
, final ReferenceType classType
) {
116 if (!isInScopeOf(debugProcess
)) {
119 ApplicationManager
.getApplication().runReadAction(new Runnable() {
122 List
<Location
> locs
= debugProcess
.getPositionManager().locationsOfLine(classType
, getSourcePosition());
123 if (locs
.size() > 0) {
124 for (final Location location
: locs
) {
125 if (LOG
.isDebugEnabled()) {
126 LOG
.debug("Found location for reference type " + classType
.name() + " at line " + getLineIndex() + "; isObsolete: " + (debugProcess
.getVirtualMachineProxy().versionHigher("1.4") && location
.method().isObsolete()));
128 BreakpointRequest request
= debugProcess
.getRequestsManager().createBreakpointRequest(LineBreakpoint
.this, location
);
129 debugProcess
.getRequestsManager().enableRequest(request
);
130 if (LOG
.isDebugEnabled()) {
131 LOG
.debug("Created breakpoint request for reference type " + classType
.name() + " at line " + getLineIndex());
136 // there's no executable code in this class
137 debugProcess
.getRequestsManager().setInvalid(LineBreakpoint
.this, DebuggerBundle
.message(
138 "error.invalid.breakpoint.no.executable.code", (getLineIndex() + 1), classType
.name())
140 if (LOG
.isDebugEnabled()) {
141 LOG
.debug("No locations of type " + classType
.name() + " found at line " + getLineIndex());
145 catch (ClassNotPreparedException ex
) {
146 if (LOG
.isDebugEnabled()) {
147 LOG
.debug("ClassNotPreparedException: " + ex
.getMessage());
149 // there's a chance to add a breakpoint when the class is prepared
151 catch (ObjectCollectedException ex
) {
152 if (LOG
.isDebugEnabled()) {
153 LOG
.debug("ObjectCollectedException: " + ex
.getMessage());
155 // there's a chance to add a breakpoint when the class is prepared
157 catch (InvalidLineNumberException ex
) {
158 if (LOG
.isDebugEnabled()) {
159 LOG
.debug("InvalidLineNumberException: " + ex
.getMessage());
161 debugProcess
.getRequestsManager().setInvalid(LineBreakpoint
.this, DebuggerBundle
.message("error.invalid.breakpoint.bad.line.number"));
163 catch (InternalException ex
) {
166 catch(Exception ex
) {
174 private boolean isInScopeOf(DebugProcessImpl debugProcess
) {
175 final SourcePosition position
= getSourcePosition();
176 if (position
!= null) {
177 final GlobalSearchScope scope
= debugProcess
.getSearchScope();
178 final VirtualFile file
= position
.getFile().getVirtualFile();
179 if (file
!= null && ProjectRootManager
.getInstance(debugProcess
.getProject()).getFileIndex().isInSourceContent(file
)) {
180 return scope
.accept(file
);
186 public boolean evaluateCondition(EvaluationContextImpl context
, LocatableEvent event
) throws EvaluateException
{
187 if(CLASS_FILTERS_ENABLED
){
188 Value value
= context
.getThisObject();
189 ObjectReference thisObject
= (ObjectReference
)value
;
190 if(thisObject
== null) {
193 String name
= DebuggerUtilsEx
.getQualifiedClassName(thisObject
.referenceType().name(), getProject());
197 ClassFilter
[] filters
= getClassFilters();
198 boolean matches
= false;
199 for (ClassFilter classFilter
: filters
) {
200 if (classFilter
.isEnabled() && classFilter
.matches(name
)) {
209 ClassFilter
[] ifilters
= getClassExclusionFilters();
210 for (ClassFilter classFilter
: ifilters
) {
211 if (classFilter
.isEnabled() && classFilter
.matches(name
)) {
216 return super.evaluateCondition(context
, event
);
219 public String
toString() {
220 return getDescription();
224 public String
getDisplayName() {
225 final int lineNumber
= (getHighlighter().getDocument().getLineNumber(getHighlighter().getStartOffset()) + 1);
227 final String className
= getClassName();
228 final boolean hasClassInfo
= className
!= null && className
.length() > 0;
229 final boolean hasMethodInfo
= myMethodName
!= null && myMethodName
.length() > 0;
230 if (hasClassInfo
|| hasMethodInfo
) {
231 final StringBuilder info
= StringBuilderSpinAllocator
.alloc();
233 String packageName
= null;
235 final int dotIndex
= className
.lastIndexOf(".");
237 info
.append(className
.substring(dotIndex
+ 1));
238 packageName
= className
.substring(0, dotIndex
);
241 info
.append(className
);
248 info
.append(myMethodName
);
250 if (packageName
!= null) {
251 info
.append(" (").append(packageName
).append(")");
253 return DebuggerBundle
.message("line.breakpoint.display.name.with.class.or.method", lineNumber
, info
.toString());
256 StringBuilderSpinAllocator
.dispose(info
);
259 return DebuggerBundle
.message("line.breakpoint.display.name", lineNumber
);
261 return DebuggerBundle
.message("status.breakpoint.invalid");
264 private static @Nullable String
findMethodName(final PsiFile file
, final int offset
) {
265 if (file
instanceof JspFile
) {
268 if (file
instanceof PsiJavaFile
) {
269 return ApplicationManager
.getApplication().runReadAction(new Computable
<String
>() {
270 public String
compute() {
271 final PsiMethod method
= DebuggerUtilsEx
.findPsiMethod(file
, offset
);
272 return method
!= null? method
.getName() + "()" : null;
279 public String
getEventMessage(LocatableEvent event
) {
280 final Location location
= event
.location();
282 return DebuggerBundle
.message(
283 "status.line.breakpoint.reached",
284 location
.declaringType().name() + "." + location
.method().name(),
285 location
.sourceName(),
289 catch (AbsentInformationException e
) {
290 final String sourceName
= getSourcePosition().getFile().getName();
291 return DebuggerBundle
.message(
292 "status.line.breakpoint.reached",
293 location
.declaringType().name() + "." + location
.method().name(),
300 public PsiElement
getEvaluationElement() {
301 return PositionUtil
.getContextElement(getSourcePosition());
304 protected static LineBreakpoint
create(Project project
, Document document
, int lineIndex
) {
305 VirtualFile virtualFile
= FileDocumentManager
.getInstance().getFile(document
);
306 if (virtualFile
== null) return null;
308 LineBreakpoint breakpoint
= new LineBreakpoint(project
, createHighlighter(project
, document
, lineIndex
));
309 return (LineBreakpoint
)breakpoint
.init();
312 public boolean canMoveTo(SourcePosition position
) {
313 if (!super.canMoveTo(position
)) {
316 final Document document
= PsiDocumentManager
.getInstance(getProject()).getDocument(position
.getFile());
317 return canAddLineBreakpoint(myProject
, document
, position
.getLine());
320 public static boolean canAddLineBreakpoint(Project project
, final Document document
, final int lineIndex
) {
321 if (lineIndex
< 0 || lineIndex
>= document
.getLineCount()) {
324 final BreakpointManager breakpointManager
= DebuggerManagerEx
.getInstanceEx(project
).getBreakpointManager();
325 final LineBreakpoint breakpointAtLine
= breakpointManager
.findBreakpoint( document
, document
.getLineStartOffset(lineIndex
), CATEGORY
);
326 if (breakpointAtLine
!= null) {
327 // there already exists a line breakpoint at this line
330 PsiDocumentManager
.getInstance(project
).commitDocument(document
);
332 final boolean[] canAdd
= new boolean[]{false};
333 XDebuggerUtil
.getInstance().iterateLine(project
, document
, lineIndex
, new Processor
<PsiElement
>() {
334 public boolean process(PsiElement element
) {
335 if ((element
instanceof PsiWhiteSpace
) || (PsiTreeUtil
.getParentOfType(element
, PsiComment
.class, false) != null)) {
338 PsiElement child
= element
;
339 while(element
!= null) {
341 final int offset
= element
.getTextOffset();
343 if (document
.getLineNumber(offset
) != lineIndex
) {
348 element
= element
.getParent();
351 if(child
instanceof PsiMethod
&& child
.getTextRange().getEndOffset() >= document
.getLineEndOffset(lineIndex
)) {
352 PsiCodeBlock body
= ((PsiMethod
)child
).getBody();
357 PsiStatement
[] statements
= body
.getStatements();
358 canAdd
[0] = statements
.length
> 0 && document
.getLineNumber(statements
[0].getTextOffset()) == lineIndex
;
371 public @Nullable String
getMethodName() {