2 * Copyright 2000-2015 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 DebuggerUtilsEx
21 package com.intellij.debugger.impl;
23 import com.intellij.debugger.DebuggerBundle;
24 import com.intellij.debugger.SourcePosition;
25 import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
26 import com.intellij.debugger.engine.DebuggerUtils;
27 import com.intellij.debugger.engine.SuspendContextImpl;
28 import com.intellij.debugger.engine.evaluation.*;
29 import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
30 import com.intellij.debugger.engine.requests.RequestManagerImpl;
31 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
32 import com.intellij.debugger.requests.Requestor;
33 import com.intellij.debugger.ui.CompletionEditor;
34 import com.intellij.debugger.ui.breakpoints.Breakpoint;
35 import com.intellij.debugger.ui.tree.DebuggerTreeNode;
36 import com.intellij.execution.filters.ExceptionFilters;
37 import com.intellij.execution.filters.TextConsoleBuilder;
38 import com.intellij.execution.filters.TextConsoleBuilderFactory;
39 import com.intellij.execution.ui.ConsoleView;
40 import com.intellij.execution.ui.RunnerLayoutUi;
41 import com.intellij.openapi.Disposable;
42 import com.intellij.openapi.actionSystem.DataContext;
43 import com.intellij.openapi.actionSystem.DefaultActionGroup;
44 import com.intellij.openapi.application.ApplicationManager;
45 import com.intellij.openapi.diagnostic.Logger;
46 import com.intellij.openapi.editor.Editor;
47 import com.intellij.openapi.fileTypes.FileType;
48 import com.intellij.openapi.fileTypes.StdFileTypes;
49 import com.intellij.openapi.project.Project;
50 import com.intellij.openapi.util.*;
51 import com.intellij.openapi.util.text.StringUtil;
52 import com.intellij.openapi.vfs.VirtualFile;
53 import com.intellij.pom.Navigatable;
54 import com.intellij.psi.*;
55 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
56 import com.intellij.ui.classFilter.ClassFilter;
57 import com.intellij.ui.content.Content;
58 import com.intellij.unscramble.ThreadDumpPanel;
59 import com.intellij.unscramble.ThreadState;
60 import com.intellij.util.SmartList;
61 import com.intellij.util.containers.ContainerUtil;
62 import com.intellij.xdebugger.XSourcePosition;
63 import com.intellij.xdebugger.frame.XValueNode;
64 import com.intellij.xdebugger.impl.XSourcePositionImpl;
66 import com.sun.jdi.event.Event;
67 import com.sun.jdi.event.EventSet;
68 import org.jdom.Attribute;
69 import org.jdom.Element;
70 import org.jetbrains.annotations.NonNls;
71 import org.jetbrains.annotations.NotNull;
72 import org.jetbrains.annotations.Nullable;
75 import java.util.regex.PatternSyntaxException;
77 public abstract class DebuggerUtilsEx extends DebuggerUtils {
78 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerUtilsEx");
82 * @return all CodeFragmentFactoryProviders that provide code fragment factories suitable in the context given
84 public static List<CodeFragmentFactory> getCodeFragmentFactories(@Nullable PsiElement context) {
85 final DefaultCodeFragmentFactory defaultFactory = DefaultCodeFragmentFactory.getInstance();
86 final CodeFragmentFactory[] providers = ApplicationManager.getApplication().getExtensions(CodeFragmentFactory.EXTENSION_POINT_NAME);
87 final List<CodeFragmentFactory> suitableFactories = new ArrayList<CodeFragmentFactory>(providers.length);
88 if (providers.length > 0) {
89 for (CodeFragmentFactory factory : providers) {
90 if (factory != defaultFactory && factory.isContextAccepted(context)) {
91 suitableFactories.add(factory);
95 suitableFactories.add(defaultFactory); // let default factory be the last one
96 return suitableFactories;
100 public static PsiMethod findPsiMethod(PsiFile file, int offset) {
101 PsiElement element = null;
104 element = file.findElementAt(offset);
105 if(element != null) {
111 for (; element != null; element = element.getParent()) {
112 if (element instanceof PsiClass || element instanceof PsiLambdaExpression) {
115 if (element instanceof PsiMethod) {
116 return (PsiMethod)element;
123 public static boolean isAssignableFrom(final String baseQualifiedName, ReferenceType checkedType) {
124 if (CommonClassNames.JAVA_LANG_OBJECT.equals(baseQualifiedName)) {
127 return getSuperClass(baseQualifiedName, checkedType) != null;
130 public static ReferenceType getSuperClass(final String baseQualifiedName, ReferenceType checkedType) {
131 if (baseQualifiedName.equals(checkedType.name())) {
135 if (checkedType instanceof ClassType) {
136 ClassType classType = (ClassType)checkedType;
137 ClassType superClassType = classType.superclass();
138 if (superClassType != null) {
139 ReferenceType superClass = getSuperClass(baseQualifiedName, superClassType);
140 if (superClass != null) {
144 List<InterfaceType> interfaces = classType.allInterfaces();
145 for (InterfaceType iface : interfaces) {
146 ReferenceType superClass = getSuperClass(baseQualifiedName, iface);
147 if (superClass != null) {
153 if (checkedType instanceof InterfaceType) {
154 List<InterfaceType> list = ((InterfaceType)checkedType).superinterfaces();
155 for (InterfaceType superInterface : list) {
156 ReferenceType superClass = getSuperClass(baseQualifiedName, superInterface);
157 if (superClass != null) {
165 public static boolean valuesEqual(Value val1, Value val2) {
172 if (val1 instanceof StringReference && val2 instanceof StringReference) {
173 return ((StringReference)val1).value().equals(((StringReference)val2).value());
175 return val1.equals(val2);
178 public static String getValueOrErrorAsString(final EvaluationContext evaluationContext, Value value) {
180 return getValueAsString(evaluationContext, value);
182 catch (EvaluateException e) {
183 return e.getMessage();
187 public static boolean isCharOrInteger(Value value) {
188 return value instanceof CharValue || isInteger(value);
191 private static Set<String> myCharOrIntegers;
193 @SuppressWarnings({"HardCodedStringLiteral"})
194 public static boolean isCharOrIntegerArray(Value value) {
195 if (value == null) return false;
196 if (myCharOrIntegers == null) {
197 myCharOrIntegers = new HashSet<String>();
198 myCharOrIntegers.add("C");
199 myCharOrIntegers.add("B");
200 myCharOrIntegers.add("S");
201 myCharOrIntegers.add("I");
202 myCharOrIntegers.add("J");
205 String signature = value.type().signature();
207 for (i = 0; signature.charAt(i) == '['; i++) ;
208 if (i == 0) return false;
209 signature = signature.substring(i, signature.length());
210 return myCharOrIntegers.contains(signature);
213 public static ClassFilter create(Element element) throws InvalidDataException {
214 ClassFilter filter = new ClassFilter();
215 DefaultJDOMExternalizer.readExternal(filter, element);
219 private static boolean isFiltered(ClassFilter classFilter, String qName) {
220 if (!classFilter.isEnabled()) {
224 if (classFilter.matches(qName)) {
228 catch (PatternSyntaxException e) {
234 public static boolean isFiltered(String qName, ClassFilter[] classFilters) {
235 return isFiltered(qName, Arrays.asList(classFilters));
238 public static boolean isFiltered(String qName, List<ClassFilter> classFilters) {
239 if(qName.indexOf('[') != -1) {
240 return false; //is array
243 for (ClassFilter filter : classFilters) {
244 if (isFiltered(filter, qName)) {
251 public static int getEnabledNumber(ClassFilter[] classFilters) {
253 for (ClassFilter filter : classFilters) {
254 if (filter.isEnabled()) {
261 public static ClassFilter[] readFilters(List<Element> children) throws InvalidDataException {
262 if (ContainerUtil.isEmpty(children)) {
263 return ClassFilter.EMPTY_ARRAY;
266 ClassFilter[] filters = new ClassFilter[children.size()];
267 for (int i = 0, size = children.size(); i < size; i++) {
268 filters[i] = create(children.get(i));
273 public static void writeFilters(Element parentNode, @NonNls String tagName, ClassFilter[] filters) throws WriteExternalException {
274 for (ClassFilter filter : filters) {
275 Element element = new Element(tagName);
276 parentNode.addContent(element);
277 DefaultJDOMExternalizer.writeExternal(filter, element);
281 public static boolean filterEquals(ClassFilter[] filters1, ClassFilter[] filters2) {
282 if (filters1.length != filters2.length) {
285 final Set<ClassFilter> f1 = new HashSet<ClassFilter>(Math.max((int) (filters1.length/.75f) + 1, 16));
286 final Set<ClassFilter> f2 = new HashSet<ClassFilter>(Math.max((int) (filters2.length/.75f) + 1, 16));
287 Collections.addAll(f1, filters1);
288 Collections.addAll(f2, filters2);
289 return f2.equals(f1);
292 private static boolean elementListsEqual(List<Element> l1, List<Element> l2) {
293 if(l1 == null) return l2 == null;
294 if(l2 == null) return false;
296 if(l1.size() != l2.size()) return false;
298 Iterator<Element> i1 = l1.iterator();
300 for (Element aL2 : l2) {
301 Element elem1 = i1.next();
303 if (!elementsEqual(elem1, aL2)) return false;
308 private static boolean attributeListsEqual(List<Attribute> l1, List<Attribute> l2) {
309 if(l1 == null) return l2 == null;
310 if(l2 == null) return false;
312 if(l1.size() != l2.size()) return false;
314 Iterator<Attribute> i1 = l1.iterator();
316 for (Attribute aL2 : l2) {
317 Attribute attr1 = i1.next();
319 if (!Comparing.equal(attr1.getName(), aL2.getName()) || !Comparing.equal(attr1.getValue(), aL2.getValue())) {
326 public static boolean elementsEqual(Element e1, Element e2) {
330 if (!Comparing.equal(e1.getName(), e2.getName())) {
333 if (!elementListsEqual (e1.getChildren(), e2.getChildren())) {
336 if (!attributeListsEqual(e1.getAttributes(), e2.getAttributes())) {
342 @SuppressWarnings({"HardCodedStringLiteral"})
343 public static boolean externalizableEqual(JDOMExternalizable e1, JDOMExternalizable e2) {
344 Element root1 = new Element("root");
345 Element root2 = new Element("root");
347 e1.writeExternal(root1);
349 catch (WriteExternalException e) {
353 e2.writeExternal(root2);
355 catch (WriteExternalException e) {
359 return elementsEqual(root1, root2);
363 public static List<Pair<Breakpoint, Event>> getEventDescriptors(SuspendContextImpl suspendContext) {
364 DebuggerManagerThreadImpl.assertIsManagerThread();
365 if(suspendContext == null) {
366 return Collections.emptyList();
368 final EventSet events = suspendContext.getEventSet();
370 return Collections.emptyList();
372 final List<Pair<Breakpoint, Event>> eventDescriptors = new SmartList<Pair<Breakpoint, Event>>();
374 final RequestManagerImpl requestManager = suspendContext.getDebugProcess().getRequestsManager();
375 for (final Event event : events) {
376 final Requestor requestor = requestManager.findRequestor(event.request());
377 if (requestor instanceof Breakpoint) {
378 eventDescriptors.add(Pair.create((Breakpoint)requestor, event));
381 return eventDescriptors;
384 public static TextWithImports getEditorText(final Editor editor) {
385 if (editor == null) {
388 final Project project = editor.getProject();
389 if (project == null) return null;
391 String defaultExpression = editor.getSelectionModel().getSelectedText();
392 if (defaultExpression == null) {
393 int offset = editor.getCaretModel().getOffset();
394 PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
395 if (psiFile != null) {
396 PsiElement elementAtCursor = psiFile.findElementAt(offset);
397 if (elementAtCursor != null) {
398 final EditorTextProvider textProvider = EditorTextProvider.EP.forLanguage(elementAtCursor.getLanguage());
399 if (textProvider != null) {
400 final TextWithImports editorText = textProvider.getEditorText(elementAtCursor);
401 if (editorText != null) return editorText;
407 return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, defaultExpression);
412 private static int myThreadDumpsCount = 0;
413 private static int myCurrentThreadDumpId = 1;
415 private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump";
417 public static void addThreadDump(Project project, List<ThreadState> threads, final RunnerLayoutUi ui, DebuggerSession session) {
418 final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
419 consoleBuilder.filters(ExceptionFilters.getFilters(session.getSearchScope()));
420 final ConsoleView consoleView = consoleBuilder.getConsole();
421 final DefaultActionGroup toolbarActions = new DefaultActionGroup();
422 consoleView.allowHeavyFilters();
423 final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads);
425 final String id = THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId;
426 final Content content = ui.createContent(id, panel, id, null, null);
427 content.setCloseable(true);
428 content.setDescription("Thread Dump");
429 ui.addContent(content);
430 ui.selectAndFocus(content, true, true);
431 myThreadDumpsCount++;
432 myCurrentThreadDumpId++;
433 Disposer.register(content, new Disposable() {
435 public void dispose() {
436 myThreadDumpsCount--;
437 if (myThreadDumpsCount == 0) {
438 myCurrentThreadDumpId = 1;
442 Disposer.register(content, consoleView);
443 ui.selectAndFocus(content, true, false);
444 if (threads.size() > 0) {
445 panel.selectStackFrame(0);
449 public abstract DebuggerTreeNode getSelectedNode (DataContext context);
451 public abstract EvaluatorBuilder getEvaluatorBuilder();
453 public abstract CompletionEditor createEditor(Project project, PsiElement context, @NonNls String recentsId);
456 public static CodeFragmentFactory findAppropriateCodeFragmentFactory(final TextWithImports text, final PsiElement context) {
457 CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable<CodeFragmentFactory>() {
459 public CodeFragmentFactory compute() {
460 final FileType fileType = text.getFileType();
461 final List<CodeFragmentFactory> factories = getCodeFragmentFactories(context);
462 if (fileType == null) {
463 return factories.get(0);
465 for (CodeFragmentFactory factory : factories) {
466 if (factory.getFileType().equals(fileType)) {
470 return DefaultCodeFragmentFactory.getInstance();
473 return new CodeFragmentFactoryContextWrapper(factory);
476 private static class SigReader {
480 SigReader(String s) {
485 return buffer.charAt(pos++);
489 return buffer.charAt(pos);
493 return buffer.length() <= pos;
496 @NonNls String getSignature() {
497 if (eof()) return "";
520 pos = buffer.indexOf(';', start) + 1;
521 LOG.assertTrue(pos > 0);
522 return buffer.substring(start, pos - 1).replace('/', '.');
524 return getSignature() + "[]";
526 StringBuilder result = new StringBuilder("(");
527 String separator = "";
528 while (peek() != ')') {
529 result.append(separator);
530 result.append(getSignature());
535 return getSignature() + " " + getClassName() + "." + getMethodName() + " " + result;
537 // LOG.assertTrue(false, "unknown signature " + buffer);
542 String getMethodName() {
546 String getClassName() {
551 public static String methodName(final Method m) {
552 return methodName(signatureToName(m.declaringType().signature()), m.name(), m.signature());
555 public static String methodName(final String className, final String methodName, final String signature) {
557 return new SigReader(signature) {
559 String getMethodName() {
564 String getClassName() {
569 catch (Exception ignored) {
570 if (LOG.isDebugEnabled()) {
571 LOG.debug("Internal error : unknown signature" + signature);
573 return className + "." + methodName;
577 public static String signatureToName(String s) {
578 return new SigReader(s).getSignature();
581 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, double value) {
582 if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) {
583 return vm.mirrorOf(value);
585 if (PsiType.FLOAT.getPresentableText().equals(expectedType)) {
586 return vm.mirrorOf((float)value);
588 return createValue(vm, expectedType, (long)value);
591 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, long value) {
592 if (PsiType.LONG.getPresentableText().equals(expectedType)) {
593 return vm.mirrorOf(value);
595 if (PsiType.INT.getPresentableText().equals(expectedType)) {
596 return vm.mirrorOf((int)value);
598 if (PsiType.SHORT.getPresentableText().equals(expectedType)) {
599 return vm.mirrorOf((short)value);
601 if (PsiType.BYTE.getPresentableText().equals(expectedType)) {
602 return vm.mirrorOf((byte)value);
604 if (PsiType.CHAR.getPresentableText().equals(expectedType)) {
605 return vm.mirrorOf((char)value);
607 if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) {
608 return vm.mirrorOf((double)value);
610 if (PsiType.FLOAT.getPresentableText().equals(expectedType)) {
611 return vm.mirrorOf((float)value);
616 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, boolean value) {
617 if (PsiType.BOOLEAN.getPresentableText().equals(expectedType)) {
618 return vm.mirrorOf(value);
623 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, char value) {
624 if (PsiType.CHAR.getPresentableText().equals(expectedType)) {
625 return vm.mirrorOf(value);
627 if (PsiType.LONG.getPresentableText().equals(expectedType)) {
628 return vm.mirrorOf((long)value);
630 if (PsiType.INT.getPresentableText().equals(expectedType)) {
631 return vm.mirrorOf((int)value);
633 if (PsiType.SHORT.getPresentableText().equals(expectedType)) {
634 return vm.mirrorOf((short)value);
636 if (PsiType.BYTE.getPresentableText().equals(expectedType)) {
637 return vm.mirrorOf((byte)value);
642 public static String truncateString(final String str) {
643 // leave a small gap over XValueNode.MAX_VALUE_LENGTH to detect oversize
644 if (str.length() > XValueNode.MAX_VALUE_LENGTH + 5) {
645 return str.substring(0, XValueNode.MAX_VALUE_LENGTH + 5);
650 public static String getThreadStatusText(int statusId) {
652 case ThreadReference.THREAD_STATUS_MONITOR:
653 return DebuggerBundle.message("status.thread.monitor");
654 case ThreadReference.THREAD_STATUS_NOT_STARTED:
655 return DebuggerBundle.message("status.thread.not.started");
656 case ThreadReference.THREAD_STATUS_RUNNING:
657 return DebuggerBundle.message("status.thread.running");
658 case ThreadReference.THREAD_STATUS_SLEEPING:
659 return DebuggerBundle.message("status.thread.sleeping");
660 case ThreadReference.THREAD_STATUS_UNKNOWN:
661 return DebuggerBundle.message("status.thread.unknown");
662 case ThreadReference.THREAD_STATUS_WAIT:
663 return DebuggerBundle.message("status.thread.wait");
664 case ThreadReference.THREAD_STATUS_ZOMBIE:
665 return DebuggerBundle.message("status.thread.zombie");
667 return DebuggerBundle.message("status.thread.undefined");
671 public static String prepareValueText(String text, Project project) {
672 text = StringUtil.unquoteString(text);
673 text = StringUtil.unescapeStringCharacters(text);
674 int tabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(StdFileTypes.JAVA);
678 return text.replace("\t", StringUtil.repeat(" ", tabSize));
682 public static XSourcePosition toXSourcePosition(@NotNull SourcePosition position) {
683 VirtualFile file = position.getFile().getVirtualFile();
685 file = position.getFile().getOriginalFile().getVirtualFile();
690 return new JavaXSourcePosition(position, file);
693 private static class JavaXSourcePosition implements XSourcePosition {
694 private final SourcePosition mySourcePosition;
695 @NotNull private final VirtualFile myFile;
697 public JavaXSourcePosition(@NotNull SourcePosition sourcePosition, @NotNull VirtualFile file) {
698 mySourcePosition = sourcePosition;
703 public int getLine() {
704 return mySourcePosition.getLine();
708 public int getOffset() {
709 return mySourcePosition.getOffset();
714 public VirtualFile getFile() {
720 public Navigatable createNavigatable(@NotNull Project project) {
721 return XSourcePositionImpl.doCreateOpenFileDescriptor(project, this);
726 * Decompiler aware version
729 public static PsiElement findElementAt(@Nullable PsiFile file, int offset) {
730 if (file instanceof PsiCompiledFile) {
731 file = ((PsiCompiledFile)file).getDecompiledPsiFile();
733 if (file == null) return null;
734 return file.findElementAt(offset);