weaker condition for testing if the file belongs to run-config scope
[fedora-idea.git] / java / debugger / impl / src / com / intellij / debugger / ui / breakpoints / LineBreakpoint.java
blob0ef85c6333b039d52791190b5267e975d2920c92
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.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;
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.Nullable;
57 import javax.swing.*;
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");
63 // icons
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) {
72 super(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() {
85 return ICON;
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() {
101 return CATEGORY;
104 protected void reload(PsiFile file) {
105 super.reload(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)) {
117 return;
119 ApplicationManager.getApplication().runReadAction(new Runnable() {
120 public void run() {
121 try {
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());
135 else {
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) {
164 LOG.info(ex);
166 catch(Exception ex) {
167 LOG.info(ex);
169 updateUI();
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);
183 return true;
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) {
191 return false;
193 String name = DebuggerUtilsEx.getQualifiedClassName(thisObject.referenceType().name(), getProject());
194 if(name == null) {
195 return false;
197 ClassFilter [] filters = getClassFilters();
198 boolean matches = false;
199 for (ClassFilter classFilter : filters) {
200 if (classFilter.isEnabled() && classFilter.matches(name)) {
201 matches = true;
202 break;
205 if(!matches) {
206 return false;
209 ClassFilter [] ifilters = getClassExclusionFilters();
210 for (ClassFilter classFilter : ifilters) {
211 if (classFilter.isEnabled() && classFilter.matches(name)) {
212 return false;
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);
226 if(isValid()) {
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();
232 try {
233 String packageName = null;
234 if (hasClassInfo) {
235 final int dotIndex = className.lastIndexOf(".");
236 if (dotIndex >= 0) {
237 info.append(className.substring(dotIndex + 1));
238 packageName = className.substring(0, dotIndex);
240 else {
241 info.append(className);
244 if(hasMethodInfo) {
245 if (hasClassInfo) {
246 info.append(".");
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());
255 finally {
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) {
266 return null;
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;
276 return null;
279 public String getEventMessage(LocatableEvent event) {
280 final Location location = event.location();
281 try {
282 return DebuggerBundle.message(
283 "status.line.breakpoint.reached",
284 location.declaringType().name() + "." + location.method().name(),
285 location.sourceName(),
286 getLineIndex() + 1
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(),
294 sourceName,
295 getLineIndex() + 1
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)) {
314 return false;
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()) {
322 return false;
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
328 return false;
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)) {
336 return true;
338 PsiElement child = element;
339 while(element != null) {
341 final int offset = element.getTextOffset();
342 if (offset >= 0) {
343 if (document.getLineNumber(offset) != lineIndex) {
344 break;
347 child = element;
348 element = element.getParent();
351 if(child instanceof PsiMethod && child.getTextRange().getEndOffset() >= document.getLineEndOffset(lineIndex)) {
352 PsiCodeBlock body = ((PsiMethod)child).getBody();
353 if(body == null) {
354 canAdd[0] = false;
356 else {
357 PsiStatement[] statements = body.getStatements();
358 canAdd[0] = statements.length > 0 && document.getLineNumber(statements[0].getTextOffset()) == lineIndex;
361 else {
362 canAdd[0] = true;
364 return false;
368 return canAdd[0];
371 public @Nullable String getMethodName() {
372 return myMethodName;