| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Class DebuggerUtilsEx |
| * @author Jeka |
| */ |
| package com.intellij.debugger.impl; |
| |
| import com.intellij.debugger.DebuggerBundle; |
| import com.intellij.debugger.SourcePosition; |
| import com.intellij.debugger.engine.DebuggerManagerThreadImpl; |
| import com.intellij.debugger.engine.DebuggerUtils; |
| import com.intellij.debugger.engine.SuspendContextImpl; |
| import com.intellij.debugger.engine.evaluation.*; |
| import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder; |
| import com.intellij.debugger.engine.requests.RequestManagerImpl; |
| import com.intellij.debugger.jdi.VirtualMachineProxyImpl; |
| import com.intellij.debugger.requests.Requestor; |
| import com.intellij.debugger.ui.CompletionEditor; |
| import com.intellij.debugger.ui.breakpoints.Breakpoint; |
| import com.intellij.debugger.ui.tree.DebuggerTreeNode; |
| import com.intellij.openapi.actionSystem.DataContext; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.fileTypes.FileType; |
| import com.intellij.openapi.fileTypes.StdFileTypes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.*; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsManager; |
| import com.intellij.ui.classFilter.ClassFilter; |
| import com.intellij.util.SmartList; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.xdebugger.XSourcePosition; |
| import com.intellij.xdebugger.impl.XSourcePositionImpl; |
| import com.sun.jdi.*; |
| import com.sun.jdi.event.Event; |
| import com.sun.jdi.event.EventSet; |
| import org.jdom.Attribute; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| import java.util.regex.PatternSyntaxException; |
| |
| public abstract class DebuggerUtilsEx extends DebuggerUtils { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerUtilsEx"); |
| |
| private static final int MAX_LABEL_SIZE = 255; |
| |
| /** |
| * @param context |
| * @return all CodeFragmentFactoryProviders that provide code fragment factories suitable in the context given |
| */ |
| public static List<CodeFragmentFactory> getCodeFragmentFactories(@Nullable PsiElement context) { |
| final DefaultCodeFragmentFactory defaultFactory = DefaultCodeFragmentFactory.getInstance(); |
| final CodeFragmentFactory[] providers = ApplicationManager.getApplication().getExtensions(CodeFragmentFactory.EXTENSION_POINT_NAME); |
| final List<CodeFragmentFactory> suitableFactories = new ArrayList<CodeFragmentFactory>(providers.length); |
| if (providers.length > 0) { |
| for (CodeFragmentFactory factory : providers) { |
| if (factory != defaultFactory && factory.isContextAccepted(context)) { |
| suitableFactories.add(factory); |
| } |
| } |
| } |
| suitableFactories.add(defaultFactory); // let default factory be the last one |
| return suitableFactories; |
| } |
| |
| |
| public static PsiMethod findPsiMethod(PsiFile file, int offset) { |
| PsiElement element = null; |
| |
| while(offset >= 0) { |
| element = file.findElementAt(offset); |
| if(element != null) { |
| break; |
| } |
| offset --; |
| } |
| |
| for (; element != null; element = element.getParent()) { |
| if (element instanceof PsiClass || element instanceof PsiLambdaExpression) { |
| return null; |
| } |
| if (element instanceof PsiMethod) { |
| return (PsiMethod)element; |
| } |
| } |
| return null; |
| } |
| |
| |
| public static boolean isAssignableFrom(final String baseQualifiedName, ReferenceType checkedType) { |
| if (CommonClassNames.JAVA_LANG_OBJECT.equals(baseQualifiedName)) { |
| return true; |
| } |
| return getSuperClass(baseQualifiedName, checkedType) != null; |
| } |
| |
| public static ReferenceType getSuperClass(final String baseQualifiedName, ReferenceType checkedType) { |
| if (baseQualifiedName.equals(checkedType.name())) { |
| return checkedType; |
| } |
| |
| if (checkedType instanceof ClassType) { |
| ClassType classType = (ClassType)checkedType; |
| ClassType superClassType = classType.superclass(); |
| if (superClassType != null) { |
| ReferenceType superClass = getSuperClass(baseQualifiedName, superClassType); |
| if (superClass != null) { |
| return superClass; |
| } |
| } |
| List<InterfaceType> interfaces = classType.allInterfaces(); |
| for (InterfaceType iface : interfaces) { |
| ReferenceType superClass = getSuperClass(baseQualifiedName, iface); |
| if (superClass != null) { |
| return superClass; |
| } |
| } |
| } |
| |
| if (checkedType instanceof InterfaceType) { |
| List<InterfaceType> list = ((InterfaceType)checkedType).superinterfaces(); |
| for (InterfaceType superInterface : list) { |
| ReferenceType superClass = getSuperClass(baseQualifiedName, superInterface); |
| if (superClass != null) { |
| return superClass; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static boolean valuesEqual(Value val1, Value val2) { |
| if (val1 == null) { |
| return val2 == null; |
| } |
| if (val2 == null) { |
| return false; |
| } |
| if (val1 instanceof StringReference && val2 instanceof StringReference) { |
| return ((StringReference)val1).value().equals(((StringReference)val2).value()); |
| } |
| return val1.equals(val2); |
| } |
| |
| public static String getValueOrErrorAsString(final EvaluationContext evaluationContext, Value value) { |
| try { |
| return getValueAsString(evaluationContext, value); |
| } |
| catch (EvaluateException e) { |
| return e.getMessage(); |
| } |
| } |
| |
| public static boolean isCharOrInteger(Value value) { |
| return value instanceof CharValue || isInteger(value); |
| } |
| |
| private static Set<String> myCharOrIntegers; |
| |
| @SuppressWarnings({"HardCodedStringLiteral"}) |
| public static boolean isCharOrIntegerArray(Value value) { |
| if (value == null) return false; |
| if (myCharOrIntegers == null) { |
| myCharOrIntegers = new HashSet<String>(); |
| myCharOrIntegers.add("C"); |
| myCharOrIntegers.add("B"); |
| myCharOrIntegers.add("S"); |
| myCharOrIntegers.add("I"); |
| myCharOrIntegers.add("J"); |
| } |
| |
| String signature = value.type().signature(); |
| int i; |
| for (i = 0; signature.charAt(i) == '['; i++) ; |
| if (i == 0) return false; |
| signature = signature.substring(i, signature.length()); |
| return myCharOrIntegers.contains(signature); |
| } |
| |
| public static ClassFilter create(Element element) throws InvalidDataException { |
| ClassFilter filter = new ClassFilter(); |
| DefaultJDOMExternalizer.readExternal(filter, element); |
| return filter; |
| } |
| |
| private static boolean isFiltered(ClassFilter classFilter, String qName) { |
| if (!classFilter.isEnabled()) { |
| return false; |
| } |
| try { |
| if (classFilter.matches(qName)) { |
| return true; |
| } |
| } |
| catch (PatternSyntaxException e) { |
| LOG.debug(e); |
| } |
| return false; |
| } |
| |
| public static boolean isFiltered(String qName, ClassFilter[] classFilters) { |
| return isFiltered(qName, Arrays.asList(classFilters)); |
| } |
| |
| public static boolean isFiltered(String qName, List<ClassFilter> classFilters) { |
| if(qName.indexOf('[') != -1) { |
| return false; //is array |
| } |
| |
| for (ClassFilter filter : classFilters) { |
| if (isFiltered(filter, qName)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static int getEnabledNumber(ClassFilter[] classFilters) { |
| int res = 0; |
| for (ClassFilter filter : classFilters) { |
| if (filter.isEnabled()) { |
| res++; |
| } |
| } |
| return res; |
| } |
| |
| public static ClassFilter[] readFilters(List<Element> children) throws InvalidDataException { |
| if (ContainerUtil.isEmpty(children)) { |
| return ClassFilter.EMPTY_ARRAY; |
| } |
| |
| ClassFilter[] filters = new ClassFilter[children.size()]; |
| for (int i = 0, size = children.size(); i < size; i++) { |
| filters[i] = create(children.get(i)); |
| } |
| return filters; |
| } |
| |
| public static void writeFilters(Element parentNode, @NonNls String tagName, ClassFilter[] filters) throws WriteExternalException { |
| for (ClassFilter filter : filters) { |
| Element element = new Element(tagName); |
| parentNode.addContent(element); |
| DefaultJDOMExternalizer.writeExternal(filter, element); |
| } |
| } |
| |
| public static boolean filterEquals(ClassFilter[] filters1, ClassFilter[] filters2) { |
| if (filters1.length != filters2.length) { |
| return false; |
| } |
| final Set<ClassFilter> f1 = new HashSet<ClassFilter>(Math.max((int) (filters1.length/.75f) + 1, 16)); |
| final Set<ClassFilter> f2 = new HashSet<ClassFilter>(Math.max((int) (filters2.length/.75f) + 1, 16)); |
| Collections.addAll(f1, filters1); |
| Collections.addAll(f2, filters2); |
| return f2.equals(f1); |
| } |
| |
| private static boolean elementListsEqual(List<Element> l1, List<Element> l2) { |
| if(l1 == null) return l2 == null; |
| if(l2 == null) return false; |
| |
| if(l1.size() != l2.size()) return false; |
| |
| Iterator<Element> i1 = l1.iterator(); |
| |
| for (Element aL2 : l2) { |
| Element elem1 = i1.next(); |
| |
| if (!elementsEqual(elem1, aL2)) return false; |
| } |
| return true; |
| } |
| |
| private static boolean attributeListsEqual(List<Attribute> l1, List<Attribute> l2) { |
| if(l1 == null) return l2 == null; |
| if(l2 == null) return false; |
| |
| if(l1.size() != l2.size()) return false; |
| |
| Iterator<Attribute> i1 = l1.iterator(); |
| |
| for (Attribute aL2 : l2) { |
| Attribute attr1 = i1.next(); |
| |
| if (!Comparing.equal(attr1.getName(), aL2.getName()) || !Comparing.equal(attr1.getValue(), aL2.getValue())) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public static boolean elementsEqual(Element e1, Element e2) { |
| if(e1 == null) { |
| return e2 == null; |
| } |
| if (!Comparing.equal(e1.getName(), e2.getName())) { |
| return false; |
| } |
| if (!elementListsEqual (e1.getChildren(), e2.getChildren())) { |
| return false; |
| } |
| if (!attributeListsEqual(e1.getAttributes(), e2.getAttributes())) { |
| return false; |
| } |
| return true; |
| } |
| |
| @SuppressWarnings({"HardCodedStringLiteral"}) |
| public static boolean externalizableEqual(JDOMExternalizable e1, JDOMExternalizable e2) { |
| Element root1 = new Element("root"); |
| Element root2 = new Element("root"); |
| try { |
| e1.writeExternal(root1); |
| } |
| catch (WriteExternalException e) { |
| LOG.debug(e); |
| } |
| try { |
| e2.writeExternal(root2); |
| } |
| catch (WriteExternalException e) { |
| LOG.debug(e); |
| } |
| |
| return elementsEqual(root1, root2); |
| } |
| |
| @NotNull |
| public static List<Pair<Breakpoint, Event>> getEventDescriptors(SuspendContextImpl suspendContext) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| if(suspendContext == null) { |
| return Collections.emptyList(); |
| } |
| final EventSet events = suspendContext.getEventSet(); |
| if(events == null) { |
| return Collections.emptyList(); |
| } |
| final List<Pair<Breakpoint, Event>> eventDescriptors = new SmartList<Pair<Breakpoint, Event>>(); |
| |
| final RequestManagerImpl requestManager = suspendContext.getDebugProcess().getRequestsManager(); |
| for (final Event event : events) { |
| final Requestor requestor = requestManager.findRequestor(event.request()); |
| if (requestor instanceof Breakpoint) { |
| eventDescriptors.add(Pair.create((Breakpoint)requestor, event)); |
| } |
| } |
| return eventDescriptors; |
| } |
| |
| public static TextWithImports getEditorText(final Editor editor) { |
| if (editor == null) { |
| return null; |
| } |
| final Project project = editor.getProject(); |
| if (project == null) return null; |
| |
| String defaultExpression = editor.getSelectionModel().getSelectedText(); |
| if (defaultExpression == null) { |
| int offset = editor.getCaretModel().getOffset(); |
| PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); |
| if (psiFile != null) { |
| PsiElement elementAtCursor = psiFile.findElementAt(offset); |
| if (elementAtCursor != null) { |
| final EditorTextProvider textProvider = EditorTextProvider.EP.forLanguage(elementAtCursor.getLanguage()); |
| if (textProvider != null) { |
| final TextWithImports editorText = textProvider.getEditorText(elementAtCursor); |
| if (editorText != null) return editorText; |
| } |
| } |
| } |
| } |
| else { |
| return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, defaultExpression); |
| } |
| return null; |
| } |
| |
| public abstract DebuggerTreeNode getSelectedNode (DataContext context); |
| |
| public abstract EvaluatorBuilder getEvaluatorBuilder(); |
| |
| public abstract CompletionEditor createEditor(Project project, PsiElement context, @NonNls String recentsId); |
| |
| @NotNull |
| public static CodeFragmentFactory findAppropriateCodeFragmentFactory(final TextWithImports text, final PsiElement context) { |
| CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable<CodeFragmentFactory>() { |
| @Override |
| public CodeFragmentFactory compute() { |
| final FileType fileType = text.getFileType(); |
| final List<CodeFragmentFactory> factories = getCodeFragmentFactories(context); |
| if (fileType == null) { |
| return factories.get(0); |
| } |
| for (CodeFragmentFactory factory : factories) { |
| if (factory.getFileType().equals(fileType)) { |
| return factory; |
| } |
| } |
| return DefaultCodeFragmentFactory.getInstance(); |
| } |
| }); |
| return new CodeFragmentFactoryContextWrapper(factory); |
| } |
| |
| private static class SigReader { |
| final String buffer; |
| int pos = 0; |
| |
| SigReader(String s) { |
| buffer = s; |
| } |
| |
| int get() { |
| return buffer.charAt(pos++); |
| } |
| |
| int peek() { |
| return buffer.charAt(pos); |
| } |
| |
| boolean eof() { |
| return buffer.length() <= pos; |
| } |
| |
| @NonNls String getSignature() { |
| if (eof()) return ""; |
| |
| switch (get()) { |
| case 'Z': |
| return "boolean"; |
| case 'B': |
| return "byte"; |
| case 'C': |
| return "char"; |
| case 'S': |
| return "short"; |
| case 'I': |
| return "int"; |
| case 'J': |
| return "long"; |
| case 'F': |
| return "float"; |
| case 'D': |
| return "double"; |
| case 'V': |
| return "void"; |
| case 'L': |
| int start = pos; |
| pos = buffer.indexOf(';', start) + 1; |
| LOG.assertTrue(pos > 0); |
| return buffer.substring(start, pos - 1).replace('/', '.'); |
| case '[': |
| return getSignature() + "[]"; |
| case '(': |
| StringBuilder result = new StringBuilder("("); |
| String separator = ""; |
| while (peek() != ')') { |
| result.append(separator); |
| result.append(getSignature()); |
| separator = ", "; |
| } |
| get(); |
| result.append(")"); |
| return getSignature() + " " + getClassName() + "." + getMethodName() + " " + result; |
| default: |
| // LOG.assertTrue(false, "unknown signature " + buffer); |
| return null; |
| } |
| } |
| |
| String getMethodName() { |
| return ""; |
| } |
| |
| String getClassName() { |
| return ""; |
| } |
| } |
| |
| public static String methodName(final Method m) { |
| return methodName(signatureToName(m.declaringType().signature()), m.name(), m.signature()); |
| } |
| |
| public static String methodName(final String className, final String methodName, final String signature) { |
| try { |
| return new SigReader(signature) { |
| @Override |
| String getMethodName() { |
| return methodName; |
| } |
| |
| @Override |
| String getClassName() { |
| return className; |
| } |
| }.getSignature(); |
| } |
| catch (Exception ignored) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Internal error : unknown signature" + signature); |
| } |
| return className + "." + methodName; |
| } |
| } |
| |
| public static String signatureToName(String s) { |
| return new SigReader(s).getSignature(); |
| } |
| |
| public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, double value) { |
| if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf(value); |
| } |
| if (PsiType.FLOAT.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((float)value); |
| } |
| return createValue(vm, expectedType, (long)value); |
| } |
| |
| public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, long value) { |
| if (PsiType.LONG.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf(value); |
| } |
| if (PsiType.INT.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((int)value); |
| } |
| if (PsiType.SHORT.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((short)value); |
| } |
| if (PsiType.BYTE.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((byte)value); |
| } |
| if (PsiType.CHAR.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((char)value); |
| } |
| if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((double)value); |
| } |
| if (PsiType.FLOAT.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((float)value); |
| } |
| return null; |
| } |
| |
| public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, boolean value) { |
| if (PsiType.BOOLEAN.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf(value); |
| } |
| return null; |
| } |
| |
| public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, char value) { |
| if (PsiType.CHAR.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf(value); |
| } |
| if (PsiType.LONG.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((long)value); |
| } |
| if (PsiType.INT.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((int)value); |
| } |
| if (PsiType.SHORT.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((short)value); |
| } |
| if (PsiType.BYTE.getPresentableText().equals(expectedType)) { |
| return vm.mirrorOf((byte)value); |
| } |
| return null; |
| } |
| |
| public static String truncateString(final String str) { |
| if (str.length() > MAX_LABEL_SIZE) { |
| return str.substring(0, MAX_LABEL_SIZE) + "..."; |
| } |
| return str; |
| } |
| |
| public static String getThreadStatusText(int statusId) { |
| switch (statusId) { |
| case ThreadReference.THREAD_STATUS_MONITOR: |
| return DebuggerBundle.message("status.thread.monitor"); |
| case ThreadReference.THREAD_STATUS_NOT_STARTED: |
| return DebuggerBundle.message("status.thread.not.started"); |
| case ThreadReference.THREAD_STATUS_RUNNING: |
| return DebuggerBundle.message("status.thread.running"); |
| case ThreadReference.THREAD_STATUS_SLEEPING: |
| return DebuggerBundle.message("status.thread.sleeping"); |
| case ThreadReference.THREAD_STATUS_UNKNOWN: |
| return DebuggerBundle.message("status.thread.unknown"); |
| case ThreadReference.THREAD_STATUS_WAIT: |
| return DebuggerBundle.message("status.thread.wait"); |
| case ThreadReference.THREAD_STATUS_ZOMBIE: |
| return DebuggerBundle.message("status.thread.zombie"); |
| default: |
| return DebuggerBundle.message("status.thread.undefined"); |
| } |
| } |
| |
| public static String prepareValueText(String text, Project project) { |
| text = StringUtil.unquoteString(text); |
| text = StringUtil.unescapeStringCharacters(text); |
| int tabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(StdFileTypes.JAVA); |
| if (tabSize < 0) { |
| tabSize = 0; |
| } |
| return text.replace("\t", StringUtil.repeat(" ", tabSize)); |
| } |
| |
| @Nullable |
| public static XSourcePosition toXSourcePosition(@NotNull SourcePosition position) { |
| return XSourcePositionImpl.create(position.getFile().getVirtualFile(), position.getLine()); |
| } |
| } |