blob: 4016289147a79f329a97b98a05b1b5cc76151a2d [file] [log] [blame]
/*
* 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.
*/
package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerContext;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.ui.tree.DebuggerTreeNode;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.debugger.ui.tree.ValueDescriptor;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizerUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiExpression;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
import com.sun.jdi.*;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import java.util.List;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
public class ToStringRenderer extends NodeRendererImpl {
public static final @NonNls String UNIQUE_ID = "ToStringRenderer";
private boolean USE_CLASS_FILTERS = false;
private ClassFilter[] myClassFilters = ClassFilter.EMPTY_ARRAY;
public ToStringRenderer() {
setEnabled(true);
}
public String getUniqueId() {
return UNIQUE_ID;
}
public @NonNls String getName() {
return "toString";
}
public void setName(String name) {
// prohibit change
}
public ToStringRenderer clone() {
final ToStringRenderer cloned = (ToStringRenderer)super.clone();
final ClassFilter[] classFilters = (myClassFilters.length > 0)? new ClassFilter[myClassFilters.length] : ClassFilter.EMPTY_ARRAY;
for (int idx = 0; idx < classFilters.length; idx++) {
classFilters[idx] = myClassFilters[idx].clone();
}
cloned.myClassFilters = classFilters;
return cloned;
}
public String calcLabel(final ValueDescriptor valueDescriptor, EvaluationContext evaluationContext, final DescriptorLabelListener labelListener)
throws EvaluateException {
final Value value = valueDescriptor.getValue();
BatchEvaluator.getBatchEvaluator(evaluationContext.getDebugProcess()).invoke(new ToStringCommand(evaluationContext, value) {
public void evaluationResult(String message) {
valueDescriptor.setValueLabel(
// no need to add quotes and escape characters here, XValueTextRendererImpl handles the presentation
message == null? "" : /*"\"" + DebuggerUtils.convertToPresentationString(*/DebuggerUtilsEx.truncateString(message)/*) + "\""*/
);
labelListener.labelChanged();
}
public void evaluationError(String message) {
final String msg = value != null? message + " " + DebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", value.type().name()) : message;
valueDescriptor.setValueLabelFailed(new EvaluateException(msg, null));
labelListener.labelChanged();
}
});
return XDebuggerUIConstants.COLLECTING_DATA_MESSAGE;
}
public boolean isUseClassFilters() {
return USE_CLASS_FILTERS;
}
public void setUseClassFilters(boolean value) {
USE_CLASS_FILTERS = value;
}
public boolean isApplicable(Type type) {
if(!(type instanceof ReferenceType)) {
return false;
}
if(JAVA_LANG_STRING.equals(type.name())) {
return false; // do not render 'String' objects for performance reasons
}
if(!overridesToString(type)) {
return false;
}
if (USE_CLASS_FILTERS) {
if (!isFiltered(type)) {
return false;
}
}
return true;
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static boolean overridesToString(Type type) {
if(type instanceof ClassType) {
final List<Method> methods = ((ClassType)type).methodsByName("toString", "()Ljava/lang/String;");
for (Method method : methods) {
if (!(method.declaringType().name()).equals(CommonClassNames.JAVA_LANG_OBJECT)) {
return true;
}
}
}
return false;
}
public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) {
final DebugProcessImpl debugProcess = (DebugProcessImpl)evaluationContext.getDebugProcess();
debugProcess.getDefaultRenderer(value).buildChildren(value, builder, evaluationContext);
}
public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
final Value parentValue = ((ValueDescriptor)node.getParent().getDescriptor()).getValue();
final DebugProcessImpl debugProcess = (DebugProcessImpl)context.getDebugProcess();
return debugProcess.getDefaultRenderer(parentValue).getChildValueExpression(node, context);
}
public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
final DebugProcessImpl debugProcess = (DebugProcessImpl)evaluationContext.getDebugProcess();
return debugProcess.getDefaultRenderer(value).isExpandable(value, evaluationContext, parentDescriptor);
}
@SuppressWarnings({"HardCodedStringLiteral"})
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
final String value = JDOMExternalizerUtil.readField(element, "USE_CLASS_FILTERS");
USE_CLASS_FILTERS = "true".equalsIgnoreCase(value);
myClassFilters = DebuggerUtilsEx.readFilters(element.getChildren("filter"));
}
@SuppressWarnings({"HardCodedStringLiteral"})
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
JDOMExternalizerUtil.writeField(element, "USE_CLASS_FILTERS", USE_CLASS_FILTERS? "true" : "false");
DebuggerUtilsEx.writeFilters(element, "filter", myClassFilters);
}
public ClassFilter[] getClassFilters() {
return myClassFilters;
}
public void setClassFilters(ClassFilter[] classFilters) {
myClassFilters = classFilters != null? classFilters : ClassFilter.EMPTY_ARRAY;
}
private boolean isFiltered(Type t) {
if (t instanceof ReferenceType) {
for (ClassFilter classFilter : myClassFilters) {
if (classFilter.isEnabled() && DebuggerUtils.getSuperType(t, classFilter.getPattern()) != null) {
return true;
}
}
}
return DebuggerUtilsEx.isFiltered(t.name(), myClassFilters);
}
}