blob: 7fe89bb422992c938e2b9d79fdc56374ec99cd76 [file] [log] [blame]
/*
* Copyright 2000-2009 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.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.engine.jdi.StackFrameProxy;
import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.debugger.ui.tree.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
import com.sun.jdi.*;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* User: lex
* Date: Sep 17, 2003
* Time: 2:04:00 PM
*/
public class ClassRenderer extends NodeRendererImpl{
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.tree.render.ClassRenderer");
public static final @NonNls String UNIQUE_ID = "ClassRenderer";
public boolean SHOW_SYNTHETICS = true;
public boolean SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES = true;
public boolean SHOW_STATIC = false;
public boolean SHOW_STATIC_FINAL = false;
public boolean SHOW_FQ_TYPE_NAMES = true;
public boolean SHOW_DECLARED_TYPE = false;
public boolean SHOW_OBJECT_ID = true;
public ClassRenderer() {
myProperties.setEnabled(true);
}
public final String renderTypeName(final String typeName) {
if (SHOW_FQ_TYPE_NAMES) {
return typeName;
}
final int dotIndex = typeName.lastIndexOf('.');
if (dotIndex > 0) {
return typeName.substring(dotIndex + 1);
}
return typeName;
}
@Override
public String getUniqueId() {
return UNIQUE_ID;
}
@Override
public boolean isEnabled() {
return myProperties.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
myProperties.setEnabled(enabled);
}
@Override
public ClassRenderer clone() {
return (ClassRenderer) super.clone();
}
@Override
public String calcLabel(ValueDescriptor descriptor, EvaluationContext evaluationContext, DescriptorLabelListener labelListener) throws EvaluateException {
return calcLabel(descriptor);
}
protected static String calcLabel(ValueDescriptor descriptor) {
final ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor;
final Value value = valueDescriptor.getValue();
if (value instanceof ObjectReference) {
final StringBuilder buf = StringBuilderSpinAllocator.alloc();
try {
if (value instanceof StringReference) {
// no need to add quotes and escape characters here, XValueTextRendererImpl handles the presentation
//buf.append('\"');
//buf.append(DebuggerUtils.convertToPresentationString(((StringReference)value).value()));
//buf.append('\"');
buf.append(((StringReference)value).value());
}
else if (value instanceof ClassObjectReference) {
ReferenceType type = ((ClassObjectReference)value).reflectedType();
buf.append((type != null)?type.name():"{...}");
}
else {
final ObjectReference objRef = (ObjectReference)value;
final Type type = objRef.type();
if (type instanceof ClassType && ((ClassType)type).isEnum()) {
final String name = getEnumConstantName(objRef, (ClassType)type);
if (name != null) {
buf.append(name);
}
else {
buf.append(type.name());
}
}
else {
buf.append(ValueDescriptorImpl.getIdLabel(objRef));
}
}
return buf.toString();
}
finally {
StringBuilderSpinAllocator.dispose(buf);
}
}
else if(value == null) {
//noinspection HardCodedStringLiteral
return "null";
}
else {
return DebuggerBundle.message("label.undefined");
}
}
@Override
public void buildChildren(final Value value, final ChildrenBuilder builder, final EvaluationContext evaluationContext) {
DebuggerManagerThreadImpl.assertIsManagerThread();
final ValueDescriptorImpl parentDescriptor = (ValueDescriptorImpl)builder.getParentDescriptor();
final NodeManager nodeManager = builder.getNodeManager();
final NodeDescriptorFactory nodeDescriptorFactory = builder.getDescriptorManager();
List<DebuggerTreeNode> children = new ArrayList<DebuggerTreeNode>();
if (value instanceof ObjectReference) {
final ObjectReference objRef = (ObjectReference)value;
final ReferenceType refType = objRef.referenceType();
// default ObjectReference processing
final List<Field> fields = refType.allFields();
if (fields.size() > 0) {
for (final Field field : fields) {
if (!shouldDisplay(evaluationContext, objRef, field)) {
continue;
}
children.add(nodeManager.createNode(nodeDescriptorFactory.getFieldDescriptor(parentDescriptor, objRef, field), evaluationContext));
}
if (XDebuggerSettingsManager.getInstance().getDataViewSettings().isSortValues()) {
Collections.sort(children, NodeManagerImpl.getNodeComparator());
}
}
else {
children.add(nodeManager.createMessageNode(MessageDescriptor.CLASS_HAS_NO_FIELDS.getLabel()));
}
}
builder.setChildren(children);
}
private boolean shouldDisplay(EvaluationContext context, @NotNull ObjectReference objInstance, @NotNull Field field) {
final boolean isSynthetic = DebuggerUtils.isSynthetic(field);
if (!SHOW_SYNTHETICS && isSynthetic) {
return false;
}
if (SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES && isSynthetic) {
try {
final StackFrameProxy frameProxy = context.getFrameProxy();
if (frameProxy != null) {
final Location location = frameProxy.location();
if (location != null && objInstance.equals(context.getThisObject()) && Comparing.equal(objInstance.referenceType(), location.declaringType()) && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) {
return false;
}
}
}
catch (EvaluateException ignored) {
}
}
if(!SHOW_STATIC && field.isStatic()) {
return false;
}
if(!SHOW_STATIC_FINAL && field.isStatic() && field.isFinal()) {
return false;
}
return true;
}
@Override
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
DefaultJDOMExternalizer.readExternal(this, element);
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
DefaultJDOMExternalizer.writeExternal(this, element);
}
@Override
public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException {
FieldDescriptor fieldDescriptor = (FieldDescriptor)node.getDescriptor();
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(node.getProject()).getElementFactory();
try {
return elementFactory.createExpressionFromText(fieldDescriptor.getField().name(), DebuggerUtils.findClass(
fieldDescriptor.getObject().referenceType().name(), context.getProject(), context.getDebugProcess().getSearchScope())
);
}
catch (IncorrectOperationException e) {
throw new EvaluateException(DebuggerBundle.message("error.invalid.field.name", fieldDescriptor.getField().name()), null);
}
}
private static boolean valueExpandable(Value value) {
try {
if(value instanceof ArrayReference) {
return ((ArrayReference)value).length() > 0;
}
else if(value instanceof ObjectReference) {
return ((ObjectReference)value).referenceType().allFields().size() > 0;
}
}
catch (ObjectCollectedException e) {
return true;
}
return false;
}
@Override
public boolean isExpandable(Value value, EvaluationContext evaluationContext, NodeDescriptor parentDescriptor) {
DebuggerManagerThreadImpl.assertIsManagerThread();
return valueExpandable(value);
}
@Override
public boolean isApplicable(Type type) {
return type instanceof ReferenceType && !(type instanceof ArrayType);
}
@Override
public @NonNls String getName() {
return "Object";
}
@Override
public void setName(String text) {
LOG.assertTrue(false);
}
@Nullable
public static String getEnumConstantName(final ObjectReference objRef, ClassType classType) {
do {
if (!classType.isPrepared()) {
return null;
}
classType = classType.superclass();
if (classType == null) {
return null;
}
}
while (!("java.lang.Enum".equals(classType.name())));
//noinspection HardCodedStringLiteral
final Field field = classType.fieldByName("name");
if (field == null) {
return null;
}
final Value value = objRef.getValue(field);
if (!(value instanceof StringReference)) {
return null;
}
return ((StringReference)value).value();
}
}