With Batch update of models we are down to 1/6 of Java/JavaScript calls when building a chess board
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 31 Aug 2013 11:44:36 +0000
changeset 2755a75c617a922
parent 274 5a09d20670a1
child 276 a8663166a8d1
With Batch update of models we are down to 1/6 of Java/JavaScript calls when building a chess board
json/src/main/java/org/apidesign/html/json/impl/Bindings.java
json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java
json/src/main/java/org/apidesign/html/json/spi/Technology.java
ko-fx/src/main/java/org/apidesign/html/kofx/Console.java
ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java
ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java
     1.1 --- a/json/src/main/java/org/apidesign/html/json/impl/Bindings.java	Sat Aug 31 08:01:08 2013 +0000
     1.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/Bindings.java	Sat Aug 31 11:44:36 2013 +0000
     1.3 @@ -32,36 +32,44 @@
     1.4   * @author Jaroslav Tulach <jtulach@netbeans.org>
     1.5   */
     1.6  public final class Bindings<Data> {
     1.7 -    private final Data data;
     1.8 +    private Data data;
     1.9      private final Technology<Data> bp;
    1.10  
    1.11 -    private Bindings(Data data, Technology<Data> bp) {
    1.12 -        this.data = data;
    1.13 +    private Bindings(Technology<Data> bp) {
    1.14          this.bp = bp;
    1.15      }
    1.16      
    1.17      public <M> PropertyBinding registerProperty(String propName, M model, SetAndGet<M> access, boolean readOnly) {
    1.18 -        PropertyBinding pb = PropertyBindingAccessor.create(new PBData<M>(this, propName, model, access, readOnly));
    1.19 -        bp.bind(pb, model, data);
    1.20 -        return pb;
    1.21 +        return PropertyBindingAccessor.create(new PBData<M>(this, propName, model, access, readOnly));
    1.22      }
    1.23  
    1.24      public <M> FunctionBinding registerFunction(String name, M model, Callback<M> access) {
    1.25 -        FunctionBinding fb = PropertyBindingAccessor.createFunction(new FBData<M>(name, model, access));
    1.26 -        bp.expose(fb, model, data);
    1.27 -        return fb;
    1.28 +        return PropertyBindingAccessor.createFunction(new FBData<M>(name, model, access));
    1.29      }
    1.30      
    1.31      public static Bindings<?> apply(BrwsrCtx c, Object model) {
    1.32          Technology<?> bp = JSON.findTechnology(c);
    1.33 -        return apply(bp, model);
    1.34 +        return apply(bp);
    1.35      }
    1.36      
    1.37 -    private static <Data> Bindings<Data> apply(
    1.38 -        Technology<Data> bp, Object model
    1.39 -    ) {
    1.40 -        Data d = bp.wrapModel(model);
    1.41 -        return new Bindings<Data>(d, bp);
    1.42 +    private static <Data> Bindings<Data> apply(Technology<Data> bp) {
    1.43 +        return new Bindings<Data>(bp);
    1.44 +    }
    1.45 +    
    1.46 +    public final void finish(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
    1.47 +        assert data == null;
    1.48 +        if (bp instanceof Technology.BatchInit) {
    1.49 +            Technology.BatchInit<Data> bi = (Technology.BatchInit<Data>)bp;
    1.50 +            data = bi.wrapModel(model, propArr, funcArr);
    1.51 +        } else {
    1.52 +            data = bp.wrapModel(model);
    1.53 +            for (PropertyBinding b : propArr) {
    1.54 +                bp.bind(b, model, data);
    1.55 +            }
    1.56 +            for (FunctionBinding b : funcArr) {
    1.57 +                bp.expose(b, model, data);
    1.58 +            }
    1.59 +        }
    1.60      }
    1.61      
    1.62      
     2.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Sat Aug 31 08:01:08 2013 +0000
     2.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Sat Aug 31 11:44:36 2013 +0000
     2.3 @@ -251,14 +251,23 @@
     2.4                  w.append("  private org.apidesign.html.json.impl.Bindings intKnckt() {\n");
     2.5                  w.append("    if (ko != null) return ko;\n");
     2.6                  w.append("    ko = org.apidesign.html.json.impl.Bindings.apply(context, this);\n");
     2.7 -                for (int i = 0; i < propsGetSet.size(); i += 5) {
     2.8 -                    w.append("    ko.registerProperty(\"").append(propsGetSet.get(i)).append("\", this, new P(");
     2.9 -                    w.append((i / 5) + "), " + (propsGetSet.get(i + 2) == null) + ");\n");
    2.10 +                {
    2.11 +                    w.append("    org.apidesign.html.json.spi.PropertyBinding[] propArr = {\n");
    2.12 +                    for (int i = 0; i < propsGetSet.size(); i += 5) {
    2.13 +                        w.append("      ko.registerProperty(\"").append(propsGetSet.get(i)).append("\", this, new P(");
    2.14 +                        w.append((i / 5) + "), " + (propsGetSet.get(i + 2) == null) + "),\n");
    2.15 +                    }
    2.16 +                    w.append("    };\n");
    2.17                  }
    2.18 -                for (int i = 0; i < functions.size(); i += 2) {
    2.19 -                    w.append("    ko.registerFunction(\"").append(functions.get(i)).append("\", this, new P(");
    2.20 -                    w.append((i / 2) + "));\n");
    2.21 +                {
    2.22 +                    w.append("    org.apidesign.html.json.spi.FunctionBinding[] funcArr = {\n");
    2.23 +                    for (int i = 0; i < functions.size(); i += 2) {
    2.24 +                        w.append("      ko.registerFunction(\"").append(functions.get(i)).append("\", this, new P(");
    2.25 +                        w.append((i / 2) + ")),\n");
    2.26 +                    }
    2.27 +                    w.append("    };\n");
    2.28                  }
    2.29 +                w.append("    ko.finish(this, propArr, funcArr);\n");
    2.30                  w.append("    return ko;\n");
    2.31                  w.append("  };\n");
    2.32                  w.append("  private static final class P implements org.apidesign.html.json.impl.SetAndGet<" + className + ">,\n");
     3.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Technology.java	Sat Aug 31 08:01:08 2013 +0000
     3.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Technology.java	Sat Aug 31 11:44:36 2013 +0000
     3.3 @@ -87,4 +87,8 @@
     3.4       * @param r the runnable to execute
     3.5       */
     3.6      public void runSafe(Runnable r);
     3.7 +
     3.8 +    public static interface BatchInit<D> extends Technology<D> {
     3.9 +        public D wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr);
    3.10 +    }
    3.11  }
     4.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/Console.java	Sat Aug 31 08:01:08 2013 +0000
     4.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/Console.java	Sat Aug 31 11:44:36 2013 +0000
     4.3 @@ -20,6 +20,7 @@
     4.4   */
     4.5  package org.apidesign.html.kofx;
     4.6  
     4.7 +import java.util.logging.Level;
     4.8  import java.util.logging.Logger;
     4.9  import net.java.html.js.JavaScriptBody;
    4.10  
    4.11 @@ -31,25 +32,28 @@
    4.12   *
    4.13   * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.14   */
    4.15 -public final class Console {
    4.16 +final class Console {
    4.17      private static final Logger LOG = Logger.getLogger(Console.class.getName());
    4.18      
    4.19      private Console() {
    4.20      }
    4.21  
    4.22      static void register() {
    4.23 -        registerImpl(new Console());
    4.24 +        registerImpl("log", Level.INFO);
    4.25 +        registerImpl("info", Level.INFO);
    4.26 +        registerImpl("warn", Level.WARNING);
    4.27 +        registerImpl("error", Level.SEVERE);
    4.28      }
    4.29      
    4.30 -    @JavaScriptBody(args = { "jconsole" }, body = 
    4.31 -        "console.log = function(m) { jconsole.log(m); };" +
    4.32 -        "console.info = function(m) { jconsole.log(m); };" +
    4.33 -        "console.error = function(m) { jconsole.log(m); };" +
    4.34 -        "console.warn = function(m) { jconsole.log(m); };"
    4.35 +    @JavaScriptBody(args = { "attr", "l" }, 
    4.36 +        javacall = true, body = 
    4.37 +        "  window.console[attr] = function(m) {\n"
    4.38 +      + "    @org.apidesign.html.kofx.Console::log(Ljava/util/logging/Level;Ljava/lang/String;)(l, m);\n"
    4.39 +      + "  };\n"
    4.40      )
    4.41 -    private static native void registerImpl(Console c);
    4.42 +    private static native void registerImpl(String attr, Level l);
    4.43      
    4.44 -    public void log(String msg) {
    4.45 -        LOG.info(msg);
    4.46 +    static void log(Level l, String msg) {
    4.47 +        LOG.log(l, msg);
    4.48      }
    4.49  }
     5.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java	Sat Aug 31 08:01:08 2013 +0000
     5.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java	Sat Aug 31 11:44:36 2013 +0000
     5.3 @@ -46,7 +46,7 @@
     5.4   */
     5.5  @ServiceProvider(service = Contexts.Provider.class)
     5.6  public final class FXContext
     5.7 -implements Technology<JSObject>, Transfer, Contexts.Provider, WSTransfer<LoadWS> {
     5.8 +implements Technology.BatchInit<JSObject>, Transfer, Contexts.Provider, WSTransfer<LoadWS> {
     5.9      static final Logger LOG = Logger.getLogger(FXContext.class.getName());
    5.10      private static Boolean javaScriptEnabled;
    5.11      
    5.12 @@ -77,6 +77,22 @@
    5.13          return LoadWS.isSupported();
    5.14      }
    5.15  
    5.16 +
    5.17 +    @Override
    5.18 +    public JSObject wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
    5.19 +        String[] propNames = new String[propArr.length];
    5.20 +        boolean[] propReadOnly = new boolean[propArr.length];
    5.21 +        for (int i = 0; i < propNames.length; i++) {
    5.22 +            propNames[i] = propArr[i].getPropertyName();
    5.23 +            propReadOnly[i] = propArr[i].isReadOnly();
    5.24 +        }
    5.25 +        String[] funcNames = new String[funcArr.length];
    5.26 +        for (int i = 0; i < funcNames.length; i++) {
    5.27 +            funcNames[i] = funcArr[i].getFunctionName();
    5.28 +        }
    5.29 +        return Knockout.wrapModel(model, propNames, propReadOnly, propArr, funcNames, funcArr);
    5.30 +    }
    5.31 +    
    5.32      @Override
    5.33      public JSObject wrapModel(Object model) {
    5.34          JSObject obj = (JSObject) Knockout.createBinding(model).koData();
     6.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java	Sat Aug 31 08:01:08 2013 +0000
     6.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java	Sat Aug 31 11:44:36 2013 +0000
     6.3 @@ -20,10 +20,6 @@
     6.4   */
     6.5  package org.apidesign.html.kofx;
     6.6  
     6.7 -import java.io.BufferedReader;
     6.8 -import java.io.IOException;
     6.9 -import java.io.InputStream;
    6.10 -import java.io.InputStreamReader;
    6.11  import java.util.logging.Level;
    6.12  import java.util.logging.Logger;
    6.13  import net.java.html.js.JavaScriptBody;
    6.14 @@ -67,15 +63,26 @@
    6.15          return new Knockout(bindings);
    6.16      }
    6.17  
    6.18 +    static JSObject wrapModel(
    6.19 +        Object model, 
    6.20 +        String[] propNames, boolean[] propReadOnly, PropertyBinding[] propArr, 
    6.21 +        String[] funcNames, FunctionBinding[] funcArr
    6.22 +    ) {
    6.23 +        return InvokeJS.wrapModel(model, propNames, propReadOnly, propArr, funcNames, funcArr);
    6.24 +    }
    6.25 +    
    6.26 +
    6.27      public void valueHasMutated(String prop) {
    6.28          valueHasMutated((JSObject) model, prop);
    6.29      }
    6.30      public static void valueHasMutated(JSObject model, String prop) {
    6.31          LOG.log(Level.FINE, "property mutated: {0}", prop);
    6.32          try {
    6.33 +            if (model != null) {
    6.34              Object koProp = model.getMember(prop);
    6.35 -            if (koProp instanceof JSObject) {
    6.36 -                ((JSObject)koProp).call("valueHasMutated");
    6.37 +                if (koProp instanceof JSObject) {
    6.38 +                    ((JSObject)koProp).call("valueHasMutated");
    6.39 +                }
    6.40              }
    6.41          } catch (Throwable t) {
    6.42              LOG.log(Level.WARNING, "valueHasMutated failed for " + model + " prop: " + prop, t);
    6.43 @@ -127,6 +134,51 @@
    6.44          )
    6.45          private static native Object kObj();
    6.46          
    6.47 +        @JavaScriptBody(
    6.48 +            javacall = true,
    6.49 +            args = {"model", "propNames", "propReadOnly", "propArr", "funcNames", "funcArr"},
    6.50 +            body
    6.51 +            = "var ret = {};\n"
    6.52 +            + "ret['ko-fx.model'] = model;\n"
    6.53 +            + "function koComputed(name, readOnly, prop) {\n"
    6.54 +            + "  var bnd = {"
    6.55 +            + "    read: function() {"
    6.56 +            + "      try {"
    6.57 +            + "        var v = prop.@org.apidesign.html.json.spi.PropertyBinding::getValue()();"
    6.58 +            + "        return v;"
    6.59 +            + "      } catch (e) {"
    6.60 +            + "        alert(\"Cannot call getValue on \" + model + \" prop: \" + name + \" error: \" + e);"
    6.61 +            + "      }"
    6.62 +            + "    },"
    6.63 +            + "    owner: ret\n"
    6.64 +            + "  };\n"
    6.65 +            + "  if (!readOnly) {\n"
    6.66 +            + "    bnd.write = function(val) {\n"
    6.67 +            + "      prop.@org.apidesign.html.json.spi.PropertyBinding::setValue(Ljava/lang/Object;)(val);\n"
    6.68 +            + "    };"
    6.69 +            + "  };"
    6.70 +            + "  ret[name] = ko.computed(bnd);"
    6.71 +            + "}\n"
    6.72 +            + "for (var i = 0; i < propNames.length; i++) {\n"
    6.73 +            + "  koComputed(propNames[i], propReadOnly[i], propArr[i]);\n"
    6.74 +            + "}\n"
    6.75 +            + "function koExpose(name, func) {\n"
    6.76 +            + "  ret[name] = function(data, ev) {\n"
    6.77 +            + "    func.@org.apidesign.html.json.spi.FunctionBinding::call(Ljava/lang/Object;Ljava/lang/Object;)(data, ev);\n"
    6.78 +            + "  };\n"
    6.79 +            + "}\n"
    6.80 +            + "for (var i = 0; i < funcNames.length; i++) {\n"
    6.81 +            + "  koExpose(funcNames[i], funcArr[i]);\n"
    6.82 +            + "}\n"
    6.83 +            + "return ret;\n"
    6.84 +            )
    6.85 +        static native JSObject wrapModel(
    6.86 +            Object model,
    6.87 +            String[] propNames, boolean[] propReadOnly, PropertyBinding[] propArr,
    6.88 +            String[] funcNames, FunctionBinding[] funcArr
    6.89 +        );
    6.90 +        
    6.91 +        
    6.92          @JavaScriptBody(args = { "value", "cnt " }, body =
    6.93                    "    var ret = {};"
    6.94  /*              + "    ret.toString = function() { return 'KObject' + cnt + ' value: ' + value + ' props: ' + Object.keys(this); }; " */