Accessing JavaScript array through Java with graal.js - java

I am migrating a project from Nashorn to graal.js. The project utilizes a large amount of scripts (over 3,400) and there's a function on the Java side which invokes a method; the method returns a JavaScript list of objects.
function filterList(ob)
{
var list = [];
var arr = ob.toArray();
for(var i = 0; i < arr.length; i++)
{
if(global.isValid(arr[i]))
{
list.push(arr[i]);
}
}
return list;
}
This worked fine on Nashorn previously with the use of ScriptUtils. This code was written by the developer who worked on the project before I picked it up:
try {
Object p = iv.invokeFunction("filterList", this.getList());
if(p != null) {
List<MyObj> lObj = new ArrayList<>(((Map<String, MyObj>)(ScriptUtils.convert(p, Map.class))).values());
return lObj;
}
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
How can I access the array through Java with graal.js? I have tried using Value.asValue(p)as(MyObj[]) to no avail. I have also tried following the Nashorn migration guide where they suggest to cast the object to List or Map, to no avail either.
I'm aware of a solution where I would have to rewrite the script to just use new Java.type('java.util.ArrayList'); and return a List rather than an array - however there are thousands of scripts and rewriting all of them will be incredibly tedious.

you can use Value.getArraySize() and Value.getArrayElement(index):
Context context = Context.newBuilder("js").build();
Value result = context.eval("js", "var list=[1,2,'foo',true]; list;");
if (result.hasArrayElements()) {
for (int i=0;i<result.getArraySize();i++) {
System.out.println(result.getArrayElement(i));
}
}
You find the full JavaDoc of the Value class in https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Value.html
Best,
Christian

Related

How can I retain the data type in result.success() method in onMethodCall in Flutter Java?

I am trying to return a List<List<String>> back to my Flutter code from my MainActivity.java.
I created a
new MethodChannel(Objects.requireNonNull(getFlutterEngine()).getDartExecutor(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull MethodChannel.Result result) {
if (call.method.equals("getOTD")) {
result.success(listOfList);
}
}
}
to return the listOfList variable. However, whenever it always coverts the List<List<String>> to <Object> while returning the "success" value. How can I make the method return the value without changing the data type? If that is not possible, how can I convert the Object back to List<List<String>> in the dart part?
Here is my dart side of the code snippet:
void getOTD() async {
try {
otdList = await platform.invokeMethod("getOTD");
otdList = otdList.cast<List<List<String>>>();
print(otdList);
} catch (e) {
print(e);
}
}
I tried casting the variable, but it did not work. I want to perform many operations on that variable later, so I need the List<List<>String> format. I have declared otdList in my dart code as follows since I was receiving an Object:
List<Object?> otdList = [];
I think I found a solution for the problem. With help of #pskink it turns out all one needs is a nested for loop.
For example,
final dynamicList = await platform.invokeMethod("methodName") as List<dynamic>;
List<List<String>> listOfListString = [];
for (int i = 0; i < dynamicList.length; i++) {
List<String> subString = [];
for (int j = 0; j < dynamicList[i].length; j++) {
subString.add((dynamicList[i][j]).toString());
}
listOfListString.add(subString);
}
Then, use the listOfListString anywhere you want. It will be in a List<List> format.

Bulk replacement of char

I am having a domain object with over 90 attributes. In all these attributes there are ., which I want to replace with an empty string.
I could do now:
for (int i = 0; i < list.size(); i++) {
list.get(i).getProp1().replace(".", "");
list.get(i).getProp2().replace(".", "");
//yadadada ...
list.get(i).getProp90().replace(".", "");
}
However, that is extremely boring and takes a lot of code to write. Is there a way to do this much more elegantly and faster?
I appreciate your ideas
What about making a method updateProp (since that's what you need) in your Domain class like this:
public void updateProp(int index) {
prop[index] = prop[index].replace(".", "");
}
and then calling it in a loop
for (int i = 0; i < list.size(); i++) {
for(int j=0; j < prop.length; j++) { /*use a getter, instance or class
reference for obtaining prop.length*/
list.get(i).updateProp(j);
}
}
Storing the Prop in its own Object could also do the trick, but I guess it'll break more code than it will fix.
You have to use java reflection:
Method[] methods = DomainObject.class.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().startsWith("getProp")) {
String result = (String) method.invoke(object, new Object[0]);
result = result.replace(".", "");
Method setter = DomainObject.class.getMethod(method.getName()
.replace("get", "set"), String.class);
setter.invoke(object, result);
}
}
Also don't forget String is immutable, the replace method doesn't change it, the call simply returns the changed version.
You're suggesting that you have a Java class which has up to 90 properties with a consistently incorrectly formatted data field. This question is possibly an X-Y Problem
There is a reason the attributes are stored with a ., and a reason you need them without the .
By understanding these reasons better, and their context, a cleaner solution will present itself.
For example, is the removal of the . purely for display purposes? Perhaps consider a generic function at the point of output to strip the .'s
Or maybe it's some kind of behaviour side-effect during data capture, in which case this needs to be part of an input filter.
Or is this a one-off ETL routine? In which case perhaps it could be done on the data columns direct by some generic SQL or grep / sed script.
If you cannot modify your object (maybe is auto-generated or something) you can achieve your goal through reflection:
for(MyObject a: list){
Field[] fields = MyObject.class.getDeclaredFields();
for (Field f : fields) {
try {
if (f.get(a) instanceof String) {
String str = ((String) f.get(a)).replace(".", "");
f.set(a, str);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
If you don't want to modify the values of your objects you can create The Monster:
List<Map<String, String>> monster = new ArrayList<Map<String, String>>();
for(MyObject a: list){
Field[] fields = MyObject.class.getDeclaredFields();
Map<String, String> map = new HashMap<String, String>();
for (Field f : fields) {
try {
if (f.get(a) instanceof String) {
String str = ((String) f.get(a)).replace(".", "");
map.put(f.getName(), str);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
monster.add(map);
}
Now you have a beatiful list of maps for fields created through reflection. You can access to prop1 of first object via
String obj1prop1 = monster.get(0).get("prop1"); //Value of prop1 for first object in list.
I guess the best way for me would be to create a method in your Object (the one from your List) and in this method, you directly change all the properties as you did here. Than you call your method instead of all of those.
The treatment will be directly on your Object ;)
for (int i = 0; i < list.size(); i++) {
list.set(i,list.get(i).update());
}
in your Object :
public void update()
{
prop1 = prop1.replace(".","");
etc for all the properties
}
Or you could use something like : Field[] getFields() if your attribute are public.
But actually, I think you should do this replace when you are creating your object, so before you already add it in the list. It would be better.

Return value from method java

I have a program in java that I wrote to return a table of values. Later on as the functions of this program grew I found that I would like to access a variable within the method that isn't returned but I am not sure the best way to go about it. I know that you cannot return more than one value but how would I go about accessing this variable without a major overhaul?
here is a simplified version of my code:
public class Reader {
public String[][] fluidigmReader(String cllmp) throws IOException {
//read in a file
while ((inpt = br.readLine()) != null) {
if (!inpt.equals("Calls")) {
continue;
}
break;
}
br.readLine();
inpt = br.readLine();
//set up parse parse parameters and parse
prse = inpt.split(dlmcma, -1);
while ((inpt = br.readLine()) != null) {
buffed.add(inpt);
}
int lncnt = 0;
String tbl[][] = new String[buffed.size()][rssnps.size()];
for (int s = 0; s < buffed.size(); s++) {
prse = buffed.get(s).split(dlmcma);
//turns out I want this smpls ArrayList elsewhere
smpls.add(prse[1]);
//making the table to search through
for (int m = 0; m < prse.length; m++) {
tbl[lncnt][m] = prse[m];
}
lncnt++;
}
//but I return just the tbl here
return tbl;
}
Can anyone recommend a way to use smpls in another class without returning it? Is this perhaps when you use a get/set sort of setup?
Sorry if this seems like an obvious question, I am still new to the world of modular programming
Right now you have this tbl variable. Wrap it in a class and add the list to the class.
class TableWrapper {
// default accessing for illustrative purposes -
// setters and getters are a good idea
String[][] table;
List<String> samples;
TableWrapper(String[][] table, List<String> samples) {
this.table = table;
this.samples = samples;
}
}
Then refactor your method to return the wrapper object.
public TableWrapper fluidigmReader(String cllmp) throws IOException {
// your code here
String tbl[][] = new String[buffed.size()][rssnps.size()];
TableWrapper tw = new TableWrapper(tbl,smpls);
// more of your code
return tw;
}
Then later in your code where you were going
String[][] tbl = fluidigmReader(cllmp);
You instead go
TableWrapper tw = fluidigmReader(cllmp);
String[][] tbl = tw.table;
List<String> smpls = tw.samples;
If you had used a dedicated class for the return value (such as the TableWrapper mentioned in another answer), then you could add additional fields there.
That is the good thing about classes - they can be extended. But you cannot extend String[][] in Java.
You can set a field, instead of a local variable, which you can retrieve later with a getter. You want to avoid it unless it is needed, but in this case it is.
You can use class(Inside Reader class) variable for this. But make sure that it's read/write is synchronized

java heap analysis with oql: Count unique strings

Im doing a memory analysis of an existing java software. Is there a sql 'group by' equivalent in oql to see the count of objects with same values but different instances.
select count(*)
from java.lang.String s
group by s.toString()
I'd like to achieve a list of duplicated strings along with the number of duplicates. The purpose of this is to see the cases with large numbers so that they could be optimized using String.intern().
Example:
"foo" 100
"bar" 99
"lazy fox" 50
etc...
The following is based on the answer by Peter Dolberg and can be used in the VisualVM OQL Console:
var counts={};
var alreadyReturned={};
filter(
sort(
map(heap.objects("java.lang.String"),
function(heapString){
if( ! counts[heapString.toString()]){
counts[heapString.toString()] = 1;
} else {
counts[heapString.toString()] = counts[heapString.toString()] + 1;
}
return { string:heapString.toString(), count:counts[heapString.toString()]};
}),
'lhs.count < rhs.count'),
function(countObject) {
if( ! alreadyReturned[countObject.string]){
alreadyReturned[countObject.string] = true;
return true;
} else {
return false;
}
}
);
It starts by using a map() call over all String instances and for each String creating or updating an object in the counts array. Each object has a string and a count field.
The resulting array will contain one entry for each String instance, each having a count value one larger than the previous entry for the same String.
The result is then sorted on the count field and the result looks something like this:
{
count = 1028.0,
string = *null*
}
{
count = 1027.0,
string = *null*
}
{
count = 1026.0,
string = *null*
}
...
(in my test the String "*null*" was the most common).
The last step is to filter this using a function that returns true for the first occurrence of each String. It uses the alreadyReturned array to keep track of which Strings have already been included.
I would use Eclipse Memory Analyzer instead.
Sadly, there isn't an equivalent to "group by" in OQL. I'm assuming you're talking about the OQL that is used in jhat and VisualVM.
There is an alternative, though. If you use pure JavaScript syntax instead of the "select x from y" syntax then you have the full power of JavaScript to work with.
Even so, the alternative way of getting the information you're looking for isn't simple. For example, here's an OQL "query" that will perform the same task as your query:
var set={};
sum(map(heap.objects("java.lang.String"),function(heapString){
if(set[heapString.toString()]){
return 0;
}
else{
set[heapString.toString()]=true;
return 1;
}
}));
In this example a regular JavaScript object mimics a set (collection with no duplicates). As the the map function goes through each string, the set is used to determine if the string has already been seen. Duplicates don't count toward the total (return 0) but new strings do (return 1).
A far more efficient query:
var countByValue = {};
// Scroll the strings
heap.forEachObject(
function(strObject) {
var key = strObject.toString();
var count = countByValue[key];
countByValue[key] = count ? count + 1 : 1;
},
"java.lang.String",
false
);
// Transform the map into array
var mapEntries = [];
for (var i = 0, keys = Object.keys(countByValue), total = keys.length; i < total; i++) {
mapEntries.push({
count : countByValue[keys[i]],
string : keys[i]
});
}
// Sort the counts
sort(mapEntries, 'rhs.count - lhs.count');
Just post my solution and experience when doing similar issue for other references.
var counts = {};
var alreadyReturned = {};
top(
filter(
sort(
map(heap.objects("java.lang.ref.Finalizer"),
function (fobject) {
var className = classof(fobject.referent)
if (!counts[className]) {
counts[className] = 1;
} else {
counts[className] = counts[className] + 1;
}
return {string: className, count: counts[className]};
}),
'rhs.count-lhs.count'),
function (countObject) {
if (!alreadyReturned[countObject.string]) {
alreadyReturned[countObject.string] = true;
return true;
} else {
return false;
}
}),
"rhs.count > lhs.count", 10);
The previous code will output the top 10 classes used by java.lang.ref.Finalizer.
Tips:
1. The sort function by using function XXX is NOT working on my Mac OS.
2. The classof function can return the class of the referent. (I tried to use fobject.referent.toString() -> this returned a lot of org.netbeans.lib.profiler.heap.InstanceDump. This also wasted a lot of my time).
Method 1
You can select all the strings and then use the terminal to aggregate them.
Increase the oql limit in the visual vm config files
restart visual vm
oql to get all the strings
copy and paste them into vim
clean the data with vim macros so there's
sort | uniq -c to get the counts.
Method 2
Use a tool to dump all the fields object the class you're interested in ( https://github.com/josephmate/DumpHprofFields can do it )
Use bash to select the strings you're interested in
Use bash to aggregate

How does this normalize function work?

I was doing a Junit tutorial and I came across this normalize function that was being tested. It was defined like this:
public static String normalizeWord(String word) {
try {
int i;
Class<?> normalizerClass = Class.forName("java.text.Normalizer");
Class<?> normalizerFormClass = null;
Class<?>[] nestedClasses = normalizerClass.getDeclaredClasses();
for (i = 0; i < nestedClasses.length; i++) {
Class<?> nestedClass = nestedClasses[i];
if (nestedClass.getName().equals("java.text.Normalizer$Form")) {
normalizerFormClass = nestedClass;
}
}
assert normalizerFormClass.isEnum();
Method methodNormalize = normalizerClass.getDeclaredMethod(
"normalize",
CharSequence.class,
normalizerFormClass);
Object nfcNormalization = null;
Object[] constants = normalizerFormClass.getEnumConstants();
for (i = 0; i < constants.length; i++) {
Object constant = constants[i];
if (constant.toString().equals("NFC")) {
nfcNormalization = constant;
}
}
return (String) methodNormalize.invoke(null, word, nfcNormalization);
} catch (Exception ex) {
return null;
}
}
How does this function work? What is it actually doing?
It does the same as:
import java.text.Normalizer;
try {
return Normalizer.normalize(word, Normalizer.Form.NFC);
} catch (Exception ex) {
return null;
}
Except that all operations are performed via Reflection.
It's using reflection to call
java.text.Normalizer.normalize(word, java.text.Normalizer.Form.NFC);
Presumably to allow it to run on Java versions before 1.6 which don't have this class.
This function offers services regarding strings normalization for Unicode.
In Unicode, you can represent the same thing in many ways. For example, you have a character with accent. You can represent it joined, using one single Unicode character, or decomposed (the original letter, without accents, then the modifier - the accent).
The class comes in Java 6. For Java 5, there's a SUN proprietary class.
See class info.olteanu.utils.TextNormalizer in Phramer project (http://sourceforge.net/projects/phramer/ , www.phramer.org ) for a way to get a normalizer both in Java 5 (SUN JDK) and in Java 6, without any compilation issues (the code will compile in any version >= 5 and the code will run in both JVMs, although SUN discarded the Java 5 proprietary class).

Categories