Hello
from the data step, I can call methods on Java classes with array arguments. A metod with this signature is fine: public void str(String args[]).
However, returning arrays seems to be impossible. I cannot get a call to a method with this signature to work: public String [] str(String args[]).
Should this be possible?
If so, how?
Thank you
Niels Jespersen
Unfortunately DATA step component object JavaObj does not have a built-in method for returning Java arrays to DATA step arrays.
Should it ? Maybe, but I speculate the demand for such is so low that it does not warrant the development time and support costs.
You can pass arrays of either String or Double into Java methods, but any changes made to those arrays in the method do not get reflected back to the DATA step array.
An adapter class is needed for the cases when you need an array in Java to be accessed in DATA step.
The JavaObj provides a bridge to hosted class methods via its JavaObj methods whose names are:
Fiddling around #1
This code shows an array passed to a Java method and its elements get changed therein. The changes do not get reflected back to the array in the calling DATA step.
proc groovy;
clear; * clean slate;
submit;
class Example {
public String hello() {
return "Hello from a groovy class";
}
public void log (String string) {
System.out.println("NOTE: Stdout of JVM says: " + string);
}
public void fill(String[] target) {
for (int i=1; i<=target.length; i++)
{
target[i-1] = i.toString();
}
System.out.println("NOTE: Stdout of JVM says: " +target);
}
void main() {
// a main is required to prevent groovy errors while
// allowing compilation and loading of the class into the JVM
}
}
endsubmit;
quit;
data _null_;
array number_strings [10] $3 ('abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu');
length s $200;
declare JavaObj j ('Example');
j.callStringMethod ('hello', s);
put s=;
j.callVoidMethod ('log', 'This is a message');
put 'NOTE: data step array before "fill": ' number_strings(*);
j.callVoidMethod ('fill', number_strings);
put 'NOTE: data step array after "fill": ' number_strings(*);
run;
Log
s=Hello from a groovy class
NOTE: data step array before "fill": abc def ghi jkl mno pqr stu
NOTE: data step array after "fill": abc def ghi jkl mno pqr stu
NOTE: Stdout of JVM says: This is a message
NOTE: Stdout of JVM says: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Fiddling around #2
Add an adapter class named StringArray to the mix. The Java array data lives in the adapter instance and the array elements are accessible through the adapter class methods get and set.
proc groovy;
clear; * clean slate;
submit;
class StringArray
{
private String[] values;
int getLength () { return (values == null) ? 0 : values.length }
void setLength (double dlength)
{
int length = (dlength >= 0) ? (int) dlength : 0;
values = new String[length];
}
String get (double dindex) // accesor method callable via JavaObj
{
int index = (int) dindex;
String result =
values == null
? ""
: index >= 0 && index < values.length && values[index] != null
? values[index]
: "";
return result;
}
void set (double dindex, String value) // accesor method callable via JavaObj
{
int index = (int) dindex;
if (values == null)
return ;
else
if (index >= 0 && index < values.length)
values[index] = value;
}
String toString() {
return values.toString();
}
void main() { }
}
class Example {
public void fill(StringArray target) {
for (int i=1; i<=target.getLength(); i++)
{
target.set(i-1, ((char)(i+64)).toString());
}
System.out.println("NOTE: Stdout of JVM. fill() says: " +target.toString());
}
void main() {
// a main is required to prevent groovy errors while
// allowing compilation and loading of the class into the JVM
}
}
endsubmit;
quit;
data _null_;
declare javaobj sa ('StringArray');
declare javaobj ex ('Example');
sa.callVoidMethod('setLength', 8);
ex.callVoidMethod('fill', sa);
length sa_string $200;
sa.callStringMethod('toString', sa_string);
put 'NOTE: ' sa_string=;
length _5th_element_value $20;
sa.callStringMethod('get', 4, _5th_element_value);
put 'NOTE: ' _5th_element_value=;
sa.callVoidMethod('set', 4, "It works!");
sa.callStringMethod('get', 4, _5th_element_value);
put 'NOTE: ' _5th_element_value=;
run;
Log
NOTE: sa_string=[A, B, C, D, E, F, G, H]
NOTE: _5th_element_value=E
NOTE: _5th_element_value=It works!
NOTE: Stdout of JVM. fill() says: [A, B, C, D, E, F, G, H]
Unfortunately DATA step component object JavaObj does not have a built-in method for returning Java arrays to DATA step arrays.
Should it ? Maybe, but I speculate the demand for such is so low that it does not warrant the development time and support costs.
You can pass arrays of either String or Double into Java methods, but any changes made to those arrays in the method do not get reflected back to the DATA step array.
An adapter class is needed for the cases when you need an array in Java to be accessed in DATA step.
The JavaObj provides a bridge to hosted class methods via its JavaObj methods whose names are:
Fiddling around #1
This code shows an array passed to a Java method and its elements get changed therein. The changes do not get reflected back to the array in the calling DATA step.
proc groovy;
clear; * clean slate;
submit;
class Example {
public String hello() {
return "Hello from a groovy class";
}
public void log (String string) {
System.out.println("NOTE: Stdout of JVM says: " + string);
}
public void fill(String[] target) {
for (int i=1; i<=target.length; i++)
{
target[i-1] = i.toString();
}
System.out.println("NOTE: Stdout of JVM says: " +target);
}
void main() {
// a main is required to prevent groovy errors while
// allowing compilation and loading of the class into the JVM
}
}
endsubmit;
quit;
data _null_;
array number_strings [10] $3 ('abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu');
length s $200;
declare JavaObj j ('Example');
j.callStringMethod ('hello', s);
put s=;
j.callVoidMethod ('log', 'This is a message');
put 'NOTE: data step array before "fill": ' number_strings(*);
j.callVoidMethod ('fill', number_strings);
put 'NOTE: data step array after "fill": ' number_strings(*);
run;
Log
s=Hello from a groovy class
NOTE: data step array before "fill": abc def ghi jkl mno pqr stu
NOTE: data step array after "fill": abc def ghi jkl mno pqr stu
NOTE: Stdout of JVM says: This is a message
NOTE: Stdout of JVM says: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Fiddling around #2
Add an adapter class named StringArray to the mix. The Java array data lives in the adapter instance and the array elements are accessible through the adapter class methods get and set.
proc groovy;
clear; * clean slate;
submit;
class StringArray
{
private String[] values;
int getLength () { return (values == null) ? 0 : values.length }
void setLength (double dlength)
{
int length = (dlength >= 0) ? (int) dlength : 0;
values = new String[length];
}
String get (double dindex) // accesor method callable via JavaObj
{
int index = (int) dindex;
String result =
values == null
? ""
: index >= 0 && index < values.length && values[index] != null
? values[index]
: "";
return result;
}
void set (double dindex, String value) // accesor method callable via JavaObj
{
int index = (int) dindex;
if (values == null)
return ;
else
if (index >= 0 && index < values.length)
values[index] = value;
}
String toString() {
return values.toString();
}
void main() { }
}
class Example {
public void fill(StringArray target) {
for (int i=1; i<=target.getLength(); i++)
{
target.set(i-1, ((char)(i+64)).toString());
}
System.out.println("NOTE: Stdout of JVM. fill() says: " +target.toString());
}
void main() {
// a main is required to prevent groovy errors while
// allowing compilation and loading of the class into the JVM
}
}
endsubmit;
quit;
data _null_;
declare javaobj sa ('StringArray');
declare javaobj ex ('Example');
sa.callVoidMethod('setLength', 8);
ex.callVoidMethod('fill', sa);
length sa_string $200;
sa.callStringMethod('toString', sa_string);
put 'NOTE: ' sa_string=;
length _5th_element_value $20;
sa.callStringMethod('get', 4, _5th_element_value);
put 'NOTE: ' _5th_element_value=;
sa.callVoidMethod('set', 4, "It works!");
sa.callStringMethod('get', 4, _5th_element_value);
put 'NOTE: ' _5th_element_value=;
run;
Log
NOTE: sa_string=[A, B, C, D, E, F, G, H]
NOTE: _5th_element_value=E
NOTE: _5th_element_value=It works!
NOTE: Stdout of JVM. fill() says: [A, B, C, D, E, F, G, H]
Thanks Niels.
I developed the concepts quite a while ago for the SUGI 30 conference paper "Java in SAS®:
JavaObj, a DATA Step Component Object" and demonstration code jDSGI
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.