Technology is the core SPI. Allows us to do simple mock testing of changes in an array.
1.1 --- a/json/src/main/java/net/java/html/json/Context.java Fri Apr 19 11:55:34 2013 +0200
1.2 +++ b/json/src/main/java/net/java/html/json/Context.java Fri Apr 19 15:35:39 2013 +0200
1.3 @@ -20,6 +20,9 @@
1.4 */
1.5 package net.java.html.json;
1.6
1.7 +import org.apidesign.html.json.impl.ContextAccessor;
1.8 +import org.apidesign.html.json.spi.Technology;
1.9 +
1.10 /** Represents context where the {@link Model} and other objects
1.11 * operate in. The context is usually a particular HTML page in a browser.
1.12 * The context is also associated with the actual HTML rendering technology
1.13 @@ -30,11 +33,38 @@
1.14 * @author Jaroslav Tulach <jtulach@netbeans.org>
1.15 */
1.16 public final class Context {
1.17 - private Context() {
1.18 + private final Technology<?> t;
1.19 +
1.20 + private Context(Technology<?> t) {
1.21 + t.getClass();
1.22 + this.t = t;
1.23 }
1.24 -
1.25 + static {
1.26 + new ContextAccessor() {
1.27 + @Override
1.28 + protected Context newContext(Technology<?> t) {
1.29 + return new Context(t);
1.30 + }
1.31 +
1.32 + @Override
1.33 + protected Technology<?> technology(Context c) {
1.34 + return c.t;
1.35 + }
1.36 + };
1.37 + }
1.38 /** Dummy context without binding to any real browser or technology.
1.39 * Useful for simple unit testing of behavior of model classes.
1.40 */
1.41 - public static final Context EMPTY = new Context();
1.42 + public static final Context EMPTY = new Context(new EmptyTech());
1.43 +
1.44 + private static final class EmptyTech implements Technology<Object> {
1.45 + @Override
1.46 + public Object wrapModel(Object model) {
1.47 + return model;
1.48 + }
1.49 +
1.50 + @Override
1.51 + public void valueHasMutated(Object data, String propertyName) {
1.52 + }
1.53 + }
1.54 }
2.1 --- a/json/src/main/java/org/apidesign/html/json/impl/Bindings.java Fri Apr 19 11:55:34 2013 +0200
2.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/Bindings.java Fri Apr 19 15:35:39 2013 +0200
2.3 @@ -21,21 +21,41 @@
2.4 package org.apidesign.html.json.impl;
2.5
2.6 import net.java.html.json.Context;
2.7 +import org.apidesign.html.json.spi.Technology;
2.8
2.9 /**
2.10 *
2.11 * @author Jaroslav Tulach <jtulach@netbeans.org>
2.12 */
2.13 -public final class Bindings {
2.14 - public static Bindings apply(Context c, Object model, String[] propsAndGetters, String[] functions) {
2.15 - return null;
2.16 +public final class Bindings<Data> {
2.17 + private final Data data;
2.18 + private final Technology<Data> bp;
2.19 +
2.20 + public Bindings(Data data, Technology<Data> bp) {
2.21 + this.data = data;
2.22 + this.bp = bp;
2.23 }
2.24
2.25 + public static Bindings<?> apply(Context c, Object model, String[] propsAndGetters, String[] functions) {
2.26 + Technology<?> bp = ContextAccessor.findTechnology(c);
2.27 + return apply(bp, model, propsAndGetters, functions);
2.28 + }
2.29 +
2.30 + private static <Data> Bindings<Data> apply(
2.31 + Technology<Data> bp, Object model,
2.32 + String[] propsAndGetters, String[] functions
2.33 + ) {
2.34 + Data d = bp.wrapModel(model);
2.35 +
2.36 + return new Bindings<>(d, bp);
2.37 + }
2.38 +
2.39 +
2.40 public Object koData() {
2.41 return this;
2.42 }
2.43
2.44 public void valueHasMutated(String firstName) {
2.45 - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
2.46 + bp.valueHasMutated(data, firstName);
2.47 }
2.48 }
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ContextAccessor.java Fri Apr 19 15:35:39 2013 +0200
3.3 @@ -0,0 +1,35 @@
3.4 +package org.apidesign.html.json.impl;
3.5 +
3.6 +import net.java.html.json.Context;
3.7 +import org.apidesign.html.json.spi.ContextBuilder;
3.8 +import org.apidesign.html.json.spi.Technology;
3.9 +
3.10 +/** Internal communication between API (e.g. {@link Context}), SPI
3.11 + * (e.g. {@link ContextBuilder}) and the implementation package.
3.12 + *
3.13 + * @author Jaroslav Tulach <jtulach@netbeans.org>
3.14 + */
3.15 +public abstract class ContextAccessor {
3.16 + private static ContextAccessor DEFAULT;
3.17 + static {
3.18 + // run initializers
3.19 + Context.class.getMethods();
3.20 + }
3.21 +
3.22 + protected ContextAccessor() {
3.23 + if (DEFAULT != null) throw new IllegalStateException();
3.24 + DEFAULT = this;
3.25 + }
3.26 +
3.27 + protected abstract Context newContext(Technology<?> t);
3.28 + protected abstract Technology<?> technology(Context c);
3.29 +
3.30 +
3.31 + public static Context create(Technology<?> t) {
3.32 + return DEFAULT.newContext(t);
3.33 + }
3.34 +
3.35 + static Technology<?> findTechnology(Context c) {
3.36 + return DEFAULT.technology(c);
3.37 + }
3.38 +}
4.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Fri Apr 19 11:55:34 2013 +0200
4.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Fri Apr 19 15:35:39 2013 +0200
4.3 @@ -323,7 +323,7 @@
4.4
4.5 w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n");
4.6 w.write(" if (locked) throw new IllegalStateException();\n");
4.7 - w.write(" prop_" + p.name() + ".assign(ko);\n");
4.8 + w.write(" prop_" + p.name() + ".assign(this.intKnckt());\n");
4.9 w.write(" return prop_" + p.name() + ";\n");
4.10 w.write("}\n");
4.11 } else {
5.1 --- a/json/src/main/java/org/apidesign/html/json/spi/ContextBuilder.java Fri Apr 19 11:55:34 2013 +0200
5.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/ContextBuilder.java Fri Apr 19 15:35:39 2013 +0200
5.3 @@ -21,6 +21,7 @@
5.4 package org.apidesign.html.json.spi;
5.5
5.6 import net.java.html.json.Context;
5.7 +import org.apidesign.html.json.impl.ContextAccessor;
5.8
5.9 /** Support for providers of new {@link Context}. Providers of different
5.10 * technologies should be of particular interest in this class. End users
5.11 @@ -30,18 +31,35 @@
5.12 * @author Jaroslav Tulach <jtulach@netbeans.org>
5.13 */
5.14 public final class ContextBuilder {
5.15 + private Technology<?> t;
5.16 +
5.17 private ContextBuilder() {
5.18 }
5.19
5.20 + /** Creates new, empty builder for creation of {@link Context}. At the
5.21 + * end call the {@link #build()} method to generate the context.
5.22 + *
5.23 + * @return new instance of the builder
5.24 + */
5.25 public static ContextBuilder create() {
5.26 return new ContextBuilder();
5.27 }
5.28
5.29 - public ContextBuilder with() {
5.30 + /** Provides technology for the context
5.31 + * @param technology
5.32 + * @return this
5.33 + */
5.34 + public ContextBuilder withTechnology(Technology<?> technology) {
5.35 + this.t = technology;
5.36 return this;
5.37 }
5.38
5.39 + /** Generates context based on values previously inserted into
5.40 + * this builder.
5.41 + *
5.42 + * @return new, immutable instance of {@link Context}
5.43 + */
5.44 public Context build() {
5.45 - return Context.EMPTY;
5.46 + return ContextAccessor.create(t);
5.47 }
5.48 }
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Technology.java Fri Apr 19 15:35:39 2013 +0200
6.3 @@ -0,0 +1,29 @@
6.4 +package org.apidesign.html.json.spi;
6.5 +
6.6 +import net.java.html.json.Model;
6.7 +
6.8 +/** An implementation of a binding between model classes (see {@link Model})
6.9 + * and particular technology like <a href="http://knockoutjs.com">knockout.js</a>
6.10 + * in a browser window, etc.
6.11 + *
6.12 + * @author Jaroslav Tulach <jtulach@netbeans.org>
6.13 + */
6.14 +public interface Technology<Data> {
6.15 + /** Creates an object to wrap the provided model object. The model
6.16 + * has previously been generated by annotation processor associated
6.17 + * with {@link Model} annotation.
6.18 + *
6.19 + * @param model the model generated from {@link Model}
6.20 + * @return
6.21 + */
6.22 + public Data wrapModel(Object model);
6.23 +
6.24 + /** Model for given data has changed its value. The technology is
6.25 + * supposed to update its state (for example DOM nodes associated
6.26 + * with the model). The update usually happens asynchronously.
6.27 + *
6.28 + * @param data technology's own representation of the model
6.29 + * @param propertyName name of the model property that changed
6.30 + */
6.31 + public void valueHasMutated(Data data, String propertyName);
6.32 +}
7.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java Fri Apr 19 11:55:34 2013 +0200
7.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java Fri Apr 19 15:35:39 2013 +0200
7.3 @@ -25,6 +25,8 @@
7.4 import java.util.Iterator;
7.5 import java.util.List;
7.6 import java.util.ListIterator;
7.7 +import org.apidesign.html.json.spi.ContextBuilder;
7.8 +import org.apidesign.html.json.spi.Technology;
7.9 import static org.testng.Assert.*;
7.10 import org.testng.annotations.BeforeMethod;
7.11 import org.testng.annotations.Test;
7.12 @@ -66,13 +68,24 @@
7.13 model.getNames().add("Jarda");
7.14 assertEquals(model.getNames().size(), 1, "One element");
7.15 }
7.16 -/*
7.17 +
7.18 @Test public void arrayChangesNotified() {
7.19 - MockKnockout my = new MockKnockout();
7.20 - MockKnockout.next = my;
7.21 -
7.22 - model.applyBindings();
7.23 -
7.24 + class My implements Technology<Object> {
7.25 + private List<String> mutated = new ArrayList<>();
7.26 +
7.27 + @Override
7.28 + public Object wrapModel(Object model) {
7.29 + return this;
7.30 + }
7.31 +
7.32 + @Override
7.33 + public void valueHasMutated(Object data, String propertyName) {
7.34 + mutated.add(propertyName);
7.35 + }
7.36 + }
7.37 + My my = new My();
7.38 +
7.39 + model = new Modelik(ContextBuilder.create().withTechnology(my).build());
7.40 model.getNames().add("Hello");
7.41
7.42 assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
7.43 @@ -95,7 +108,7 @@
7.44 assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
7.45 assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
7.46 }
7.47 -
7.48 +/*
7.49 @Test public void autoboxedArray() {
7.50 MockKnockout my = new MockKnockout();
7.51 MockKnockout.next = my;