/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.debug.core.model; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.jdt.debug.core.IJavaType; import org.eclipse.jdt.debug.core.IJavaValue; import org.eclipse.jdt.debug.core.IJavaVariable; import com.ibm.icu.text.MessageFormat; import com.sun.jdi.ArrayReference; import com.sun.jdi.ClassObjectReference; import com.sun.jdi.Field; import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ObjectReference; import com.sun.jdi.PrimitiveValue; import com.sun.jdi.ReferenceType; import com.sun.jdi.StringReference; import com.sun.jdi.Type; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.Value; /** * Represents the value of a java variable * * @see IJavaValue */ public class JDIValue extends JDIDebugElement implements IJavaValue { private Value fValue; private List<IJavaVariable> fVariables; /** * A flag indicating if this value is still allocated (valid) */ private boolean fAllocated = true; /** * When created for a logical structure we hold onto the original * non-logical value for purposes of equality. This way a logical * structure's children remain more stable in the variables view. * * This is <code>null</code> when not created for a logical structure. */ protected IJavaValue fLogicalParent; /** * Constructor * * @param target * debug target that this value belongs to * @param value * the underlying value this value represents */ public JDIValue(JDIDebugTarget target, Value value) { super(target); fValue = value; } /* * (non-Javadoc) * * @see * org.eclipse.jdt.internal.debug.core.model.JDIDebugElement#getAdapter( * java.lang.Class) */ @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { if (adapter == IJavaValue.class) { return (T) this; } return super.getAdapter(adapter); } /** * Creates the appropriate kind of value - i.e. a primitive value, object, * class object, array, or <code>null</code>. */ public static JDIValue createValue(JDIDebugTarget target, Value value) { if (value == null) { return new JDINullValue(target); } if (value instanceof ArrayReference) { return new JDIArrayValue(target, (ArrayReference) value); } if (value instanceof ClassObjectReference) { return new JDIClassObjectValue(target, (ClassObjectReference) value); } if (value instanceof ObjectReference) { return new JDIObjectValue(target, (ObjectReference) value); } if (value instanceof PrimitiveValue) { return new JDIPrimitiveValue(target, value); } return new JDIValue(target, value); } /** * @see IValue#getValueString() */ @Override public String getValueString() throws DebugException { if (fValue == null) { return JDIDebugModelMessages.JDIValue_null_4; } if (fValue instanceof StringReference) { try { return ((StringReference) fValue).value(); } catch (ObjectCollectedException e) { return JDIDebugModelMessages.JDIValue_deallocated; } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_value, new Object[] { e.toString() }), e); // execution will not reach this line, as // #targetRequestFailed will thrown an exception return null; } } if (fValue instanceof ObjectReference) { StringBuffer name = new StringBuffer(); if (fValue instanceof ClassObjectReference) { name.append('('); name.append(((ClassObjectReference) fValue).reflectedType()); name.append(')'); } long id = 0; try { id = ((ObjectReference) fValue).uniqueID(); } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_unique_id, new Object[] { e.toString() }), e); // execution will not reach this line, as // #targetRequestFailed will thrown an exception return null; } name.append(" "); //$NON-NLS-1$ name.append(MessageFormat.format( JDIDebugModelMessages.JDIValue_id_8, new Object[] { String.valueOf(id) })); return name.toString(); } // see bug 43285 return String.valueOf(fValue); } /** * @see IValue#getReferenceTypeName() */ @Override public String getReferenceTypeName() throws DebugException { try { if (fValue == null) { return JDIDebugModelMessages.JDIValue_null_4; } return getUnderlyingType().name(); } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_reference_type_name, new Object[] { e.toString() }), e); // execution will not reach this line, as // #targetRequestFailed will thrown an exception return null; } } /** * @see Object#hashCode() */ @Override public int hashCode() { if (fValue == null) { return getClass().hashCode(); } return fValue.hashCode(); } /** * @see Object#equals(Object) */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JDIValue) { Value other = ((JDIValue) o).getUnderlyingValue(); if (fValue == null) { return false; } if (other == null) { return false; } return fValue.equals(other); } return false; } /** * @see IValue#getVariables() */ @Override public IVariable[] getVariables() throws DebugException { List<IJavaVariable> list = getVariablesList(); return list.toArray(new IVariable[list.size()]); } /** * Returns a list of variables that are children of this value. The result * is cached. * * @return list of variable children * @throws DebugException */ protected synchronized List<IJavaVariable> getVariablesList() throws DebugException { if (fVariables != null) { return fVariables; } else if (fValue instanceof ObjectReference) { ObjectReference object = (ObjectReference) fValue; fVariables = new ArrayList<>(); if (isArray()) { try { int length = getArrayLength(); for (int i = 0; i < length; i++) { fVariables.add(new JDIArrayEntryVariable( getJavaDebugTarget(), getArrayReference(), i, fLogicalParent)); } } catch (DebugException e) { if (e.getCause() instanceof ObjectCollectedException) { return Collections.EMPTY_LIST; } throw e; } } else { List<Field> fields = null; try { ReferenceType refType = object.referenceType(); fields = refType.allFields(); } catch (ObjectCollectedException e) { return Collections.EMPTY_LIST; } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_fields, new Object[] { e.toString() }), e); // execution will not reach this line, as // #targetRequestFailed will thrown an exception return null; } Iterator<Field> list = fields.iterator(); while (list.hasNext()) { Field field = list.next(); fVariables.add(new JDIFieldVariable( (JDIDebugTarget) getDebugTarget(), field, object, fLogicalParent)); } Collections.sort(fVariables, new Comparator<IJavaVariable>() { @Override public int compare(IJavaVariable a, IJavaVariable b) { return sortChildren(a, b); } }); } return fVariables; } else { return Collections.EMPTY_LIST; } } /** * Group statics and instance variables, sort alphabetically within each * group. */ protected int sortChildren(Object a, Object b) { IJavaVariable v1 = (IJavaVariable) a; IJavaVariable v2 = (IJavaVariable) b; try { boolean v1isStatic = v1.isStatic(); boolean v2isStatic = v2.isStatic(); if (v1isStatic && !v2isStatic) { return -1; } if (!v1isStatic && v2isStatic) { return 1; } return v1.getName().compareToIgnoreCase(v2.getName()); } catch (DebugException de) { logError(de); return -1; } } /** * Returns whether this value is an array */ protected boolean isArray() { return fValue instanceof ArrayReference; } /** * Returns this value as an array reference, or <code>null</code> */ protected ArrayReference getArrayReference() { if (isArray()) { return (ArrayReference) fValue; } return null; } /** * @see IValue#isAllocated() */ @Override public boolean isAllocated() throws DebugException { if (fAllocated) { if (fValue instanceof ObjectReference) { try { fAllocated = !((ObjectReference) fValue).isCollected(); } catch (VMDisconnectedException e) { // if the VM disconnects, this value is not allocated fAllocated = false; } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_is_collected, new Object[] { e.toString() }), e); // execution will fall through, as // #targetRequestFailed will thrown an exception } } else { JDIDebugTarget dt = (JDIDebugTarget) getDebugTarget(); fAllocated = dt.isAvailable(); } } return fAllocated; } /** * @see IJavaValue#getSignature() */ @Override public String getSignature() throws DebugException { try { if (fValue != null) { return fValue.type().signature(); } return null; } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_type_signature, new Object[] { e.toString() }), e); // execution will not reach this line, as // #targetRequestFailed will thrown an exception return null; } } /* * (non-Javadoc) * * @see org.eclipse.jdt.debug.core.IJavaValue#getGenericSignature() */ @Override public String getGenericSignature() throws DebugException { try { if (fValue != null) { Type type = fValue.type(); if (type instanceof ReferenceType) { return ((ReferenceType) type).genericSignature(); } return null; } return null; } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_type_signature, new Object[] { e.toString() }), e); // execution will not reach this line, as // #targetRequestFailed will thrown an exception return null; } } /** * @see IJavaValue#getArrayLength() */ public int getArrayLength() throws DebugException { if (isArray()) { try { return getArrayReference().length(); } catch (RuntimeException e) { targetRequestFailed( MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_length_of_array, new Object[] { e.toString() }), e); } } return -1; } /** * Returns this value's underlying JDI value */ protected Value getUnderlyingValue() { return fValue; } /** * @see IJavaValue#getJavaType() */ @Override public IJavaType getJavaType() throws DebugException { return JDIType.createType((JDIDebugTarget) getDebugTarget(), getUnderlyingType()); } /** * Returns this value's underlying type. * * @return type * @exception DebugException * if this method fails. Reasons include: * <ul> * <li>Failure communicating with the VM. The * DebugException's status code contains the underlying * exception responsible for the failure.</li> */ protected Type getUnderlyingType() throws DebugException { try { return getUnderlyingValue().type(); } catch (RuntimeException e) { targetRequestFailed(MessageFormat.format( JDIDebugModelMessages.JDIValue_exception_retrieving_type, new Object[] { e.toString() }), e); // execution will not fall through to here, // as #requestFailed will throw an exception return null; } } /** * @see java.lang.Object#toString() */ @Override public String toString() { return getUnderlyingValue().toString(); } /** * @see IValue#hasVariables() */ @Override public boolean hasVariables() throws DebugException { return getVariablesList().size() > 0; } /** * Sets the value that is the original non-logical value that this child * value was computed for. * * @param logicalParent * parent value */ public void setLogicalParent(IJavaValue logicalParent) { fLogicalParent = logicalParent; } /** * Returns the value that is the original non-logical value that this child * value was computed for or <code>null</code> if none * * @param logicalParent * parent value or <code>null</code> */ public IJavaValue getLogicalParent() { return fLogicalParent; } /* * (non-Javadoc) * * @see org.eclipse.jdt.debug.core.IJavaValue#isNull() */ @Override public boolean isNull() { return false; } }