Skip to content
2 changes: 1 addition & 1 deletion build/shared/lib/languages/PDE.properties
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ editor.status.hiding_enclosing_type = The class “%s” cannot have the same na

editor.status.bad.assignment = Possible error on variable assignment near ‘%s’?
editor.status.bad.generic = Possibly missing type in generic near ‘%s’?
editor.status.bad.identifier = Bad identifier? Did you forget a variable or start an identifier with digits near ‘%s’?
editor.status.bad.identifier = There's an issue with a "bad identifier" in your code near '%s'.
editor.status.bad.parameter = Error on parameter or method declaration near ‘%s’?
editor.status.bad.import = Import not allowed here.
editor.status.bad.mixed_mode = You may be mixing active and static modes.
Expand Down
58 changes: 43 additions & 15 deletions java/src/processing/mode/java/debug/VariableNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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(
"^(?<prefix>[^\\[]+)(?<unbounded>(\\[\\])*)(?<bounded>(\\[\\d+\\])+)(?<unneeded>[^\\[]*)$"
);

protected String type;
protected String name;
protected Value value;
protected List<MutableTreeNode> children = new ArrayList<>();
protected MutableTreeNode parent;


/**
* Construct a {@link VariableNode}.
* @param name the name
Expand Down Expand Up @@ -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;
}


Expand Down Expand Up @@ -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();
}
}
34 changes: 30 additions & 4 deletions java/src/processing/mode/java/tweak/Handle.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,26 @@ public Handle(String t, String n, int vi, String v, int ti, int l, int sc,
textFormat = "0x%x";

} else if ("webcolor".equals(type)) {
Long val = Long.parseLong(strValue.substring(1, strValue.length()), 16);
Long val;
String prefix;
if (strValue.length() == 7) {
val = Long.parseLong(strValue.substring(1, strValue.length()), 16);
prefix = "";
} else {
String valStr = strValue.substring(
strValue.length() - 6,
strValue.length()
);
val = Long.parseLong(valStr, 16);
prefix = strValue.substring(
1,
strValue.length() - 6
);
}
val = val | 0xff000000;
value = newValue = val.intValue();
strNewValue = strValue;
textFormat = "#%06x";

textFormat = "#" + prefix + "%06x";
} else if ("float".equals(type)) {
value = newValue = Float.parseFloat(strValue);
strNewValue = strValue;
Expand Down Expand Up @@ -267,7 +281,19 @@ public void sendNewValue() {
} else if ("hex".equals(type)) {
tweakClient.sendInt(index, newValue.intValue());
} else if ("webcolor".equals(type)) {
tweakClient.sendInt(index, newValue.intValue());
// If full opaque color, don't spend the cycles on string processing
// which does appear to matter at high frame rates. Otherwise take the
// hit and parse back from string value with transparency.
if (strNewValue.length() == 7) {
tweakClient.sendInt(index, newValue.intValue());
} else {
long target = Long.parseLong(
strNewValue.substring(1, strNewValue.length()),
16
);
tweakClient.sendInt(index, (int) target);
}

} else if ("float".equals(type)) {
tweakClient.sendFloat(index, newValue.floatValue());
}
Expand Down
2 changes: 1 addition & 1 deletion java/src/processing/mode/java/tweak/SketchParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private void addAllHexNumbers() {
* list of all hexadecimal numbers in the sketch
*/
private void addAllWebColorNumbers() {
Pattern p = Pattern.compile("#[A-Fa-f0-9]{6}");
Pattern p = Pattern.compile("#([A-Fa-f0-9]{2})?[A-Fa-f0-9]{6}");
for (int i=0; i<codeTabs.length; i++)
{
String c = codeTabs[i];
Expand Down
101 changes: 101 additions & 0 deletions java/test/processing/mode/java/debug/VariableNodeTests.java
Original file line number Diff line number Diff line change
@@ -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;
}

}