launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 09 Jan 2014 21:21:34 +0100
branchNbHtml4J
changeset 1429 66358a11c016
parent 1423 237dbcd482dc
child 1676 87f66a77adf9
permissions -rw-r--r--
Need to implement FromJavaScript interface
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.launcher.fximpl;
    19 
    20 import java.io.BufferedReader;
    21 import java.io.Reader;
    22 import java.net.URL;
    23 import java.util.ArrayList;
    24 import java.util.Arrays;
    25 import java.util.Collection;
    26 import java.util.List;
    27 import java.util.TooManyListenersException;
    28 import java.util.concurrent.Executor;
    29 import java.util.logging.Level;
    30 import java.util.logging.Logger;
    31 import javafx.application.Platform;
    32 import javafx.beans.value.ChangeListener;
    33 import javafx.scene.web.WebEngine;
    34 import netscape.javascript.JSObject;
    35 import org.apidesign.html.boot.spi.Fn;
    36 import org.netbeans.html.boot.impl.FindResources;
    37 import org.netbeans.html.boot.impl.FnUtils;
    38 
    39 /**
    40  *
    41  * @author Jaroslav Tulach <jtulach@netbeans.org>
    42  */
    43 public final class JVMBridge {
    44     static final Logger LOG = Logger.getLogger(JVMBridge.class.getName());
    45     
    46     private final WebEngine engine;
    47     private final ClassLoader cl;
    48     private final WebPresenter presenter;
    49     
    50     private static ClassLoader[] ldrs;
    51     private static ChangeListener<Void> onBck2BrwsrLoad;
    52     
    53     JVMBridge(WebEngine eng) {
    54         this.engine = eng;
    55         final ClassLoader p = JVMBridge.class.getClassLoader().getParent();
    56         this.presenter = new WebPresenter();
    57         this.cl = FnUtils.newLoader(presenter, presenter, p);
    58     }
    59         
    60     public static void registerClassLoaders(ClassLoader[] loaders) {
    61         ldrs = loaders.clone();
    62     }
    63     
    64     public static void addBck2BrwsrLoad(ChangeListener<Void> l) throws TooManyListenersException {
    65         if (onBck2BrwsrLoad != null) {
    66             throw new TooManyListenersException();
    67         }
    68         onBck2BrwsrLoad = l;
    69     }
    70 
    71     public static void onBck2BrwsrLoad() {
    72         ChangeListener<Void> l = onBck2BrwsrLoad;
    73         if (l != null) {
    74             l.changed(null, null, null);
    75         }
    76     }
    77     
    78     public Class<?> loadClass(String name) throws ClassNotFoundException {
    79         Fn.activate(presenter);
    80         return Class.forName(name, true, cl);
    81     }
    82     
    83     private final class WebPresenter 
    84     implements FindResources, Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor {
    85         @Override
    86         public void findResources(String name, Collection<? super URL> results, boolean oneIsEnough) {
    87             if (ldrs != null) for (ClassLoader l : ldrs) {
    88                 URL u = l.getResource(name);
    89                 if (u != null) {
    90                     results.add(u);
    91                 }
    92             }
    93         }
    94 
    95         @Override
    96         public Fn defineFn(String code, String... names) {
    97             return defineJSFn(code, names);
    98         }
    99         private JSFn defineJSFn(String code, String... names) {
   100             StringBuilder sb = new StringBuilder();
   101             sb.append("(function() {");
   102             sb.append("  return function(");
   103             String sep = "";
   104             for (String n : names) {
   105                 sb.append(sep).append(n);
   106                 sep = ",";
   107             }
   108             sb.append(") {\n");
   109             sb.append(code);
   110             sb.append("};");
   111             sb.append("})()");
   112             
   113             JSObject x = (JSObject) engine.executeScript(sb.toString());
   114             return new JSFn(this, x);
   115         }
   116 
   117         @Override
   118         public void displayPage(URL page, Runnable onPageLoad) {
   119             throw new UnsupportedOperationException("Not supported yet.");
   120         }
   121 
   122         @Override
   123         public void loadScript(Reader code) throws Exception {
   124             BufferedReader r = new BufferedReader(code);
   125             StringBuilder sb = new StringBuilder();
   126             for (;;) {
   127                 String l = r.readLine();
   128                 if (l == null) {
   129                     break;
   130                 }
   131                 sb.append(l).append('\n');
   132             }
   133             engine.executeScript(sb.toString());
   134         }
   135 
   136         @Override
   137         public Object toJava(Object js) {
   138             return checkArray(js);
   139         }
   140 
   141         @Override
   142         public Object toJavaScript(Object toReturn) {
   143             if (toReturn instanceof Object[]) {
   144                 return convertArrays((Object[]) toReturn);
   145             }
   146             return toReturn;
   147         }
   148 
   149         @Override
   150         public void execute(Runnable command) {
   151             if (Platform.isFxApplicationThread()) {
   152                 command.run();
   153             } else {
   154                 Platform.runLater(command);
   155             }
   156         }
   157         
   158         final JSObject convertArrays(Object[] arr) {
   159             for (int i = 0; i < arr.length; i++) {
   160                 if (arr[i] instanceof Object[]) {
   161                     arr[i] = convertArrays((Object[]) arr[i]);
   162                 }
   163             }
   164             final JSObject wrapArr = (JSObject) wrapArrFn().call("array", arr); // NOI18N
   165             return wrapArr;
   166         }
   167 
   168         private JSObject wrapArrImpl;
   169 
   170         private final JSObject wrapArrFn() {
   171             if (wrapArrImpl == null) {
   172                 try {
   173                     wrapArrImpl = (JSObject) defineJSFn("  var k = {};"
   174                         + "  k.array= function() {"
   175                         + "    return Array.prototype.slice.call(arguments);"
   176                         + "  };"
   177                         + "  return k;"
   178                     ).invokeImpl(null, false);
   179                 } catch (Exception ex) {
   180                     throw new IllegalStateException(ex);
   181                 }
   182             }
   183             return wrapArrImpl;
   184         }
   185 
   186         final Object checkArray(Object val) {
   187             int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
   188             if (length == -1) {
   189                 return val;
   190             }
   191             Object[] arr = new Object[length];
   192             arraySizeFn().call("array", val, arr);
   193             return arr;
   194         }
   195         private JSObject arraySize;
   196 
   197         private final JSObject arraySizeFn() {
   198             if (arraySize == null) {
   199                 try {
   200                     arraySize = (JSObject) defineJSFn("  var k = {};"
   201                         + "  k.array = function(arr, to) {"
   202                         + "    if (to === null) {"
   203                         + "      if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;"
   204                         + "      else return -1;"
   205                         + "    } else {"
   206                         + "      var l = arr.length;"
   207                         + "      for (var i = 0; i < l; i++) to[i] = arr[i];"
   208                         + "      return l;"
   209                         + "    }"
   210                         + "  };"
   211                         + "  return k;"
   212                     ).invokeImpl(null, false);
   213                 } catch (Exception ex) {
   214                     throw new IllegalStateException(ex);
   215                 }
   216             }
   217             return arraySize;
   218         }
   219         
   220     }
   221     
   222     private static final class JSFn extends Fn {
   223         private final JSObject fn;
   224 
   225         private JSFn(WebPresenter cl, JSObject fn) {
   226             super(cl);
   227             this.fn = fn;
   228         }
   229 
   230         @Override
   231         public Object invoke(Object thiz, Object... args) throws Exception {
   232             return invokeImpl(thiz, true, args);
   233         }
   234 
   235         final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
   236             try {
   237                 List<Object> all = new ArrayList<Object>(args.length + 1);
   238                 all.add(thiz == null ? fn : thiz);
   239                 for (int i = 0; i < args.length; i++) {
   240                     if (arrayChecks && args[i] instanceof Object[]) {
   241                         Object[] arr = (Object[]) args[i];
   242                         Object conv = ((WebPresenter) presenter()).convertArrays(arr);
   243                         args[i] = conv;
   244                     }
   245                     all.add(args[i]);
   246                 }
   247                 Object ret = fn.call("call", all.toArray()); // NOI18N
   248                 if (ret == fn) {
   249                     return null;
   250                 }
   251                 if (!arrayChecks) {
   252                     return ret;
   253                 }
   254                 return ((WebPresenter) presenter()).checkArray(ret);
   255             } catch (Error t) {
   256                 t.printStackTrace();
   257                 throw t;
   258             } catch (Exception t) {
   259                 t.printStackTrace();
   260                 throw t;
   261             }
   262         }
   263     }
   264 }