592b9f262973778ecc2fac233ff828c6729f6748
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / ui / breakpoints / BreakpointManager.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * Class BreakpointManager
19  * @author Jeka
20  */
21 package com.intellij.debugger.ui.breakpoints;
22
23 import com.intellij.debugger.DebuggerBundle;
24 import com.intellij.debugger.DebuggerInvocationUtil;
25 import com.intellij.debugger.engine.BreakpointStepMethodFilter;
26 import com.intellij.debugger.engine.DebugProcessImpl;
27 import com.intellij.debugger.engine.requests.RequestManagerImpl;
28 import com.intellij.debugger.impl.DebuggerContextImpl;
29 import com.intellij.debugger.impl.DebuggerContextListener;
30 import com.intellij.debugger.impl.DebuggerManagerImpl;
31 import com.intellij.debugger.impl.DebuggerSession;
32 import com.intellij.debugger.ui.JavaDebuggerSupport;
33 import com.intellij.openapi.application.ApplicationManager;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.editor.Document;
36 import com.intellij.openapi.editor.Editor;
37 import com.intellij.openapi.editor.markup.GutterIconRenderer;
38 import com.intellij.openapi.editor.markup.RangeHighlighter;
39 import com.intellij.openapi.fileEditor.FileDocumentManager;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.startup.StartupManager;
42 import com.intellij.openapi.ui.MessageType;
43 import com.intellij.openapi.util.Comparing;
44 import com.intellij.openapi.util.Computable;
45 import com.intellij.openapi.util.InvalidDataException;
46 import com.intellij.openapi.util.Key;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.openapi.vfs.VirtualFileManager;
49 import com.intellij.psi.PsiField;
50 import com.intellij.util.containers.ContainerUtil;
51 import com.intellij.xdebugger.XDebuggerManager;
52 import com.intellij.xdebugger.XDebuggerUtil;
53 import com.intellij.xdebugger.XSourcePosition;
54 import com.intellij.xdebugger.breakpoints.*;
55 import com.intellij.xdebugger.impl.DebuggerSupport;
56 import com.intellij.xdebugger.impl.XDebugSessionImpl;
57 import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase;
58 import com.intellij.xdebugger.impl.breakpoints.XBreakpointManagerImpl;
59 import com.intellij.xdebugger.impl.breakpoints.XDependentBreakpointManager;
60 import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl;
61 import com.sun.jdi.InternalException;
62 import com.sun.jdi.ThreadReference;
63 import com.sun.jdi.request.*;
64 import gnu.trove.THashMap;
65 import org.jdom.Element;
66 import org.jetbrains.annotations.NonNls;
67 import org.jetbrains.annotations.NotNull;
68 import org.jetbrains.annotations.Nullable;
69 import org.jetbrains.java.debugger.breakpoints.properties.JavaExceptionBreakpointProperties;
70
71 import javax.swing.*;
72 import java.util.LinkedHashMap;
73 import java.util.List;
74 import java.util.Map;
75
76 public class BreakpointManager {
77   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.BreakpointManager");
78
79   @NonNls private static final String MASTER_BREAKPOINT_TAG_NAME = "master_breakpoint";
80   @NonNls private static final String SLAVE_BREAKPOINT_TAG_NAME = "slave_breakpoint";
81   @NonNls private static final String DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME = "default_suspend_policy";
82   @NonNls private static final String DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME = "default_condition_enabled";
83
84   @NonNls private static final String RULES_GROUP_NAME = "breakpoint_rules";
85   private static final String CONVERTED_PARAM = "converted";
86
87   private final Project myProject;
88   private final Map<String, String> myUIProperties = new LinkedHashMap<>();
89
90   private final StartupManager myStartupManager;
91
92   public BreakpointManager(@NotNull Project project, @NotNull StartupManager startupManager, @NotNull DebuggerManagerImpl debuggerManager) {
93     myProject = project;
94     myStartupManager = startupManager;
95     debuggerManager.getContextManager().addListener(new DebuggerContextListener() {
96       private DebuggerSession myPreviousSession;
97
98       @Override
99       public void changeEvent(@NotNull DebuggerContextImpl newContext, DebuggerSession.Event event) {
100         if (event == DebuggerSession.Event.ATTACHED) {
101           for (XBreakpoint breakpoint : getXBreakpointManager().getAllBreakpoints()) {
102             if (checkAndNotifyPossiblySlowBreakpoint(breakpoint)) break;
103           }
104         }
105         if (newContext.getDebuggerSession() != myPreviousSession || event == DebuggerSession.Event.DETACHED) {
106           updateBreakpointsUI();
107           myPreviousSession = newContext.getDebuggerSession();
108         }
109       }
110     });
111   }
112
113   private static boolean checkAndNotifyPossiblySlowBreakpoint(XBreakpoint breakpoint) {
114     if (breakpoint.isEnabled() &&
115         (breakpoint.getType() instanceof JavaMethodBreakpointType || breakpoint.getType() instanceof JavaWildcardMethodBreakpointType)) {
116       Breakpoint bpt = getJavaBreakpoint(breakpoint);
117       if (bpt instanceof MethodBreakpoint && ((MethodBreakpoint)bpt).isEmulated()) {
118         return false;
119       }
120       XDebugSessionImpl.NOTIFICATION_GROUP
121         .createNotification(DebuggerBundle.message("method.breakpoints.slowness.warning"), MessageType.WARNING)
122         .notify(((XBreakpointBase)breakpoint).getProject());
123       return true;
124     }
125     return false;
126   }
127
128   public void init() {
129     XBreakpointManager manager = XDebuggerManager.getInstance(myProject).getBreakpointManager();
130     manager.addBreakpointListener(new XBreakpointAdapter<XBreakpoint<?>>() {
131       @Override
132       public void breakpointAdded(@NotNull XBreakpoint<?> xBreakpoint) {
133         Breakpoint breakpoint = getJavaBreakpoint(xBreakpoint);
134         if (breakpoint != null) {
135           addBreakpoint(breakpoint);
136         }
137       }
138
139       @Override
140       public void breakpointChanged(@NotNull XBreakpoint xBreakpoint) {
141         Breakpoint breakpoint = getJavaBreakpoint(xBreakpoint);
142         if (breakpoint != null) {
143           fireBreakpointChanged(breakpoint);
144         }
145       }
146     });
147   }
148
149   private XBreakpointManager getXBreakpointManager() {
150     return XDebuggerManager.getInstance(myProject).getBreakpointManager();
151   }
152
153   public void editBreakpoint(final Breakpoint breakpoint, final Editor editor) {
154     DebuggerInvocationUtil.swingInvokeLater(myProject, () -> {
155       XBreakpoint xBreakpoint = breakpoint.myXBreakpoint;
156       if (xBreakpoint instanceof XLineBreakpointImpl) {
157         RangeHighlighter highlighter = ((XLineBreakpointImpl)xBreakpoint).getHighlighter();
158         if (highlighter != null) {
159           GutterIconRenderer renderer = highlighter.getGutterIconRenderer();
160           if (renderer != null) {
161             DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class).getEditBreakpointAction().editBreakpoint(
162               myProject, editor, breakpoint.myXBreakpoint, renderer
163             );
164           }
165         }
166       }
167     });
168   }
169
170   public void setBreakpointDefaults(Key<? extends Breakpoint> category, BreakpointDefaults defaults) {
171     Class typeCls = null;
172     if (LineBreakpoint.CATEGORY.toString().equals(category.toString())) {
173       typeCls = JavaLineBreakpointType.class;
174     }
175     else if (MethodBreakpoint.CATEGORY.toString().equals(category.toString())) {
176       typeCls = JavaMethodBreakpointType.class;
177     }
178     else if (FieldBreakpoint.CATEGORY.toString().equals(category.toString())) {
179       typeCls = JavaFieldBreakpointType.class;
180     }
181     else if (ExceptionBreakpoint.CATEGORY.toString().equals(category.toString())) {
182       typeCls = JavaExceptionBreakpointType.class;
183     }
184     if (typeCls != null) {
185       XBreakpointType type = XDebuggerUtil.getInstance().findBreakpointType(typeCls);
186       ((XBreakpointManagerImpl)getXBreakpointManager()).getBreakpointDefaults(type).setSuspendPolicy(Breakpoint.transformSuspendPolicy(defaults.getSuspendPolicy()));
187     }
188   }
189
190   @Nullable
191   public RunToCursorBreakpoint addRunToCursorBreakpoint(@NotNull XSourcePosition position, final boolean ignoreBreakpoints) {
192     return RunToCursorBreakpoint.create(myProject, position, ignoreBreakpoints);
193   }
194
195   @Nullable
196   public StepIntoBreakpoint addStepIntoBreakpoint(@NotNull BreakpointStepMethodFilter filter) {
197     return StepIntoBreakpoint.create(myProject, filter);
198   }
199
200   @Nullable
201   public LineBreakpoint addLineBreakpoint(Document document, int lineIndex) {
202     ApplicationManager.getApplication().assertIsDispatchThread();
203     if (!LineBreakpoint.canAddLineBreakpoint(myProject, document, lineIndex)) {
204       return null;
205     }
206     XLineBreakpoint xLineBreakpoint = addXLineBreakpoint(JavaLineBreakpointType.class, document, lineIndex);
207     Breakpoint breakpoint = getJavaBreakpoint(xLineBreakpoint);
208     if (breakpoint instanceof LineBreakpoint) {
209       addBreakpoint(breakpoint);
210       return ((LineBreakpoint)breakpoint);
211     }
212     return null;
213   }
214
215   @Nullable
216   public FieldBreakpoint addFieldBreakpoint(@NotNull Document document, int offset) {
217     PsiField field = FieldBreakpoint.findField(myProject, document, offset);
218     if (field == null) {
219       return null;
220     }
221
222     int line = document.getLineNumber(offset);
223
224     if (document.getLineNumber(field.getNameIdentifier().getTextOffset()) < line) {
225       return null;
226     }
227
228     return addFieldBreakpoint(document, line, field.getName());
229   }
230
231   @Nullable
232   public FieldBreakpoint addFieldBreakpoint(Document document, int lineIndex, String fieldName) {
233     ApplicationManager.getApplication().assertIsDispatchThread();
234     XLineBreakpoint xBreakpoint = addXLineBreakpoint(JavaFieldBreakpointType.class, document, lineIndex);
235     Breakpoint javaBreakpoint = getJavaBreakpoint(xBreakpoint);
236     if (javaBreakpoint instanceof FieldBreakpoint) {
237       FieldBreakpoint fieldBreakpoint = (FieldBreakpoint)javaBreakpoint;
238       fieldBreakpoint.setFieldName(fieldName);
239       addBreakpoint(javaBreakpoint);
240       return fieldBreakpoint;
241     }
242     return null;
243   }
244
245   @NotNull
246   public ExceptionBreakpoint addExceptionBreakpoint(@NotNull final String exceptionClassName, final String packageName) {
247     ApplicationManager.getApplication().assertIsDispatchThread();
248     final JavaExceptionBreakpointType type = XDebuggerUtil.getInstance().findBreakpointType(JavaExceptionBreakpointType.class);
249     return ApplicationManager.getApplication().runWriteAction((Computable<ExceptionBreakpoint>)() -> {
250       XBreakpoint<JavaExceptionBreakpointProperties> xBreakpoint = XDebuggerManager.getInstance(myProject).getBreakpointManager()
251         .addBreakpoint(type, new JavaExceptionBreakpointProperties(exceptionClassName, packageName));
252       Breakpoint javaBreakpoint = getJavaBreakpoint(xBreakpoint);
253       if (javaBreakpoint instanceof ExceptionBreakpoint) {
254         ExceptionBreakpoint exceptionBreakpoint = (ExceptionBreakpoint)javaBreakpoint;
255         exceptionBreakpoint.setQualifiedName(exceptionClassName);
256         exceptionBreakpoint.setPackageName(packageName);
257         addBreakpoint(exceptionBreakpoint);
258         LOG.debug("ExceptionBreakpoint Added");
259         return exceptionBreakpoint;
260       }
261       return null;
262     });
263   }
264
265   @Nullable
266   public MethodBreakpoint addMethodBreakpoint(Document document, int lineIndex) {
267     ApplicationManager.getApplication().assertIsDispatchThread();
268
269     XLineBreakpoint xBreakpoint = addXLineBreakpoint(JavaMethodBreakpointType.class, document, lineIndex);
270     Breakpoint javaBreakpoint = getJavaBreakpoint(xBreakpoint);
271     if (javaBreakpoint instanceof MethodBreakpoint) {
272       addBreakpoint(javaBreakpoint);
273       return (MethodBreakpoint)javaBreakpoint;
274     }
275     return null;
276   }
277
278   private <B extends XBreakpoint<?>> XLineBreakpoint addXLineBreakpoint(Class<? extends XBreakpointType<B,?>> typeCls, Document document, final int lineIndex) {
279     final XBreakpointType<B, ?> type = XDebuggerUtil.getInstance().findBreakpointType(typeCls);
280     final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
281     return ApplicationManager.getApplication().runWriteAction((Computable<XLineBreakpoint>)() -> XDebuggerManager.getInstance(myProject).getBreakpointManager()
282       .addLineBreakpoint((XLineBreakpointType)type, file.getUrl(), lineIndex,
283                          ((XLineBreakpointType)type).createBreakpointProperties(file, lineIndex)));
284   }
285
286   /**
287    * @param category breakpoint category, null if the category does not matter
288    */
289   @Nullable
290   public <T extends BreakpointWithHighlighter> T findBreakpoint(final Document document, final int offset, @Nullable final Key<T> category) {
291     for (final Breakpoint breakpoint : getBreakpoints()) {
292       if (breakpoint instanceof BreakpointWithHighlighter && ((BreakpointWithHighlighter)breakpoint).isAt(document, offset)) {
293         if (category == null || category.equals(breakpoint.getCategory())) {
294           //noinspection CastConflictsWithInstanceof,unchecked
295           return (T)breakpoint;
296         }
297       }
298     }
299     return null;
300   }
301
302   private final Map<String, Element> myOriginalBreakpointsNodes = new LinkedHashMap<>();
303
304   public void readExternal(@NotNull final Element parentNode) {
305     myOriginalBreakpointsNodes.clear();
306     // save old breakpoints
307     for (Element element : parentNode.getChildren()) {
308       myOriginalBreakpointsNodes.put(element.getName(), element.clone());
309     }
310     if (myProject.isOpen()) {
311       doRead(parentNode);
312     }
313     else {
314       myStartupManager.registerPostStartupActivity(() -> doRead(parentNode));
315     }
316   }
317
318   private void doRead(@NotNull final Element parentNode) {
319     ApplicationManager.getApplication().runReadAction(() -> {
320       final Map<String, Breakpoint> nameToBreakpointMap = new THashMap<>();
321       try {
322         final List groups = parentNode.getChildren();
323         for (final Object group1 : groups) {
324           final Element group = (Element)group1;
325           if (group.getName().equals(RULES_GROUP_NAME)) {
326             continue;
327           }
328           // skip already converted
329           if (group.getAttribute(CONVERTED_PARAM) != null) {
330             continue;
331           }
332           final String categoryName = group.getName();
333           final Key<Breakpoint> breakpointCategory = BreakpointCategory.lookup(categoryName);
334           final String defaultPolicy = group.getAttributeValue(DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME);
335           final boolean conditionEnabled = Boolean.parseBoolean(group.getAttributeValue(DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME, "true"));
336           setBreakpointDefaults(breakpointCategory, new BreakpointDefaults(defaultPolicy, conditionEnabled));
337           Element anyExceptionBreakpointGroup;
338           if (!AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT.equals(breakpointCategory)) {
339             // for compatibility with previous format
340             anyExceptionBreakpointGroup = group.getChild(AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT.toString());
341             //final BreakpointFactory factory = BreakpointFactory.getInstance(breakpointCategory);
342             //if (factory != null) {
343               for (Element breakpointNode : group.getChildren("breakpoint")) {
344                 //Breakpoint breakpoint = factory.createBreakpoint(myProject, breakpointNode);
345                 Breakpoint breakpoint = createBreakpoint(categoryName, breakpointNode);
346                 breakpoint.readExternal(breakpointNode);
347                 nameToBreakpointMap.put(breakpoint.getDisplayName(), breakpoint);
348               }
349             //}
350           }
351           else {
352             anyExceptionBreakpointGroup = group;
353           }
354
355           if (anyExceptionBreakpointGroup != null) {
356             final Element breakpointElement = group.getChild("breakpoint");
357             if (breakpointElement != null) {
358               XBreakpointManager manager = XDebuggerManager.getInstance(myProject).getBreakpointManager();
359               JavaExceptionBreakpointType type = XDebuggerUtil.getInstance().findBreakpointType(JavaExceptionBreakpointType.class);
360               XBreakpoint<JavaExceptionBreakpointProperties> xBreakpoint = manager.getDefaultBreakpoint(type);
361               Breakpoint breakpoint = getJavaBreakpoint(xBreakpoint);
362               if (breakpoint != null) {
363                 breakpoint.readExternal(breakpointElement);
364                 addBreakpoint(breakpoint);
365               }
366             }
367           }
368         }
369       }
370       catch (InvalidDataException ignored) {
371       }
372
373       final Element rulesGroup = parentNode.getChild(RULES_GROUP_NAME);
374       if (rulesGroup != null) {
375         final List<Element> rules = rulesGroup.getChildren("rule");
376         for (Element rule : rules) {
377           // skip already converted
378           if (rule.getAttribute(CONVERTED_PARAM) != null) {
379             continue;
380           }
381           final Element master = rule.getChild(MASTER_BREAKPOINT_TAG_NAME);
382           if (master == null) {
383             continue;
384           }
385           final Element slave = rule.getChild(SLAVE_BREAKPOINT_TAG_NAME);
386           if (slave == null) {
387             continue;
388           }
389           final Breakpoint masterBreakpoint = nameToBreakpointMap.get(master.getAttributeValue("name"));
390           if (masterBreakpoint == null) {
391             continue;
392           }
393           final Breakpoint slaveBreakpoint = nameToBreakpointMap.get(slave.getAttributeValue("name"));
394           if (slaveBreakpoint == null) {
395             continue;
396           }
397
398           boolean leaveEnabled = "true".equalsIgnoreCase(rule.getAttributeValue("leaveEnabled"));
399           XDependentBreakpointManager dependentBreakpointManager = ((XBreakpointManagerImpl)getXBreakpointManager()).getDependentBreakpointManager();
400           dependentBreakpointManager.setMasterBreakpoint(slaveBreakpoint.myXBreakpoint, masterBreakpoint.myXBreakpoint, leaveEnabled);
401           //addBreakpointRule(new EnableBreakpointRule(BreakpointManager.this, masterBreakpoint, slaveBreakpoint, leaveEnabled));
402         }
403       }
404
405       DebuggerInvocationUtil.invokeLater(myProject, this::updateBreakpointsUI);
406     });
407
408     myUIProperties.clear();
409     final Element props = parentNode.getChild("ui_properties");
410     if (props != null) {
411       final List children = props.getChildren("property");
412       for (Object child : children) {
413         Element property = (Element)child;
414         final String name = property.getAttributeValue("name");
415         final String value = property.getAttributeValue("value");
416         if (name != null && value != null) {
417           myUIProperties.put(name, value);
418         }
419       }
420     }
421   }
422
423   private Breakpoint createBreakpoint(String category, Element breakpointNode) throws InvalidDataException {
424     XBreakpoint xBreakpoint = null;
425     if (category.equals(LineBreakpoint.CATEGORY.toString())) {
426       xBreakpoint = createXLineBreakpoint(JavaLineBreakpointType.class, breakpointNode);
427     }
428     else if (category.equals(MethodBreakpoint.CATEGORY.toString())) {
429       if (breakpointNode.getAttribute("url") != null) {
430         xBreakpoint = createXLineBreakpoint(JavaMethodBreakpointType.class, breakpointNode);
431       }
432       else {
433         xBreakpoint = createXBreakpoint(JavaWildcardMethodBreakpointType.class);
434       }
435     }
436     else if (category.equals(FieldBreakpoint.CATEGORY.toString())) {
437       xBreakpoint = createXLineBreakpoint(JavaFieldBreakpointType.class, breakpointNode);
438     }
439     else if (category.equals(ExceptionBreakpoint.CATEGORY.toString())) {
440       xBreakpoint = createXBreakpoint(JavaExceptionBreakpointType.class);
441     }
442     if (xBreakpoint == null) {
443       throw new IllegalStateException("Unknown breakpoint category " + category);
444     }
445     return getJavaBreakpoint(xBreakpoint);
446   }
447
448   private <B extends XBreakpoint<?>> XBreakpoint createXBreakpoint(Class<? extends XBreakpointType<B, ?>> typeCls) {
449     final XBreakpointType<B, ?> type = XDebuggerUtil.getInstance().findBreakpointType(typeCls);
450     return ApplicationManager.getApplication().runWriteAction((Computable<XBreakpoint>)() -> XDebuggerManager.getInstance(myProject).getBreakpointManager().addBreakpoint((XBreakpointType)type, type.createProperties()));
451   }
452
453   private <B extends XBreakpoint<?>> XLineBreakpoint createXLineBreakpoint(Class<? extends XBreakpointType<B, ?>> typeCls,
454                                                                            Element breakpointNode) throws InvalidDataException {
455     final String url = breakpointNode.getAttributeValue("url");
456     VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(url);
457     if (vFile == null) {
458       throw new InvalidDataException(DebuggerBundle.message("error.breakpoint.file.not.found", url));
459     }
460     final Document doc = FileDocumentManager.getInstance().getDocument(vFile);
461     if (doc == null) {
462       throw new InvalidDataException(DebuggerBundle.message("error.cannot.load.breakpoint.file", url));
463     }
464
465     final int line;
466     try {
467       //noinspection HardCodedStringLiteral
468       line = Integer.parseInt(breakpointNode.getAttributeValue("line"));
469     }
470     catch (Exception e) {
471       throw new InvalidDataException("Line number is invalid for breakpoint");
472     }
473     return addXLineBreakpoint(typeCls, doc, line);
474   }
475
476   public static void addBreakpoint(@NotNull Breakpoint breakpoint) {
477     assert breakpoint.myXBreakpoint.getUserData(Breakpoint.DATA_KEY) == breakpoint;
478     breakpoint.updateUI();
479     checkAndNotifyPossiblySlowBreakpoint(breakpoint.myXBreakpoint);
480   }
481
482   public void removeBreakpoint(@Nullable final Breakpoint breakpoint) {
483     if (breakpoint == null) {
484       return;
485     }
486     ApplicationManager.getApplication().runWriteAction(() -> getXBreakpointManager().removeBreakpoint(breakpoint.myXBreakpoint));
487   }
488
489   public void writeExternal(@NotNull final Element parentNode) {
490     // restore old breakpoints
491     for (Element group : myOriginalBreakpointsNodes.values()) {
492       if (group.getAttribute(CONVERTED_PARAM) == null) {
493         group.setAttribute(CONVERTED_PARAM, "true");
494       }
495       parentNode.addContent(group.clone());
496     }
497   }
498
499   @NotNull
500   public List<Breakpoint> getBreakpoints() {
501     return ApplicationManager.getApplication().runReadAction((Computable<List<Breakpoint>>)() ->
502       ContainerUtil.mapNotNull(getXBreakpointManager().getAllBreakpoints(), BreakpointManager::getJavaBreakpoint));
503   }
504
505   @Nullable
506   public static Breakpoint getJavaBreakpoint(@Nullable final XBreakpoint xBreakpoint) {
507     if (xBreakpoint == null) {
508       return null;
509     }
510     Breakpoint breakpoint = xBreakpoint.getUserData(Breakpoint.DATA_KEY);
511     if (breakpoint == null && xBreakpoint.getType() instanceof JavaBreakpointType) {
512       Project project = ((XBreakpointBase)xBreakpoint).getProject();
513       breakpoint = ((JavaBreakpointType)xBreakpoint.getType()).createJavaBreakpoint(project, xBreakpoint);
514       xBreakpoint.putUserData(Breakpoint.DATA_KEY, breakpoint);
515     }
516     return breakpoint;
517   }
518
519   //interaction with RequestManagerImpl
520   public void disableBreakpoints(@NotNull final DebugProcessImpl debugProcess) {
521     final List<Breakpoint> breakpoints = getBreakpoints();
522     if (!breakpoints.isEmpty()) {
523       final RequestManagerImpl requestManager = debugProcess.getRequestsManager();
524       for (Breakpoint breakpoint : breakpoints) {
525         breakpoint.markVerified(requestManager.isVerified(breakpoint));
526         requestManager.deleteRequest(breakpoint);
527       }
528       SwingUtilities.invokeLater(this::updateBreakpointsUI);
529     }
530   }
531
532   public void enableBreakpoints(final DebugProcessImpl debugProcess) {
533     final List<Breakpoint> breakpoints = getBreakpoints();
534     if (!breakpoints.isEmpty()) {
535       for (Breakpoint breakpoint : breakpoints) {
536         breakpoint.markVerified(false); // clean cached state
537         breakpoint.createRequest(debugProcess);
538       }
539       SwingUtilities.invokeLater(this::updateBreakpointsUI);
540     }
541   }
542
543   public void applyThreadFilter(@NotNull final DebugProcessImpl debugProcess, @Nullable ThreadReference newFilterThread) {
544     final RequestManagerImpl requestManager = debugProcess.getRequestsManager();
545     final ThreadReference oldFilterThread = requestManager.getFilterThread();
546     if (Comparing.equal(newFilterThread, oldFilterThread)) {
547       // the filter already added
548       return;
549     }
550     requestManager.setFilterThread(newFilterThread);
551     if (newFilterThread == null || oldFilterThread != null) {
552       final List<Breakpoint> breakpoints = getBreakpoints();
553       for (Breakpoint breakpoint : breakpoints) {
554         if (LineBreakpoint.CATEGORY.equals(breakpoint.getCategory()) || MethodBreakpoint.CATEGORY.equals(breakpoint.getCategory())) {
555           requestManager.deleteRequest(breakpoint);
556           breakpoint.createRequest(debugProcess);
557         }
558       }
559     }
560     else {
561       // important! need to add filter to _existing_ requests, otherwise Requestor->Request mapping will be lost
562       // and debugger trees will not be restored to original state
563       abstract class FilterSetter <T extends EventRequest> {
564          void applyFilter(@NotNull final List<T> requests, final ThreadReference thread) {
565           for (T request : requests) {
566             try {
567               final boolean wasEnabled = request.isEnabled();
568               if (wasEnabled) {
569                 request.disable();
570               }
571               addFilter(request, thread);
572               if (wasEnabled) {
573                 request.enable();
574               }
575             }
576             catch (InternalException e) {
577               LOG.info(e);
578             }
579             catch (InvalidRequestStateException e) {
580               LOG.info(e);
581             }
582           }
583         }
584         protected abstract void addFilter(final T request, final ThreadReference thread);
585       }
586
587       final EventRequestManager eventRequestManager = requestManager.getVMRequestManager();
588       if (eventRequestManager != null) {
589         new FilterSetter<BreakpointRequest>() {
590           @Override
591           protected void addFilter(@NotNull final BreakpointRequest request, final ThreadReference thread) {
592             request.addThreadFilter(thread);
593           }
594         }.applyFilter(eventRequestManager.breakpointRequests(), newFilterThread);
595
596         new FilterSetter<MethodEntryRequest>() {
597           @Override
598           protected void addFilter(@NotNull final MethodEntryRequest request, final ThreadReference thread) {
599             request.addThreadFilter(thread);
600           }
601         }.applyFilter(eventRequestManager.methodEntryRequests(), newFilterThread);
602
603         new FilterSetter<MethodExitRequest>() {
604           @Override
605           protected void addFilter(@NotNull final MethodExitRequest request, final ThreadReference thread) {
606             request.addThreadFilter(thread);
607           }
608         }.applyFilter(eventRequestManager.methodExitRequests(), newFilterThread);
609       }
610     }
611   }
612
613   public void updateBreakpointsUI() {
614     ApplicationManager.getApplication().assertIsDispatchThread();
615     getBreakpoints().forEach(Breakpoint::updateUI);
616   }
617
618   public void reloadBreakpoints() {
619     ApplicationManager.getApplication().assertIsDispatchThread();
620     getBreakpoints().forEach(Breakpoint::reload);
621   }
622
623   public void fireBreakpointChanged(Breakpoint breakpoint) {
624     breakpoint.reload();
625     breakpoint.updateUI();
626   }
627
628   public void setBreakpointEnabled(@NotNull final Breakpoint breakpoint, final boolean enabled) {
629     if (breakpoint.isEnabled() != enabled) {
630       breakpoint.setEnabled(enabled);
631     }
632   }
633
634   @Nullable
635   public Breakpoint findMasterBreakpoint(@NotNull Breakpoint dependentBreakpoint) {
636     XDependentBreakpointManager dependentBreakpointManager = ((XBreakpointManagerImpl)getXBreakpointManager()).getDependentBreakpointManager();
637     return getJavaBreakpoint(dependentBreakpointManager.getMasterBreakpoint(dependentBreakpoint.myXBreakpoint));
638   }
639
640   public String getProperty(String name) {
641     return myUIProperties.get(name);
642   }
643   
644   public String setProperty(String name, String value) {
645     return myUIProperties.put(name, value);
646   }
647 }