4

In Javascript i have the following code:

var r=applet.foo({var0:99,var1:'foo',var2:applet});

In my Java applet i have the following:

public JSObject foo(JSObject args){
    System.out.println("The function is correctly invoked");
    //In fact, the following works perfectly:
    System.out.println("var1 is:"+(String)args.getMember("var1"));
    JSObject w=JSObject.getWindow(this);
    JSObject j=(JSObject)w.eval("new Object();");
    Map m=new Hashmap();

    //TODO here all the keys and values of args should be added to m

    m.put("hello","world");

    //TODO here all the keys and values of m should be added to j

    return j;
}

How can this be done? (TODOs)


Reading http://docstore.mik.ua/orelly/web/jscript/ch19_06.html, i noticed theres a getSlot method for JSObject but if i do

args.getSlot(0)

all i have is one Exception:

netscape.javascript.JSException: No such slot 0 on JavaScript object
...
user1504906
  • 41
  • 1
  • 2

3 Answers3

3

Unfortunately, Errandir's solution works only when you know a name of global variable that can be used to access an object you want to get properties' names of. You need to know this name to be able to add keys method to the object, and invoke it using JSObject's call method later. Of course, you can pass a global name of your object to Java if you have it. This solution doesn't look so good especially when you can't refer to your object in global context.

As an alternative, I proposed to use this of JSObject's eval method in the comment supposing that it will do all the work. And it does. But a big disappointent was that it works as expected only in Mozilla Firefox and Opera. In Internet Explorer 9 and Google Chrome (tested under Windows 7 and Ubuntu 12.04 LTS) this of eval method always refers to applet's document window ignoring which JavaScript object JSObject instance actually represents. I don't know whether it's a bug or simply LiveConnect is supported in these browsers very poorly.

The good news is that call method of JSObject executes specified function on the proper context. Keeping that in mind I finally found a solution how a list of names of JavaScript object's properties can be retrieved. The idea is to define a temporary function in global context using eval method. This function has to receive a JavaScript object we want to get properties of and to return names of these properties as an array. After that we can invoke the temporary function through JSObject's call method passing a Java representation of concerned JavaScript object (jsObject in my method below or args as it sounds in the question). At last, temporary function can be removed.

public static ArrayList<String> getJsObjectPropertiesNames(Applet applet, JSObject jsObject) {

    if (applet == null || jsObject == null)
        return null;

    // Retrieving global context - a JSObject representing a window applet belongs to
    JSObject globalContext;
    try {

        globalContext = JSObject.getWindow(applet);
    }
    catch (JSException ex) {

        return null;
    }

    // Checking whether passed object is not an array
    try {

        jsObject.getSlot(0);
        return null;
    }
    catch (JSException e) {

    }

    String keysFunctionName = String.format("_getKeys%d", Calendar.getInstance().getTimeInMillis());
    jsObject.eval("window['" + keysFunctionName + "'] = function(jsObject) { return Object.keys(jsObject) }");
    JSObject propertiesNamesJsObject = (JSObject)globalContext.call(keysFunctionName, new Object[] { jsObject });
    jsObject.eval("delete(window['" + keysFunctionName + "'])");

    ArrayList<String> propertiesNames = new ArrayList<>();
    try {

        int slotIndex = 0;
        while (true) {

            Object propertyName = propertiesNamesJsObject.getSlot(slotIndex);
            if (propertyName instanceof String)
                propertiesNames.add((String)propertyName);
            slotIndex++;
        }
    }
    catch (JSException e) {

    }

    return propertiesNames;
}
ezze
  • 3,933
  • 2
  • 34
  • 58
1

As a solution, you could define method keys as proposed here (You can do it within your java-code using JSObject.eval(...)). Then you could get keys like:

JSObject keys = (JSObject)args.call("keys", Collections.EMPTY_LIST);
keys.getSlot(0);
Community
  • 1
  • 1
Timofey Gorshkov
  • 4,987
  • 6
  • 41
  • 66
  • 1
    In order to get keys there is no need to define `keys` method. It's enough to use `eval` method since it executes a JavaScript code in context of `JSObject`: `JSObject keys = (JSObject)args.eval("Object.keys(this)");` – ezze Dec 19 '12 at 18:25
-1

Here below I print a String, please modify it to get whatever you need.

public final static String getKeys = "{var keys = [];for (var key in this) {keys.push(key);} keys;}";

private static String printProperties(final Object o,
                                      final boolean printType,
                                      final int level,
                                      final String tab) {
    final StringBuilder sb = new StringBuilder(100);
    if (printType) {
        sb.append("(");
        sb.append(o.getClass().getSimpleName());
        sb.append(") ");
    }
    if (o instanceof JSObject) {
        sb.append("{\n");
        final JSObject js = (JSObject) o;
        final JSObject keys = (JSObject) js.eval(getKeys);
        boolean needComma = false;
        for (int i = 0;; i++) {
            final String key = (String) keys.getSlot(i);
            if ((key != null) && !(key.equals("undefined"))) {
                final Object val = js.getMember(key);
                if (!needComma) {
                    needComma = true;
                } else {
                    sb.append(",\n");
                }
                sb.append(multitab(tab, level));
                sb.append(key);
                sb.append(":");
                sb.append(printProperties(val, printType, level + 1, tab));
            } else {
                break;
            }
        }
        sb.append("\n");
        sb.append(multitab(tab, level - 1));
        sb.append("}");
    } else {
        sb.append(o);
    }
    return sb.toString();
}

private final static String tab = "  ";

private static String multitab(final String tab,
                               int i) {
    final StringBuilder sb = new StringBuilder();
    while (i-- > 0) {
        sb.append(tab);
    }
    return sb.toString();
}
John
  • 7
  • 4