Precompute property values in Java, so ko.computed does not need to call back from JavaScript to get initial value.
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 01 Sep 2013 07:13:38 +0000
changeset 279539d268a7fec
parent 278 96d0e3799ba2
child 280 83f39c708849
Precompute property values in Java, so ko.computed does not need to call back from JavaScript to get initial value.
ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java
ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java
ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java
ko-fx/src/test/java/org/apidesign/html/kofx/LessCallbacksCheck.java
     1.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java	Sun Sep 01 06:52:02 2013 +0000
     1.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java	Sun Sep 01 07:13:38 2013 +0000
     1.3 @@ -82,15 +82,21 @@
     1.4      public JSObject wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
     1.5          String[] propNames = new String[propArr.length];
     1.6          boolean[] propReadOnly = new boolean[propArr.length];
     1.7 +        Object[] propValues = new Object[propArr.length];
     1.8          for (int i = 0; i < propNames.length; i++) {
     1.9              propNames[i] = propArr[i].getPropertyName();
    1.10              propReadOnly[i] = propArr[i].isReadOnly();
    1.11 +            propValues[i] = propArr[i].getValue();
    1.12          }
    1.13          String[] funcNames = new String[funcArr.length];
    1.14          for (int i = 0; i < funcNames.length; i++) {
    1.15              funcNames[i] = funcArr[i].getFunctionName();
    1.16          }
    1.17 -        return Knockout.wrapModel(model, propNames, propReadOnly, propArr, funcNames, funcArr);
    1.18 +        
    1.19 +        return Knockout.wrapModel(model, 
    1.20 +            propNames, propReadOnly, Knockout.toArray(propValues), propArr, 
    1.21 +            funcNames, funcArr
    1.22 +        );
    1.23      }
    1.24      
    1.25      @Override
     2.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java	Sun Sep 01 06:52:02 2013 +0000
     2.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java	Sun Sep 01 07:13:38 2013 +0000
     2.3 @@ -73,19 +73,25 @@
     2.4          
     2.5      @JavaScriptBody(
     2.6          javacall = true,
     2.7 -        args = {"model", "propNames", "propReadOnly", "propArr", "funcNames", "funcArr"},
     2.8 +        args = {"model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
     2.9          body
    2.10          = "var ret = {};\n"
    2.11          + "ret['ko-fx.model'] = model;\n"
    2.12 -        + "function koComputed(name, readOnly, prop) {\n"
    2.13 +        + "function koComputed(name, readOnly, value, prop) {\n"
    2.14 +        + "  function realGetter() {\n"
    2.15 +        + "    try {"
    2.16 +        + "      var v = prop.@org.apidesign.html.json.spi.PropertyBinding::getValue()();"
    2.17 +        + "      return v;"
    2.18 +        + "    } catch (e) {"
    2.19 +        + "      alert(\"Cannot call getValue on \" + model + \" prop: \" + name + \" error: \" + e);"
    2.20 +        + "    }"
    2.21 +        + "  }\n"
    2.22 +        + "  var activeGetter = function() { return value; };\n"
    2.23          + "  var bnd = {"
    2.24          + "    read: function() {"
    2.25 -        + "      try {"
    2.26 -        + "        var v = prop.@org.apidesign.html.json.spi.PropertyBinding::getValue()();"
    2.27 -        + "        return v;"
    2.28 -        + "      } catch (e) {"
    2.29 -        + "        alert(\"Cannot call getValue on \" + model + \" prop: \" + name + \" error: \" + e);"
    2.30 -        + "      }"
    2.31 +        + "      var r = activeGetter();"
    2.32 +        + "      activeGetter = realGetter;"
    2.33 +        + "      return r;"
    2.34          + "    },"
    2.35          + "    owner: ret\n"
    2.36          + "  };\n"
    2.37 @@ -97,7 +103,7 @@
    2.38          + "  ret[name] = ko.computed(bnd);"
    2.39          + "}\n"
    2.40          + "for (var i = 0; i < propNames.length; i++) {\n"
    2.41 -        + "  koComputed(propNames[i], propReadOnly[i], propArr[i]);\n"
    2.42 +        + "  koComputed(propNames[i], propReadOnly[i], propValues[i], propArr[i]);\n"
    2.43          + "}\n"
    2.44          + "function koExpose(name, func) {\n"
    2.45          + "  ret[name] = function(data, ev) {\n"
    2.46 @@ -111,7 +117,7 @@
    2.47          )
    2.48      static native JSObject wrapModel(
    2.49          Object model,
    2.50 -        String[] propNames, boolean[] propReadOnly, PropertyBinding[] propArr,
    2.51 +        String[] propNames, boolean[] propReadOnly, Object propValues, PropertyBinding[] propArr,
    2.52          String[] funcNames, FunctionBinding[] funcArr
    2.53      );
    2.54  }
     3.1 --- a/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java	Sun Sep 01 06:52:02 2013 +0000
     3.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java	Sun Sep 01 07:13:38 2013 +0000
     3.3 @@ -86,16 +86,22 @@
     3.4          List<Object> res = new ArrayList<Object>();
     3.5          for (int i = 0; i < arr.length; i++) {
     3.6              Class<?> c = Class.forName(arr[i].getName(), true, l);
     3.7 -            Class<? extends Annotation> koTest = 
     3.8 -                c.getClassLoader().loadClass(KOTest.class.getName()).
     3.9 -                asSubclass(Annotation.class);
    3.10 -            for (Method m : c.getMethods()) {
    3.11 -                if (m.getAnnotation(koTest) != null) {
    3.12 -                    res.add(new KOFx(m));
    3.13 -                }
    3.14 +            seekKOTests(c, res);
    3.15 +        }
    3.16 +        Class<?> c = Class.forName(LessCallbacksCheck.class.getName(), true, l);
    3.17 +        seekKOTests(c, res);
    3.18 +        return res.toArray();
    3.19 +    }
    3.20 +
    3.21 +    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
    3.22 +        Class<? extends Annotation> koTest =
    3.23 +            c.getClassLoader().loadClass(KOTest.class.getName()).
    3.24 +            asSubclass(Annotation.class);
    3.25 +        for (Method m : c.getMethods()) {
    3.26 +            if (m.getAnnotation(koTest) != null) {
    3.27 +                res.add(new KOFx(m));
    3.28              }
    3.29          }
    3.30 -        return res.toArray();
    3.31      }
    3.32  
    3.33      static synchronized ClassLoader getClassLoader() throws InterruptedException {
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/LessCallbacksCheck.java	Sun Sep 01 07:13:38 2013 +0000
     4.3 @@ -0,0 +1,60 @@
     4.4 +/**
     4.5 + * HTML via Java(tm) Language Bindings
     4.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4.7 + *
     4.8 + * This program is free software: you can redistribute it and/or modify
     4.9 + * it under the terms of the GNU General Public License as published by
    4.10 + * the Free Software Foundation, version 2 of the License.
    4.11 + *
    4.12 + * This program is distributed in the hope that it will be useful,
    4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.15 + * GNU General Public License for more details. apidesign.org
    4.16 + * designates this particular file as subject to the
    4.17 + * "Classpath" exception as provided by apidesign.org
    4.18 + * in the License file that accompanied this code.
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License
    4.21 + * along with this program. Look for COPYING file in the top folder.
    4.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    4.23 + */
    4.24 +package org.apidesign.html.kofx;
    4.25 +
    4.26 +import java.io.PrintWriter;
    4.27 +import java.io.StringWriter;
    4.28 +import net.java.html.json.ComputedProperty;
    4.29 +import net.java.html.json.Model;
    4.30 +import net.java.html.json.Property;
    4.31 +import org.apidesign.html.json.tck.KOTest;
    4.32 +
    4.33 +/**
    4.34 + *
    4.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.36 + */
    4.37 +@Model(className = "LessCalls", properties = {
    4.38 +    @Property(name = "value", type = int.class)
    4.39 +})
    4.40 +public class LessCallbacksCheck {
    4.41 +    private static StringWriter sw;
    4.42 +    
    4.43 +    @ComputedProperty static int plusOne(int value) {
    4.44 +        if (sw == null) {
    4.45 +            sw = new StringWriter();
    4.46 +        }
    4.47 +        new Exception("Who calls me?").printStackTrace(
    4.48 +            new PrintWriter(sw)
    4.49 +        );
    4.50 +        return value + 1;
    4.51 +    }
    4.52 +    
    4.53 +    @KOTest public void dontCallForInitialValueBackToJavaVM() {
    4.54 +        LessCalls m = new LessCalls(10).applyBindings();
    4.55 +        assert m.getPlusOne() == 11 : "Expecting 11: " + m.getPlusOne();
    4.56 +        
    4.57 +        assert sw != null : "StringWriter should be initialized: " + sw;
    4.58 +        
    4.59 +        if (sw.toString().contains("$JsCallbacks$")) {
    4.60 +            assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
    4.61 +        }
    4.62 +    }
    4.63 +}