diff --git a/java/src/processing/mode/java/debug/VariableNode.java b/java/src/processing/mode/java/debug/VariableNode.java index b8b2684471..7056854b2d 100644 --- a/java/src/processing/mode/java/debug/VariableNode.java +++ b/java/src/processing/mode/java/debug/VariableNode.java @@ -29,6 +29,9 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.StringJoiner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; @@ -52,13 +55,16 @@ public class VariableNode implements MutableTreeNode { public static final int TYPE_SHORT = 10; public static final int TYPE_VOID = 11; + private static final Pattern ARRAY_REGEX = Pattern.compile( + "^(?[^\\[]+)(?(\\[\\])*)(?(\\[\\d+\\])+)(?[^\\[]*)$" + ); + protected String type; protected String name; protected Value value; protected List children = new ArrayList<>(); protected MutableTreeNode parent; - /** * Construct a {@link VariableNode}. * @param name the name @@ -88,22 +94,21 @@ public Value getValue() { * @return a String representing the value. */ public String getStringValue() { - String str; - if (value != null) { - if (getType() == TYPE_OBJECT) { - str = "instance of " + type; - } else if (getType() == TYPE_ARRAY) { - //instance of int[5] (id=998) --> instance of int[5] - str = value.toString().substring(0, value.toString().lastIndexOf(" ")); - } else if (getType() == TYPE_STRING) { - str = ((StringReference) value).value(); // use original string value (without quotes) - } else { - str = value.toString(); - } + if (value == null) { + return "null"; + } + + int typeDescriptor = getType(); + if (typeDescriptor == TYPE_OBJECT) { + return "instance of " + type; + } else if (typeDescriptor == TYPE_ARRAY) { + return describeArray(value.toString()); + } else if (typeDescriptor == TYPE_STRING) { + // use original string value (without quotes) + return ((StringReference) value).value(); } else { - str = "null"; + return value.toString(); } - return str; } @@ -380,4 +385,27 @@ public int hashCode() { hash = 97 * hash + (this.value != null ? this.value.hashCode() : 0); return hash; } + + + /** + * Describe an array in a human friendly description. + * + * @see Issue #606 + * @param fullDescrition The full description of the array like "instance of + * int[5] (id=998)" or "instance of int[][5] (id=998)" + * @return Human-friendly description like "instance of int[5]" or + * "instance of int[5][]". + */ + private String describeArray(String fullDescription) { + Matcher matcher = ARRAY_REGEX.matcher(fullDescription); + StringJoiner joiner = new StringJoiner(""); + if (!matcher.matches()) { + return fullDescription; + } + + joiner.add(matcher.group("prefix")); // Type without brackets + joiner.add(matcher.group("bounded")); // Brackets with numbers + joiner.add(matcher.group("unbounded")); // Brackets without numbers + return joiner.toString(); + } } diff --git a/java/test/processing/mode/java/debug/VariableNodeTests.java b/java/test/processing/mode/java/debug/VariableNodeTests.java new file mode 100644 index 0000000000..427f902150 --- /dev/null +++ b/java/test/processing/mode/java/debug/VariableNodeTests.java @@ -0,0 +1,101 @@ +package processing.mode.java.debug; + +import com.sun.jdi.StringReference; +import com.sun.jdi.Value; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + + +public class VariableNodeTests { + + @Test + public void describeNull() { + VariableNode node = new VariableNode("test", "null", null); + Assert.assertEquals(node.getStringValue(), "null"); + } + + @Test + public void describeInt() { + Value value = buildMockValue("5"); + VariableNode node = new VariableNode("test", "int", value); + Assert.assertEquals(node.getStringValue(), "5"); + } + + @Test + public void describeFloat() { + Value value = buildMockValue("5.5"); + VariableNode node = new VariableNode("test", "float", value); + Assert.assertEquals(node.getStringValue(), "5.5"); + } + + @Test + public void describeObject() { + Value value = buildMockValue("5.5"); + VariableNode node = new VariableNode("test", "Other", value); + Assert.assertEquals(node.getStringValue(), "instance of Other"); + } + + @Test + public void describeString() { + Value value = buildMockString("testing"); + VariableNode node = new VariableNode("test", "java.lang.String", value); + Assert.assertEquals(node.getStringValue(), "testing"); + } + + @Test + public void describeSimpleArray() { + Value value = buildMockValue("instance of int[5] (id=998)"); + VariableNode node = new VariableNode("test", "int[]", value); + Assert.assertEquals(node.getStringValue(), "instance of int[5]"); + } + + @Test + public void describeNestedArraySingleDimensionUnknown() { + Value value = buildMockValue("instance of int[][5] (id=998)"); + VariableNode node = new VariableNode("test", "int[][]", value); + Assert.assertEquals(node.getStringValue(), "instance of int[5][]"); + } + + @Test + public void describeNestedArrayMultiDimensionUnknown() { + Value value = buildMockValue("instance of int[][][5] (id=998)"); + VariableNode node = new VariableNode("test", "int[][][]", value); + Assert.assertEquals(node.getStringValue(), "instance of int[5][][]"); + } + + @Test + public void describeNestedArrayMixed() { + Value value = buildMockValue("instance of int[][][5][7] (id=998)"); + VariableNode node = new VariableNode("test", "int[][][][]", value); + Assert.assertEquals(node.getStringValue(), "instance of int[5][7][][]"); + } + + @Test + public void describeArrayFailsafe() { + Value value = buildMockValue("instance of int[x][7] (id=98)"); + VariableNode node = new VariableNode("test", "int[][][][]", value); + Assert.assertEquals(node.getStringValue(), "instance of int[x][7] (id=98)"); + } + + @Test + public void describeArrayUnexpectedOrder() { + Value value = buildMockValue("instance of int[7][] (id=98)"); + VariableNode node = new VariableNode("test", "int[][][][]", value); + Assert.assertEquals(node.getStringValue(), "instance of int[7][] (id=98)"); + } + + private Value buildMockValue(String toStringValue) { + Value value = Mockito.mock(Value.class); + Mockito.when(value.toString()).thenReturn(toStringValue); + return value; + } + + private StringReference buildMockString(String innerValue) { + StringReference value = Mockito.mock(StringReference.class); + Mockito.when(value.value()).thenReturn(innerValue); + return value; + } + +}