1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Tue Jul 08 07:30:45 2014 +0200
1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Tue Jul 08 10:05:27 2014 +0200
1.3 @@ -43,6 +43,8 @@
1.4 package net.java.html.json.tests;
1.5
1.6 import java.util.List;
1.7 +import java.util.Timer;
1.8 +import java.util.TimerTask;
1.9 import net.java.html.BrwsrCtx;
1.10 import net.java.html.json.ComputedProperty;
1.11 import net.java.html.json.Function;
1.12 @@ -64,6 +66,7 @@
1.13 @Property(name="latitude", type=double.class)
1.14 })
1.15 public final class KnockoutTest {
1.16 + private KnockoutModel js;
1.17
1.18 @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
1.19 Object exp = Utils.exposeHTML(KnockoutTest.class,
1.20 @@ -137,6 +140,38 @@
1.21 }
1.22 }
1.23
1.24 + @KOTest public void modifyValueAssertAsyncChangeInModel() throws Exception {
1.25 + if (js == null) {
1.26 + Utils.exposeHTML(KnockoutTest.class,
1.27 + "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
1.28 + "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
1.29 + "<button id=\"hello\">Say Hello!</button>\n"
1.30 + );
1.31 +
1.32 + js = Models.bind(new KnockoutModel(), newContext());
1.33 + js.setName("Kukuc");
1.34 + js.applyBindings();
1.35 +
1.36 + String v = getSetInput(null);
1.37 + assert "Kukuc".equals(v) : "Value is really kukuc: " + v;
1.38 +
1.39 + Timer t = new Timer("Set to Jardo");
1.40 + t.schedule(new TimerTask() {
1.41 + @Override
1.42 + public void run() {
1.43 + js.setName("Jardo");
1.44 + }
1.45 + }, 1);
1.46 + }
1.47 +
1.48 + String v = getSetInput(null);
1.49 + if (!"Jardo".equals(v)) {
1.50 + throw new InterruptedException();
1.51 + }
1.52 +
1.53 + Utils.exposeHTML(KnockoutTest.class, "");
1.54 + }
1.55 +
1.56 private static String getSetInput(String value) throws Exception {
1.57 String s = "var value = arguments[0];\n"
1.58 + "var n = window.document.getElementById('input'); \n "
1.59 @@ -185,6 +220,45 @@
1.60 }
1.61 }
1.62
1.63 + @KOTest public void displayContentOfAsyncArray() throws Exception {
1.64 + if (js == null) {
1.65 + Utils.exposeHTML(KnockoutTest.class,
1.66 + "<ul id='ul' data-bind='foreach: results'>\n"
1.67 + + " <li data-bind='text: $data, click: $root.call'/>\n"
1.68 + + "</ul>\n"
1.69 + );
1.70 + js = Models.bind(new KnockoutModel(), newContext());
1.71 + js.getResults().add("Ahoj");
1.72 + js.applyBindings();
1.73 +
1.74 + int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.75 + assert cnt == 1 : "One child, but was " + cnt;
1.76 +
1.77 + Timer t = new Timer("add to array");
1.78 + t.schedule(new TimerTask() {
1.79 + @Override
1.80 + public void run() {
1.81 + js.getResults().add("Hi");
1.82 + }
1.83 + }, 1);
1.84 + }
1.85 +
1.86 +
1.87 + int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.88 + if (cnt != 2) {
1.89 + throw new InterruptedException();
1.90 + }
1.91 +
1.92 + try {
1.93 + triggerChildClick("ul", 1);
1.94 +
1.95 + assert 1 == js.getCallbackCount() : "One callback " + js.getCallbackCount();
1.96 + assert "Hi".equals(js.getName()) : "We got callback from 2nd child " + js.getName();
1.97 + } finally {
1.98 + Utils.exposeHTML(KnockoutTest.class, "");
1.99 + }
1.100 + }
1.101 +
1.102 @KOTest public void displayContentOfComputedArray() throws Exception {
1.103 Object exp = Utils.exposeHTML(KnockoutTest.class,
1.104 "<ul id='ul' data-bind='foreach: bothNames'>\n"
2.1 --- a/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java Tue Jul 08 07:30:45 2014 +0200
2.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java Tue Jul 08 10:05:27 2014 +0200
2.3 @@ -66,19 +66,7 @@
2.4
2.5 @KOTest public void asyncOperation() throws InterruptedException {
2.6 if (js == null) {
2.7 - try {
2.8 - // needs full JVM (not Bck2Brwsr VM) to run
2.9 - Class<?> thread = Class.forName("java.lang.Thread");
2.10 - Thread t = new Thread("Empty");
2.11 - // following operations are supposed to generate SecurityException
2.12 - // on bck2brwsr VM
2.13 - t.setName("Different");
2.14 - t.setDaemon(false);
2.15 - t.interrupt();
2.16 - t.start();
2.17 - } catch (ClassNotFoundException ex) {
2.18 - return;
2.19 - } catch (SecurityException ex) {
2.20 + if (Utils.skipIfNoFullJDK()) {
2.21 return;
2.22 }
2.23
2.24 @@ -104,7 +92,4 @@
2.25 assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
2.26 assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
2.27 }
2.28 -
2.29 -
2.30 -
2.31 }
3.1 --- a/json-tck/src/main/java/net/java/html/json/tests/Utils.java Tue Jul 08 07:30:45 2014 +0200
3.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/Utils.java Tue Jul 08 10:05:27 2014 +0200
3.3 @@ -56,6 +56,22 @@
3.4 public final class Utils {
3.5 private static KnockoutTCK instantiatedTCK;
3.6
3.7 + static boolean skipIfNoFullJDK() {
3.8 + try {
3.9 + Class<?> thread = Class.forName("java.lang.Thread");
3.10 + Thread t = new Thread("Empty");
3.11 + t.setName("Different");
3.12 + t.setDaemon(false);
3.13 + t.interrupt();
3.14 + t.start();
3.15 + } catch (ClassNotFoundException ex) {
3.16 + return true;
3.17 + } catch (SecurityException ex) {
3.18 + return true;
3.19 + }
3.20 + return false;
3.21 + }
3.22 +
3.23 private Utils() {
3.24 }
3.25
4.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java Tue Jul 08 07:30:45 2014 +0200
4.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java Tue Jul 08 10:05:27 2014 +0200
4.3 @@ -110,14 +110,22 @@
4.4 }
4.5
4.6 /** Whenever model changes a property. It should notify the
4.7 - * associated technology by calling this method.
4.8 + * associated technology by calling this method.
4.9 + * Since 0.8.3: This method may be called by any thread - it reschedules
4.10 + * its actual execution into appropriate one by using
4.11 + * {@link BrwsrCtx#execute(java.lang.Runnable)}.
4.12 *
4.13 * @param propName name of the changed property
4.14 */
4.15 - public void valueHasMutated(String propName) {
4.16 - if (ko != null) {
4.17 - ko.valueHasMutated(propName, null, null);
4.18 - }
4.19 + public void valueHasMutated(final String propName) {
4.20 + context.execute(new Runnable() {
4.21 + @Override
4.22 + public void run() {
4.23 + if (ko != null) {
4.24 + ko.valueHasMutated(propName, null, null);
4.25 + }
4.26 + }
4.27 + });
4.28 }
4.29
4.30 /** Whenever model changes a propertyit should notify the
4.31 @@ -125,16 +133,26 @@
4.32 * (if the new value is known and different to the old one) or
4.33 * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
4.34 * method.
4.35 + * Since 0.8.3: This method may be called by any thread - it reschedules
4.36 + * its actual execution into appropriate one by using
4.37 + * {@link BrwsrCtx#execute(java.lang.Runnable)}.
4.38 *
4.39 * @param propName name of the changed property
4.40 * @param oldValue provides previous value of the property
4.41 * @param newValue provides new value of the property
4.42 * @since 0.7.6
4.43 */
4.44 - public void valueHasMutated(String propName, Object oldValue, Object newValue) {
4.45 - if (ko != null) {
4.46 - ko.valueHasMutated(propName, oldValue, newValue);
4.47 - }
4.48 + public void valueHasMutated(
4.49 + final String propName, final Object oldValue, final Object newValue
4.50 + ) {
4.51 + context.execute(new Runnable() {
4.52 + @Override
4.53 + public void run() {
4.54 + if (ko != null) {
4.55 + ko.valueHasMutated(propName, oldValue, newValue);
4.56 + }
4.57 + }
4.58 + });
4.59 }
4.60
4.61 /** Initializes the associated model in the current {@link #getContext() context}.
5.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Tue Jul 08 07:30:45 2014 +0200
5.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Tue Jul 08 10:05:27 2014 +0200
5.3 @@ -174,16 +174,21 @@
5.4 }
5.5
5.6 private void notifyChange() {
5.7 - Bindings m = PropertyBindingAccessor.getBindings(proto, false);
5.8 - if (m != null) {
5.9 - m.valueHasMutated(name, null, null);
5.10 - for (String dependant : deps) {
5.11 - m.valueHasMutated(dependant, null, null);
5.12 + proto.getContext().execute(new Runnable() {
5.13 + @Override
5.14 + public void run() {
5.15 + Bindings m = PropertyBindingAccessor.getBindings(proto, false);
5.16 + if (m != null) {
5.17 + m.valueHasMutated(name, null, null);
5.18 + for (String dependant : deps) {
5.19 + m.valueHasMutated(dependant, null, null);
5.20 + }
5.21 + if (index >= 0) {
5.22 + PropertyBindingAccessor.notifyProtoChange(proto, index);
5.23 + }
5.24 + }
5.25 }
5.26 - if (index >= 0) {
5.27 - PropertyBindingAccessor.notifyProtoChange(proto, index);
5.28 - }
5.29 - }
5.30 + });
5.31 }
5.32
5.33 @Override
6.1 --- a/src/main/javadoc/overview.html Tue Jul 08 07:30:45 2014 +0200
6.2 +++ b/src/main/javadoc/overview.html Tue Jul 08 10:05:27 2014 +0200
6.3 @@ -75,6 +75,13 @@
6.4 yet the application code can be written in Java.
6.5 </p>
6.6
6.7 + <h3>What's New in Version 0.8.3?</h3>
6.8 +
6.9 + <p>
6.10 + Setters or array properties on classes generated by {@link net.java.html.json.Model}
6.11 + annotation can be accessed from any thread.
6.12 + </p>
6.13 +
6.14 <h3>What's New in Version 0.8.2?</h3>
6.15
6.16 <p>