| /* |
| * 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. |
| */ |
| |
| /* |
| * Class FieldEvaluator |
| * @author Jeka |
| */ |
| package com.intellij.debugger.engine.evaluation.expression; |
| |
| import com.intellij.debugger.DebuggerBundle; |
| import com.intellij.debugger.engine.JVMNameUtil; |
| import com.intellij.debugger.engine.evaluation.EvaluateException; |
| import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; |
| import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; |
| import com.intellij.debugger.impl.DebuggerUtilsEx; |
| import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl; |
| import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.PsiAnonymousClass; |
| import com.intellij.psi.PsiArrayType; |
| import com.intellij.psi.PsiClass; |
| import com.intellij.psi.PsiType; |
| import com.intellij.psi.util.PsiUtil; |
| import com.sun.jdi.*; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.Nullable; |
| |
| public class FieldEvaluator implements Evaluator { |
| private final Evaluator myObjectEvaluator; |
| private final TargetClassFilter myTargetClassFilter; |
| private final String myFieldName; |
| private Object myEvaluatedQualifier; |
| private Field myEvaluatedField; |
| |
| public interface TargetClassFilter { |
| TargetClassFilter ALL = new TargetClassFilter() { |
| public boolean acceptClass(final ReferenceType refType) { |
| return true; |
| } |
| }; |
| boolean acceptClass(ReferenceType refType); |
| } |
| |
| public FieldEvaluator(Evaluator objectEvaluator, TargetClassFilter filter, @NonNls String fieldName) { |
| myObjectEvaluator = objectEvaluator; |
| myFieldName = fieldName; |
| myTargetClassFilter = filter; |
| } |
| |
| public static TargetClassFilter createClassFilter(PsiType psiType) { |
| if(psiType instanceof PsiArrayType) { |
| return TargetClassFilter.ALL; |
| } |
| PsiClass psiClass = PsiUtil.resolveClassInType(psiType); |
| if (psiClass != null) { |
| return createClassFilter(psiClass); |
| } |
| return new FQNameClassFilter(psiType.getCanonicalText()); |
| } |
| |
| public static TargetClassFilter createClassFilter(PsiClass psiClass) { |
| if (psiClass instanceof PsiAnonymousClass) { |
| return TargetClassFilter.ALL; |
| } |
| if (PsiUtil.isLocalClass(psiClass)) { |
| return new LocalClassFilter(psiClass.getName()); |
| } |
| final String name = JVMNameUtil.getNonAnonymousClassName(psiClass); |
| return name != null? new FQNameClassFilter(name) : TargetClassFilter.ALL; |
| } |
| |
| @Nullable |
| private Field findField(Type t, final EvaluationContextImpl context) throws EvaluateException { |
| if(t instanceof ClassType) { |
| ClassType cls = (ClassType) t; |
| if(myTargetClassFilter.acceptClass(cls)) { |
| return cls.fieldByName(myFieldName); |
| } |
| for (final InterfaceType interfaceType : cls.interfaces()) { |
| final Field field = findField(interfaceType, context); |
| if (field != null) { |
| return field; |
| } |
| } |
| return findField(cls.superclass(), context); |
| } |
| else if(t instanceof InterfaceType) { |
| InterfaceType iface = (InterfaceType) t; |
| if(myTargetClassFilter.acceptClass(iface)) { |
| return iface.fieldByName(myFieldName); |
| } |
| for (final InterfaceType interfaceType : iface.superinterfaces()) { |
| final Field field = findField(interfaceType, context); |
| if (field != null) { |
| return field; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public Object evaluate(EvaluationContextImpl context) throws EvaluateException { |
| myEvaluatedField = null; |
| myEvaluatedQualifier = null; |
| Object object = myObjectEvaluator.evaluate(context); |
| |
| return evaluateField(object, context); |
| |
| } |
| |
| private Object evaluateField(Object object, EvaluationContextImpl context) throws EvaluateException { |
| if (object instanceof ReferenceType) { |
| ReferenceType refType = (ReferenceType)object; |
| Field field = findField(refType, context); |
| if (field == null || !field.isStatic()) { |
| field = refType.fieldByName(myFieldName); |
| } |
| if (field == null || !field.isStatic()) { |
| throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.static.field", myFieldName)); |
| } |
| myEvaluatedField = field; |
| myEvaluatedQualifier = refType; |
| return refType.getValue(field); |
| } |
| |
| if (object instanceof ObjectReference) { |
| ObjectReference objRef = (ObjectReference)object; |
| ReferenceType refType = objRef.referenceType(); |
| if (!(refType instanceof ClassType || refType instanceof ArrayType)) { |
| throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.class.or.array.expected", myFieldName)); |
| } |
| |
| // expressions like 'array.length' must be treated separately |
| //noinspection HardCodedStringLiteral |
| if (objRef instanceof ArrayReference && "length".equals(myFieldName)) { |
| //noinspection HardCodedStringLiteral |
| return DebuggerUtilsEx.createValue( |
| context.getDebugProcess().getVirtualMachineProxy(), |
| "int", |
| ((ArrayReference)objRef).length() |
| ); |
| } |
| |
| Field field = findField(refType, context); |
| if (field == null) { |
| field = refType.fieldByName(myFieldName); |
| } |
| |
| if (field == null) { |
| throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.no.instance.field", myFieldName)); |
| } |
| myEvaluatedQualifier = field.isStatic()? (Object)refType : (Object)objRef; |
| myEvaluatedField = field; |
| return field.isStatic()? refType.getValue(field) : objRef.getValue(field); |
| } |
| |
| if(object == null) { |
| throw EvaluateExceptionUtil.createEvaluateException(new NullPointerException()); |
| } |
| |
| throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.evaluating.field", myFieldName)); |
| } |
| |
| public Modifier getModifier() { |
| Modifier modifier = null; |
| if (myEvaluatedField != null && (myEvaluatedQualifier instanceof ClassType || myEvaluatedQualifier instanceof ObjectReference)) { |
| modifier = new Modifier() { |
| public boolean canInspect() { |
| return myEvaluatedQualifier instanceof ObjectReference; |
| } |
| |
| public boolean canSetValue() { |
| return true; |
| } |
| |
| public void setValue(Value value) throws ClassNotLoadedException, InvalidTypeException { |
| if (myEvaluatedQualifier instanceof ReferenceType) { |
| ClassType classType = (ClassType)myEvaluatedQualifier; |
| classType.setValue(myEvaluatedField, value); |
| } |
| else { |
| ObjectReference objRef = (ObjectReference)myEvaluatedQualifier; |
| objRef.setValue(myEvaluatedField, value); |
| } |
| } |
| |
| public Type getExpectedType() throws ClassNotLoadedException { |
| return myEvaluatedField.type(); |
| } |
| |
| public NodeDescriptorImpl getInspectItem(Project project) { |
| if(myEvaluatedQualifier instanceof ObjectReference) { |
| return new FieldDescriptorImpl(project, (ObjectReference)myEvaluatedQualifier, myEvaluatedField); |
| } else |
| return null; |
| } |
| }; |
| } |
| return modifier; |
| } |
| |
| private static final class FQNameClassFilter implements TargetClassFilter { |
| private final String myQName; |
| |
| private FQNameClassFilter(String qName) { |
| myQName = qName; |
| } |
| |
| public boolean acceptClass(final ReferenceType refType) { |
| return refType.name().equals(myQName); |
| } |
| } |
| |
| private static final class LocalClassFilter implements TargetClassFilter{ |
| private final String myLocalClassShortName; |
| |
| private LocalClassFilter(String localClassShortName) { |
| myLocalClassShortName = localClassShortName; |
| } |
| |
| public boolean acceptClass(final ReferenceType refType) { |
| final String name = refType.name(); |
| final int index = name.lastIndexOf(myLocalClassShortName); |
| if (index < 0) { |
| return false; |
| } |
| for (int idx = index - 1; idx >= 0; idx--) { |
| final char ch = name.charAt(idx); |
| if (ch == '$') { |
| return idx < (index - 1); |
| } |
| if (!Character.isDigit(ch)) { |
| return false; |
| } |
| } |
| return false; |
| } |
| } |
| } |