Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Importing a map in javax.scripting javascript environment

Tags:

java

rhino

I'm seeing some odd behavior in the javax.scripting map implementation.

The online examples show an example of adding to a list within the js environment:

ScriptEngineManager mgr = new ScriptEngineManager();
  ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
  List<String> namesList = new ArrayList<String>();
  namesList.add("Jill");
  namesList.add("Bob");
  namesList.add("Laureen");
  namesList.add("Ed");
  jsEngine.put("namesListKey", namesList);
  System.out.println("Executing in script environment...");
  try
  {
   jsEngine.eval("var names = namesListKey.toArray();" + "for(x in names) {" + "  println(names[x]);" + "}"
     + "namesListKey.add(\"Dana\");");
  } catch (ScriptException ex)
  {
   ex.printStackTrace();
  }
  System.out.println(namesList);

However, if you try to do something similar with a map, you see odd behavior. For one thing, if you try to iterate through the map keys, e.g.

 HashMap<String, Object> m = new HashMap<String, Object>();
 jsEngine.put("map", m);

There's no way to obtain the map keys - if you try to iterate through the keys, you get method names-

jsEngine.eval(" for (var k in m.keySet()){ println(k)};");

results in :

notifyAll
removeAll
containsAll
contains
empty
equals
...

In the js context you can address values in the map with m.get(key) but not with m[key], and if the key doesn't exist it throws an error. Can anyone shed some light on this behavior, or is it just broken? Thanks.

like image 851
Steve B. Avatar asked Aug 13 '10 17:08

Steve B.


People also ask

What are Javascript maps?

Map is a collection of elements where each element is stored as a Key, value pair. Map object can hold both objects and primitive values as either key or value. When we iterate over the map object it returns the key, value pair in the same order as inserted.


1 Answers

for..in in JavaScript is not the same thing as for..each in Java, even though they look similar. for..in in JavaScript iterates over the property names in an object. The method names are exposed to Rhino as properties on the native Java HashMap object, just as if you had the following JavaScript object:

{
notifyAll:function(){},
removeAll:function(){},
containsAll:function(){},
contains:function(){},
empty:function(){},
equals:function(){}
}

My recommendation is that you either convert the HashMap keyset to an array, using method Set.toArray, or you obtain an iterator using Set.iterator(). Here's a short Rhino script showing how you may accomplish this using the toArray method:

x=new java.util.HashMap();
x.put("foo","bar");
x.put("bat","bif");
x.put("barf","boo");

var keyArray = x.keySet().toArray();
for(var i=0, l = keyArray.length; i < l; i++){
    var key = keyArray[i]; 
    var value = x.get(key);
    print(value);
}

Which outputs:

bif
bar
boo

Here's how you can do the same thing using Set.iterator:

x=new java.util.HashMap();
x.put("foo","bar");
x.put("bat","bif");
x.put("barf","boo");

var keyIter = x.keySet().iterator();
while(keyIter.hasNext()){
    var key = keyIter.next() 
    var value = x.get(key);
    print(value);
}
like image 189
jbeard4 Avatar answered Sep 17 '22 21:09

jbeard4