Setters from any thread merge
authorJaroslav Tulach <jtulach@netbeans.org>
Sun, 13 Jul 2014 23:50:25 +0200
changeset 72766f03ad8f00f
parent 726 522f8ee41295
parent 721 f8e1066fc8cd
child 729 0f8827288b31
Setters from any thread merge
geo/src/main/java/net/java/html/geo/Position.java
     1.1 --- a/boot-script/src/main/java/net/java/html/boot/script/Scripts.java	Sun Jul 13 23:49:46 2014 +0200
     1.2 +++ b/boot-script/src/main/java/net/java/html/boot/script/Scripts.java	Sun Jul 13 23:50:25 2014 +0200
     1.3 @@ -42,10 +42,12 @@
     1.4   */
     1.5  package net.java.html.boot.script;
     1.6  
     1.7 +import java.io.Closeable;
     1.8  import java.util.concurrent.Executor;
     1.9  import javax.script.ScriptEngine;
    1.10  import net.java.html.boot.BrowserBuilder;
    1.11  import net.java.html.js.JavaScriptBody;
    1.12 +import org.apidesign.html.boot.spi.Fn;
    1.13  import org.apidesign.html.boot.spi.Fn.Presenter;
    1.14  
    1.15  /** Implementations of {@link Presenter}s that delegate
    1.16 @@ -55,14 +57,29 @@
    1.17   * <pre>
    1.18   * 
    1.19   * {@link Runnable} <em>run</em> = ...; // your own init code
    1.20 - * {@link Presenter Fn.Presenter} <em>p</em> = Scripts.{@link Scripts#createPresenter()};
    1.21 - * BrowserBuilder.{@link BrowserBuilder#newBrowser(java.lang.Object...) newBrowser(<em>p</em>)}.
    1.22 + * {@link Presenter Fn.Presenter} <b>p</b> = Scripts.{@link Scripts#createPresenter()};
    1.23 + * BrowserBuilder.{@link BrowserBuilder#newBrowser(java.lang.Object...) newBrowser(<b>p</b>)}.
    1.24   *      {@link BrowserBuilder#loadFinished(java.lang.Runnable) loadFinished(run)}.
    1.25   *      {@link BrowserBuilder#showAndWait()};
    1.26   * </pre>
    1.27   * 
    1.28   * and your runnable can make extensive use of {@link JavaScriptBody} directly or
    1.29   * indirectly via APIs using {@link JavaScriptBody such annotation} themselves.
    1.30 + * <p>
    1.31 + * Alternatively one can manipulate the presenter manually, which is
    1.32 + * especially useful when writing tests:
    1.33 + * <pre>
    1.34 + * {@code @Test} public void runInASimulatedBrowser() throws Exception {
    1.35 + *   {@link Presenter Fn.Presenter} <b>p</b> = Scripts.{@link Scripts#createPresenter()};
    1.36 + *   try ({@link Closeable} c = {@link Fn#activate(org.apidesign.html.boot.spi.Fn.Presenter) Fn.activate}(<b>p</b>)) {
    1.37 + *     // your code operating in context of <b>p</b>
    1.38 + *   }
    1.39 + * }
    1.40 + * </pre>
    1.41 + * The previous code snippet requires Java 7 language syntax, as it relies
    1.42 + * on try-with-resources language syntactic sugar feature. The same block
    1.43 + * of code can be used on older versions of Java, but it is slightly more
    1.44 + * verbose.
    1.45   * 
    1.46   * @author Jaroslav Tulach
    1.47   */
     2.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Sun Jul 13 23:49:46 2014 +0200
     2.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Sun Jul 13 23:50:25 2014 +0200
     2.3 @@ -193,8 +193,9 @@
     2.4          return FnContext.currentPresenter(false);
     2.5      }
     2.6      
     2.7 -    /** Activates given presenter. Used by the code generated by 
     2.8 -     * {@link JavaScriptBody} annotation: 
     2.9 +    /** Activates given presenter. Used to associate the native 
    2.10 +     * JavaScript code specified by 
    2.11 +     * {@link JavaScriptBody} annotation with certain presenter:
    2.12       * <pre>
    2.13       * try ({@link Closeable} c = Fn.activate(presenter)) {
    2.14       *   doCallsInPresenterContext();
     3.1 --- a/geo/src/main/java/net/java/html/geo/OnLocation.java	Sun Jul 13 23:49:46 2014 +0200
     3.2 +++ b/geo/src/main/java/net/java/html/geo/OnLocation.java	Sun Jul 13 23:50:25 2014 +0200
     3.3 @@ -64,7 +64,7 @@
     3.4   * In case something goes wrong a method in the same class named {@link #onError()}
     3.5   * can be specified (should take one {@link Exception} parameter). 
     3.6   * <p>
     3.7 - * The overall behavior of the system mimics <a href="http://www.w3.org/TR/2012/PR­geolocation­API­20120510/">
     3.8 + * The overall behavior of the system mimics <a href="http://www.w3.org/TR/geolocation-API/">
     3.9   * W3C's Geolocation API</a>.
    3.10   *
    3.11   * @author Jaroslav Tulach <jtulach@netbeans.org>
     4.1 --- a/geo/src/main/java/net/java/html/geo/Position.java	Sun Jul 13 23:49:46 2014 +0200
     4.2 +++ b/geo/src/main/java/net/java/html/geo/Position.java	Sun Jul 13 23:50:25 2014 +0200
     4.3 @@ -90,24 +90,46 @@
     4.4              this.data = data;
     4.5          }
     4.6          
     4.7 +        /**
     4.8 +         * @return geographic coordinate specified in decimal degrees.
     4.9 +         */
    4.10          public double getLatitude() {
    4.11              return ((Number)JsG.get(data, "latitude")).doubleValue(); // NOI18N
    4.12          }
    4.13  
    4.14 +        /**
    4.15 +         * @return geographic coordinate specified in decimal degrees.
    4.16 +         */
    4.17          public double getLongitude() {
    4.18              return ((Number)JsG.get(data, "longitude")).doubleValue(); // NOI18N
    4.19 -        }
    4.20 +        } 
    4.21  
    4.22 +        /**
    4.23 +         * The accuracy attribute denotes the accuracy level of the latitude 
    4.24 +         * and longitude coordinates. It is specified in meters. 
    4.25 +         * The value of the accuracy attribute must be a non-negative number.
    4.26 +         * 
    4.27 +         * @return accuracy in meters
    4.28 +         */
    4.29          public double getAccuracy() {
    4.30              return ((Number)JsG.get(data, "accuracy")).doubleValue(); // NOI18N
    4.31          }
    4.32          
    4.33 -        /** @return may return null, if the information is not available */
    4.34 +        /** Denotes the height of the position, specified in meters above the ellipsoid. 
    4.35 +         * If the implementation cannot provide altitude information, 
    4.36 +         * the value of this attribute must be null.
    4.37 +         * @return value in meters, may return null, if the information is not available 
    4.38 +         */
    4.39          public Double getAltitude() {
    4.40              return (Double)JsG.get(data, "altitude"); // NOI18N
    4.41          }
    4.42          
    4.43 -        /** @return may return null, if the information is not available */
    4.44 +        /**  The altitude accuracy is specified in meters. 
    4.45 +         * If the implementation cannot provide altitude information, 
    4.46 +         * the value of this attribute must be null. Otherwise, the value 
    4.47 +         * must be a non-negative real number.
    4.48 +         * @return value in meters; may return null, if the information is not available 
    4.49 +         */
    4.50          public Double getAltitudeAccuracy() {
    4.51              return (Double)JsG.get(data, "altitudeAccuracy"); // NOI18N
    4.52          }
     5.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Sun Jul 13 23:49:46 2014 +0200
     5.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Sun Jul 13 23:50:25 2014 +0200
     5.3 @@ -43,6 +43,8 @@
     5.4  package net.java.html.json.tests;
     5.5  
     5.6  import java.util.List;
     5.7 +import java.util.Timer;
     5.8 +import java.util.TimerTask;
     5.9  import net.java.html.BrwsrCtx;
    5.10  import net.java.html.json.ComputedProperty;
    5.11  import net.java.html.json.Function;
    5.12 @@ -64,6 +66,7 @@
    5.13      @Property(name="latitude", type=double.class)
    5.14  }) 
    5.15  public final class KnockoutTest {
    5.16 +    private KnockoutModel js;
    5.17      
    5.18      @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
    5.19          Object exp = Utils.exposeHTML(KnockoutTest.class, 
    5.20 @@ -137,6 +140,38 @@
    5.21          }
    5.22      }
    5.23      
    5.24 +    @KOTest public void modifyValueAssertAsyncChangeInModel() throws Exception {
    5.25 +        if (js == null) {
    5.26 +            Utils.exposeHTML(KnockoutTest.class, 
    5.27 +                "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
    5.28 +                "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
    5.29 +                "<button id=\"hello\">Say Hello!</button>\n"
    5.30 +            );
    5.31 +            
    5.32 +            js = Models.bind(new KnockoutModel(), newContext());
    5.33 +            js.setName("Kukuc");
    5.34 +            js.applyBindings();
    5.35 +            
    5.36 +            String v = getSetInput(null);
    5.37 +            assert "Kukuc".equals(v) : "Value is really kukuc: " + v;
    5.38 +            
    5.39 +            Timer t = new Timer("Set to Jardo");
    5.40 +            t.schedule(new TimerTask() {
    5.41 +                @Override
    5.42 +                public void run() {
    5.43 +                    js.setName("Jardo");
    5.44 +                }
    5.45 +            }, 1);
    5.46 +        }
    5.47 +        
    5.48 +        String v = getSetInput(null);
    5.49 +        if (!"Jardo".equals(v)) {
    5.50 +            throw new InterruptedException();
    5.51 +        }
    5.52 +        
    5.53 +        Utils.exposeHTML(KnockoutTest.class, "");
    5.54 +    }
    5.55 +    
    5.56      private static String getSetInput(String value) throws Exception {
    5.57          String s = "var value = arguments[0];\n"
    5.58          + "var n = window.document.getElementById('input'); \n "
    5.59 @@ -185,6 +220,45 @@
    5.60          }
    5.61      }
    5.62      
    5.63 +    @KOTest public void displayContentOfAsyncArray() throws Exception {
    5.64 +        if (js == null) {
    5.65 +            Utils.exposeHTML(KnockoutTest.class, 
    5.66 +                "<ul id='ul' data-bind='foreach: results'>\n"
    5.67 +                + "  <li data-bind='text: $data, click: $root.call'/>\n"
    5.68 +                + "</ul>\n"
    5.69 +            );
    5.70 +            js = Models.bind(new KnockoutModel(), newContext());
    5.71 +            js.getResults().add("Ahoj");
    5.72 +            js.applyBindings();
    5.73 +
    5.74 +            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
    5.75 +            assert cnt == 1 : "One child, but was " + cnt;
    5.76 +            
    5.77 +            Timer t = new Timer("add to array");
    5.78 +            t.schedule(new TimerTask() {
    5.79 +                @Override
    5.80 +                public void run() {
    5.81 +                    js.getResults().add("Hi");
    5.82 +                }
    5.83 +            }, 1);
    5.84 +        }
    5.85 +
    5.86 +
    5.87 +        int cnt = Utils.countChildren(KnockoutTest.class, "ul");
    5.88 +        if (cnt != 2) {
    5.89 +            throw new InterruptedException();
    5.90 +        }
    5.91 +
    5.92 +        try {
    5.93 +            triggerChildClick("ul", 1);
    5.94 +
    5.95 +            assert 1 == js.getCallbackCount() : "One callback " + js.getCallbackCount();
    5.96 +            assert "Hi".equals(js.getName()) : "We got callback from 2nd child " + js.getName();
    5.97 +        } finally {
    5.98 +            Utils.exposeHTML(KnockoutTest.class, "");
    5.99 +        }
   5.100 +    }
   5.101 +    
   5.102      @KOTest public void displayContentOfComputedArray() throws Exception {
   5.103          Object exp = Utils.exposeHTML(KnockoutTest.class, 
   5.104              "<ul id='ul' data-bind='foreach: bothNames'>\n"
     6.1 --- a/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java	Sun Jul 13 23:49:46 2014 +0200
     6.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java	Sun Jul 13 23:50:25 2014 +0200
     6.3 @@ -66,19 +66,7 @@
     6.4      
     6.5      @KOTest public void asyncOperation() throws InterruptedException {
     6.6          if (js == null) {
     6.7 -            try {
     6.8 -                // needs full JVM (not Bck2Brwsr VM) to run
     6.9 -                Class<?> thread = Class.forName("java.lang.Thread");
    6.10 -                Thread t = new Thread("Empty");
    6.11 -                // following operations are supposed to generate SecurityException
    6.12 -                // on bck2brwsr VM
    6.13 -                t.setName("Different");
    6.14 -                t.setDaemon(false);
    6.15 -                t.interrupt();
    6.16 -                t.start();
    6.17 -            } catch (ClassNotFoundException ex) {
    6.18 -                return;
    6.19 -            } catch (SecurityException ex) {
    6.20 +            if (Utils.skipIfNoFullJDK()) {
    6.21                  return;
    6.22              }
    6.23              
    6.24 @@ -104,7 +92,4 @@
    6.25          assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
    6.26          assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.27      }
    6.28 -    
    6.29 -
    6.30 -    
    6.31  }
     7.1 --- a/json-tck/src/main/java/net/java/html/json/tests/Utils.java	Sun Jul 13 23:49:46 2014 +0200
     7.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/Utils.java	Sun Jul 13 23:50:25 2014 +0200
     7.3 @@ -56,6 +56,22 @@
     7.4  public final class Utils {
     7.5      private static KnockoutTCK instantiatedTCK;
     7.6  
     7.7 +    static boolean skipIfNoFullJDK() {
     7.8 +        try {
     7.9 +            Class<?> thread = Class.forName("java.lang.Thread");
    7.10 +            Thread t = new Thread("Empty");
    7.11 +            t.setName("Different");
    7.12 +            t.setDaemon(false);
    7.13 +            t.interrupt();
    7.14 +            t.start();
    7.15 +        } catch (ClassNotFoundException ex) {
    7.16 +            return true;
    7.17 +        } catch (SecurityException ex) {
    7.18 +            return true;
    7.19 +        }
    7.20 +        return false;
    7.21 +    }
    7.22 +
    7.23      private Utils() {
    7.24      }
    7.25      
     8.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Sun Jul 13 23:49:46 2014 +0200
     8.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Sun Jul 13 23:50:25 2014 +0200
     8.3 @@ -110,14 +110,22 @@
     8.4      }
     8.5      
     8.6      /** Whenever model changes a property. It should notify the
     8.7 -     * associated technology by calling this method.
     8.8 +     * associated technology by calling this method. 
     8.9 +     * Since 0.8.3: This method may be called by any thread - it reschedules
    8.10 +     * its actual execution into appropriate one by using
    8.11 +     * {@link BrwsrCtx#execute(java.lang.Runnable)}.
    8.12       * 
    8.13       * @param propName name of the changed property
    8.14       */
    8.15 -    public void valueHasMutated(String propName) {
    8.16 -        if (ko != null) {
    8.17 -            ko.valueHasMutated(propName, null, null);
    8.18 -        }
    8.19 +    public void valueHasMutated(final String propName) {
    8.20 +        context.execute(new Runnable() {
    8.21 +            @Override
    8.22 +            public void run() {
    8.23 +                if (ko != null) {
    8.24 +                    ko.valueHasMutated(propName, null, null);
    8.25 +                }
    8.26 +            }
    8.27 +        });
    8.28      }
    8.29  
    8.30      /** Whenever model changes a propertyit should notify the
    8.31 @@ -125,16 +133,26 @@
    8.32       * (if the new value is known and different to the old one) or
    8.33       * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
    8.34       * method.
    8.35 +     * Since 0.8.3: This method may be called by any thread - it reschedules
    8.36 +     * its actual execution into appropriate one by using
    8.37 +     * {@link BrwsrCtx#execute(java.lang.Runnable)}.
    8.38       * 
    8.39       * @param propName name of the changed property
    8.40       * @param oldValue provides previous value of the property
    8.41       * @param newValue provides new value of the property
    8.42       * @since 0.7.6
    8.43       */
    8.44 -    public void valueHasMutated(String propName, Object oldValue, Object newValue) {
    8.45 -        if (ko != null) {
    8.46 -            ko.valueHasMutated(propName, oldValue, newValue);
    8.47 -        }
    8.48 +    public void valueHasMutated(
    8.49 +        final String propName, final Object oldValue, final Object newValue
    8.50 +    ) {
    8.51 +        context.execute(new Runnable() {
    8.52 +            @Override
    8.53 +            public void run() {
    8.54 +                if (ko != null) {
    8.55 +                    ko.valueHasMutated(propName, oldValue, newValue);
    8.56 +                }
    8.57 +            }
    8.58 +        });
    8.59      }
    8.60      
    8.61      /** Initializes the associated model in the current {@link #getContext() context}.
     9.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Sun Jul 13 23:49:46 2014 +0200
     9.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Sun Jul 13 23:50:25 2014 +0200
     9.3 @@ -174,16 +174,21 @@
     9.4      }
     9.5  
     9.6      private void notifyChange() {
     9.7 -        Bindings m = PropertyBindingAccessor.getBindings(proto, false);
     9.8 -        if (m != null) {
     9.9 -            m.valueHasMutated(name, null, null);
    9.10 -            for (String dependant : deps) {
    9.11 -                m.valueHasMutated(dependant, null, null);
    9.12 +        proto.getContext().execute(new Runnable() {
    9.13 +            @Override
    9.14 +            public void run() {
    9.15 +                Bindings m = PropertyBindingAccessor.getBindings(proto, false);
    9.16 +                if (m != null) {
    9.17 +                    m.valueHasMutated(name, null, null);
    9.18 +                    for (String dependant : deps) {
    9.19 +                        m.valueHasMutated(dependant, null, null);
    9.20 +                    }
    9.21 +                    if (index >= 0) {
    9.22 +                        PropertyBindingAccessor.notifyProtoChange(proto, index);
    9.23 +                    }
    9.24 +                }
    9.25              }
    9.26 -            if (index >= 0) {
    9.27 -                PropertyBindingAccessor.notifyProtoChange(proto, index);
    9.28 -            }
    9.29 -        }
    9.30 +        });
    9.31      }
    9.32  
    9.33      @Override
    10.1 --- a/src/main/javadoc/overview.html	Sun Jul 13 23:49:46 2014 +0200
    10.2 +++ b/src/main/javadoc/overview.html	Sun Jul 13 23:50:25 2014 +0200
    10.3 @@ -75,6 +75,13 @@
    10.4           yet the application code can be written in Java.
    10.5          </p>
    10.6          
    10.7 +        <h3>What's New in Version 0.8.3?</h3>
    10.8 +        
    10.9 +        <p>
   10.10 +            Setters or array properties on classes generated by {@link net.java.html.json.Model}
   10.11 +            annotation can be accessed from any thread. 
   10.12 +        </p>
   10.13 +        
   10.14          <h3>What's New in Version 0.8.2?</h3>
   10.15          
   10.16          <p>