# HG changeset patch
# User Andrew Janke <andrew@apjanke.net>
# Date 1531987778 14400
#      Thu Jul 19 04:09:38 2018 -0400
# Node ID b4ca545ba65236f968a5eab87d38067c1eb552e8
# Parent  7aaf091d39fc730e37e6fdc51158c28bcb7ee92f
Add Java org.octave.BoxingTester to help test Java boxing behavior

diff --git a/scripts/java/module.mk b/scripts/java/module.mk
--- a/scripts/java/module.mk
+++ b/scripts/java/module.mk
@@ -16,16 +16,17 @@ if AMCOND_HAVE_JAVA
 
 JAR_FILES += $(%canon_reldir%_JAR_FILES)
 endif
 
 org_octave_dir = org/octave
 
 if AMCOND_HAVE_JAVA
 JAVA_SRC = \
+  $(org_octave_dir)/BoxingTester.java \
   $(org_octave_dir)/ClassHelper.java \
   $(org_octave_dir)/Matrix.java \
   $(org_octave_dir)/OctClassLoader.java \
   $(org_octave_dir)/Octave.java \
   $(org_octave_dir)/OctaveReference.java
 endif
 
 JAVA_CLASSES = $(JAVA_SRC:.java=.class)
diff --git a/scripts/java/org/octave/BoxingTester.java b/scripts/java/org/octave/BoxingTester.java
new file mode 100644
--- /dev/null
+++ b/scripts/java/org/octave/BoxingTester.java
@@ -0,0 +1,342 @@
+package org.octave;
+
+import java.util.Objects;
+
+import static java.lang.System.out;
+
+/**
+ * A class with a variety of method signatures to support testing of Octave/Java value boxing
+ * behavior.
+ *
+ * This class defines overloaded methods with various permutations of argument types. This
+ * lets you call these methods from the Octave REPL and see how the Java boxing conversions
+ * behave with respect to those method signature permutations.
+ *
+ * There are lots of these method signatures defined. Not all of them actually have automatic
+ * boxing of Octave types to their Java types. Their presence here does *not* imply that there
+ * is a corresponding automatic boxing behavior in Octave. They are just here to demonstrate how
+ * boxing *does* happen in their presence.
+ *
+ * This will be a rather large class. Feel free to add new methods with new names to test out new
+ * signature permutations. In general, do *not* add new overloaded methods with the same names
+ * as existing methods, because that will change the permutations of the sets of overloaded
+ * methods that exist, changing the context for boxing behavior when calling those methods.
+ * Exceptions: Feel free to:
+ * <ul>
+ *   <li>add any new single-argument foo(...) method with any type of argument.</li>
+ * </ul>
+ */
+public class BoxingTester {
+
+    // Simple single-argument methods
+
+    public void foo(Object x) {
+        out.format("foo(Object): (%s)\n", x);
+    }
+
+    public void foo(String x) {
+        out.format("foo(String): (%s)\n", x);
+    }
+
+    public void foo(String[] x) {
+        out.format("foo(String[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(char[] x) {
+        out.format("foo(char[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(CharSequence x) {
+        out.format("foo(CharSequence): (%s)\n", x);
+    }
+
+    public void foo(StringBuilder x) {
+        out.format("foo(StringBuilder): (%s)\n", x);
+    }
+
+    public void foo(char x) {
+        out.format("foo(char): (%s)\n", x);
+    }
+
+    public void foo(double x) {
+        out.format("foo(double): (%s)\n", x);
+    }
+
+    public void foo(Double x) {
+        out.format("foo(Double): (%s)\n", x);
+    }
+
+    public void foo(double[] x) {
+        out.format("foo(double[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(Double[] x) {
+        out.format("foo(Double[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(int x) {
+        out.format("foo(int): (%s)\n", x);
+    }
+
+    public void foo(int[] x) {
+        out.format("foo(int[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(Integer x) {
+        out.format("foo(Integer): (%s)\n", x);
+    }
+
+    public void foo(Integer[] x) {
+        out.format("foo(Integer[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(long x) {
+        out.format("foo(long): (%s)\n", x);
+    }
+
+    public void foo(long[] x) {
+        out.format("foo(long[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(Long x) {
+        out.format("foo(Long): (%s)\n", x);
+    }
+
+    public void foo(Long[] x) {
+        out.format("foo(Long[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(short x) {
+        out.format("foo(short): (%s)\n", x);
+    }
+
+    public void foo(short[] x) {
+        out.format("foo(short[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(Short x) {
+        out.format("foo(Short): (%s)\n", x);
+    }
+
+    public void foo(Short[] x) {
+        out.format("foo(Short[]): (%s)\n", new Object[] {x});
+    }
+
+    public void foo(double[][] x) {
+        out.format("foo(double[][]): (%s)\n", new Object[] {x});
+    }
+
+    // Multi-argument methods
+
+    public void f1(Object arg1, Object arg2) {
+        out.format("f1(Object, Object): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(Object arg1, double arg2) {
+        out.format("f1(Object, double): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(double arg1, Object arg2) {
+        out.format("f1(double, Object): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(int arg1, Object arg2) {
+        out.format("f1(int, Object): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(Object arg1, int arg2) {
+        out.format("f1(Object, int): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(int arg1, Double arg2) {
+        out.format("f1(int, Double): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(Double arg1, int arg2) {
+        out.format("f1(Double, int): (%s, %s)\n", arg1, arg2);
+    }
+
+    public void f1(double[] arg1, Object arg2) {
+        out.format("f1(double[], Object): (%s, %s)\n", Objects.toString(arg1), arg2);
+    }
+
+    public void f1(Object arg1, double[] arg2) {
+        out.format("f1(Object, double[]): (%s, %s)\n", arg1, Objects.toString(arg2));
+    }
+
+    public void f1(double arg1, double[] arg2) {
+        out.format("f1(double, double[]): (%s, %s)\n", arg1, Objects.toString(arg2));
+    }
+
+    public void f1(double[] arg1, double arg2) {
+        out.format("f1(double[], double): (%s, %s)\n", Objects.toString(arg1), arg2);
+    }
+
+    public void f1(int arg1, double[] arg2) {
+        out.format("f1(int, double[]): (%s, %s)\n", arg1, Objects.toString(arg2));
+    }
+
+    public void f1(double[] arg1, int arg2) {
+        out.format("f1(double[], int): (%s, %s)\n", Objects.toString(arg1), arg2);
+    }
+
+    public void f1(Double arg1, double[] arg2) {
+        out.format("f1(Double, double[]): (%s, %s)\n", arg1, Objects.toString(arg2));
+    }
+
+    public void f1(double[] arg1, Double arg2) {
+        out.format("f1(double[], Double): (%s, %s)\n", Objects.toString(arg1), arg2);
+    }
+
+    // Simple getters of various types
+
+    // We use shared constants to prove that the original Java values are the same, and the only
+    // things happening to change them are type conversions and unboxing.
+    //
+    // In a typical Java class, these would all be static. But it's easier to access non-static
+    // member fields in Octave. And maybe we want to allow for modification of these values at
+    // some point.
+
+    public final String sampleString = "Hello, world!";
+    public final int sampleInt = 42;
+    public final Integer sampleIntObj= Integer.valueOf(sampleInt);
+    public final double sampleDouble = 12.345;
+    public final Double sampleDoubleObj = Double.valueOf(sampleDouble);
+    public final double[] sampleDoubleArray = new double[] { 1.23, 2.34, 3.45 };
+    public final double[][] sampleDouble2DArray = new double[][] {
+            new double[] { 1.23, 2.34, 3.45 },
+            new double[] { 2.34, 3.45, 4.56 },
+            new double[] { 3.45, 4.56, 5.67 }
+    };
+    public final double[][] sampleDouble2DRaggedArray = new double[][] {
+            new double[] { 1.23, 2.34, 3.45 },
+            new double[] { 2.34, 3.45, 4.56, 5.67 },
+            new double[] { 3.45, 4.56, 5.67, 6.78, 7.89 }
+    };
+
+    public String getStringAsString() {
+        return sampleString;
+    }
+
+    public Object getStringAsObject() {
+        return sampleString;
+    }
+
+    public CharSequence getStringAsCharSequence() {
+        return sampleString;
+    }
+
+    public int getInt() {
+        return sampleInt;
+    }
+
+    public Integer getIntObject() {
+        return sampleIntObj;
+    }
+
+    public double getDouble() {
+        return sampleDouble;
+    }
+
+    public Double getDoubleObj() {
+        return sampleDoubleObj;
+    }
+
+    public Object getDoubleObjAsObject() {
+        return sampleDoubleObj;
+    }
+
+    public double[] getDoubleArray() {
+        return sampleDoubleArray;
+    }
+
+    public Object getDoubleArrayAsObject() {
+        return sampleDoubleArray;
+    }
+
+    public double[][] getDouble2DArray() {
+        return sampleDouble2DArray;
+    }
+
+    public Object getDouble2DArrayAsObject() {
+        return sampleDouble2DArray;
+    }
+
+    public double[][] getDouble2DRaggedArray() {
+        return sampleDouble2DRaggedArray;
+    }
+
+    public Object getDouble2DRaggedArrayAsObject() {
+        return sampleDouble2DRaggedArray;
+    }
+
+    public Object getNullAsObject() {
+        return null;
+    }
+
+    public double[] getNullAsDoubleArray() {
+        return null;
+    }
+
+    public Double getNullAsDoubleObject() {
+        return null;
+    }
+
+    public int[] getNullAsIntArray() {
+        return null;
+    }
+
+    // Round-trip methods that take various inputs and return the same input value
+    //
+    // These let you see if the Octave boxing/unboxing process is breaking Java identity
+    // relationships, or if values are being muddled up.
+
+    public Object roundtripObject(Object x) {
+        return x;
+    }
+
+    public String roundtripString(String x) {
+        return x;
+    }
+
+    public double[] roundtripDoubleArray(double[] x) {
+        return x;
+    }
+
+    public char[] roundtripCharArray(char[] x) {
+        return x;
+    }
+
+    public Double roundtripDoubleObject(Double x) {
+        return x;
+    }
+
+    public Integer roundtripIntObject(Integer x) {
+        return x;
+    }
+
+    public Object roundtripDoubleArrayAsObject(double[] x) {
+        return x;
+    }
+
+    public Object roundtripStringAsObject(String x) {
+        return x;
+    }
+
+    public double roundtripDouble(double x) {
+        return x;
+    }
+
+    public int roundtripInt(int x) {
+        return x;
+    }
+
+    public char roundtripChar(char x) {
+        return x;
+    }
+
+    public Character roundtripCharObject(Character x) {
+        return x;
+    }
+
+}
