| /******************************************************************************* |
| * Copyright (c) 2007, 2013 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stepan Vavra - Bug 419316 - All References or All instances may throw NPE in Eclipse |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.core.model; |
| |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.model.IIndexedValue; |
| import org.eclipse.debug.core.model.IVariable; |
| import org.eclipse.jdt.debug.core.IJavaObject; |
| import org.eclipse.jdt.debug.core.IJavaType; |
| import org.eclipse.jdt.debug.core.IJavaVariable; |
| import org.eclipse.jdt.internal.debug.core.HeapWalkingManager; |
| import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; |
| import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIPlaceholderVariable; |
| |
| import com.ibm.icu.text.MessageFormat; |
| import com.sun.jdi.ObjectReference; |
| |
| /** |
| * A JDI Value representing a set of references to the root object specified in |
| * the constructor. Used to add a list of all references to an object to various |
| * views including the variables view. The value should belong to a |
| * <code>JDIReferenceListVariable</code>. The children of this value will be |
| * <code>JDIReferenceListEntryVariable</code>, each representing one reference |
| * to the root object. |
| * |
| * @see JDIReferenceListVariable |
| * @see JDIReferenceListEntryVariable |
| * @since 3.3 |
| */ |
| public class JDIReferenceListValue extends JDIObjectValue implements |
| IIndexedValue { |
| |
| private IJavaObject fRoot; |
| private boolean fIsMoreThanPreference; |
| private IJavaType fType = null; |
| |
| /** |
| * Constructor, initializes this value with its debug target and root object |
| * |
| * @param target |
| * The debug target associated with this value |
| * @param root |
| * The root object that the elements in the array refer to. |
| */ |
| public JDIReferenceListValue(IJavaObject root) { |
| super((JDIDebugTarget) root.getDebugTarget(), ((JDIObjectValue) root) |
| .getUnderlyingObject()); |
| fRoot = root; |
| try { |
| IJavaType[] javaTypes = ((JDIDebugTarget) root.getDebugTarget()) |
| .getJavaTypes("java.lang.Object[]"); //$NON-NLS-1$ |
| if (javaTypes != null && javaTypes.length > 0) { |
| fType = javaTypes[0]; |
| } |
| } catch (DebugException e) { |
| } |
| } |
| |
| /** |
| * @return all references to the root object as an array of IJavaObjects |
| */ |
| protected synchronized IJavaObject[] getReferences() { |
| try { |
| int max = HeapWalkingManager.getDefault() |
| .getAllReferencesMaxCount(); |
| IJavaObject[] referringObjects = null; |
| fIsMoreThanPreference = false; |
| if (max == 0) { |
| referringObjects = fRoot.getReferringObjects(max); |
| } else { |
| referringObjects = fRoot.getReferringObjects(max + 1); |
| if (referringObjects.length > max) { |
| fIsMoreThanPreference = true; |
| referringObjects[max] = new JDIPlaceholderValue( |
| (JDIDebugTarget) fRoot.getDebugTarget(), |
| MessageFormat |
| .format(JDIDebugModelMessages.JDIReferenceListValue_9, |
| Integer.toString(max))); |
| } |
| } |
| return referringObjects; |
| } catch (DebugException e) { |
| JDIDebugPlugin.log(e); |
| return new IJavaObject[0]; |
| } |
| } |
| |
| /** |
| * @return whether the references to the root object have been loaded from |
| * the vm yet. |
| */ |
| protected synchronized boolean referencesLoaded() { |
| if (fRoot instanceof JDIObjectValue) { |
| return ((JDIObjectValue) fRoot).isReferencesLoaded(); |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getVariables() |
| */ |
| @Override |
| public IVariable[] getVariables() throws DebugException { |
| IJavaObject[] elements = getReferences(); |
| IVariable[] vars = new JDIPlaceholderVariable[elements.length]; |
| int length = elements.length; |
| if (fIsMoreThanPreference) { |
| length--; |
| vars[length] = new JDIPlaceholderVariable( |
| JDIDebugModelMessages.JDIReferenceListValue_11, |
| elements[length]); |
| } |
| |
| for (int i = 0; i < length; i++) { |
| vars[i] = new JDIReferenceListEntryVariable(MessageFormat.format( |
| JDIDebugModelMessages.JDIReferenceListValue_0, |
| Integer.toString(i)), elements[i]); |
| } |
| return vars; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.internal.debug.core.model.JDIObjectValue#getUnderlyingObject |
| * () |
| */ |
| @Override |
| public ObjectReference getUnderlyingObject() { |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#hasVariables() |
| */ |
| @Override |
| public boolean hasVariables() throws DebugException { |
| if (referencesLoaded()) { |
| return getReferences().length > 0; |
| } |
| return true; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#isAllocated() |
| */ |
| @Override |
| public boolean isAllocated() throws DebugException { |
| return fRoot.isAllocated(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getJavaType() |
| */ |
| @Override |
| public IJavaType getJavaType() throws DebugException { |
| return fType; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getSignature() |
| */ |
| @Override |
| public String getSignature() throws DebugException { |
| return "[Ljava/lang/Object;"; //$NON-NLS-1$ |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.internal.debug.core.model.JDIObjectValue#getReferenceTypeName |
| * () |
| */ |
| @Override |
| public String getReferenceTypeName() throws DebugException { |
| return "java.lang.Object[]"; //$NON-NLS-1$ |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getValueString() |
| */ |
| @Override |
| public String getValueString() throws DebugException { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns a string representation of this value intended to be displayed in |
| * the detail pane of views. Lists the references on separate lines. |
| * |
| * @return a string representation of this value to display in the detail |
| * pane |
| */ |
| public String getDetailString() { |
| StringBuilder buf = new StringBuilder(); |
| Object[] elements = getReferences(); |
| if (elements.length == 0) { |
| buf.append(JDIDebugModelMessages.JDIReferenceListValue_2); |
| } else { |
| String length = null; |
| if (fIsMoreThanPreference) { |
| length = MessageFormat.format( |
| JDIDebugModelMessages.JDIReferenceListValue_15, |
| Integer.toString(elements.length - 1)); |
| } else { |
| length = Integer.toString(elements.length); |
| } |
| if (elements.length == 1) { |
| buf.append(MessageFormat.format( |
| JDIDebugModelMessages.JDIReferenceListValue_3, |
| length)); |
| } else { |
| buf.append(MessageFormat.format( |
| JDIDebugModelMessages.JDIReferenceListValue_4, |
| length)); |
| } |
| for (Object element : elements) { |
| buf.append(element + "\n"); //$NON-NLS-1$ |
| } |
| } |
| return buf.toString(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#toString() |
| */ |
| @Override |
| public String toString() { |
| return MessageFormat.format( |
| JDIDebugModelMessages.JDIReferenceListValue_6, |
| getUnderlyingValue().toString()); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.internal.debug.core.model.JDIValue#equals(java.lang.Object |
| * ) |
| */ |
| @Override |
| public boolean equals(Object o) { |
| // Two JDIReferenceListValues are equal if they both have the same root |
| // object. |
| if (o instanceof JDIReferenceListValue) { |
| JDIReferenceListValue ref = (JDIReferenceListValue) o; |
| return ref.fRoot.equals(fRoot); |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| return getClass().hashCode() + fRoot.hashCode(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.debug.core.model.IIndexedValue#getInitialOffset() |
| */ |
| @Override |
| public int getInitialOffset() { |
| return 0; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.debug.core.model.IIndexedValue#getSize() |
| */ |
| @Override |
| public int getSize() throws DebugException { |
| return getVariables().length; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.debug.core.model.IIndexedValue#getVariable(int) |
| */ |
| @Override |
| public IVariable getVariable(int offset) throws DebugException { |
| IVariable[] variables = getVariables(); |
| if (offset < variables.length) { |
| return variables[offset]; |
| } |
| requestFailed(JDIDebugModelMessages.JDIReferenceListValue_7, new IndexOutOfBoundsException()); |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.debug.core.model.IIndexedValue#getVariables(int, int) |
| */ |
| @Override |
| public IVariable[] getVariables(int offset, int length) |
| throws DebugException { |
| IVariable[] variables = getVariables(); |
| if (offset < variables.length && (offset + length) <= variables.length) { |
| IJavaVariable[] vars = new IJavaVariable[length]; |
| System.arraycopy(variables, offset, vars, 0, length); |
| return vars; |
| } |
| requestFailed(JDIDebugModelMessages.JDIReferenceListValue_8, new IndexOutOfBoundsException()); |
| return null; |
| } |
| |
| } |