Technology is the core SPI. Allows us to do simple mock testing of changes in an array.
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 19 Apr 2013 15:35:39 +0200
changeset 68f7eb5d6d47a
parent 5 e9d240f0590d
child 7 e7718a31c6d2
Technology is the core SPI. Allows us to do simple mock testing of changes in an array.
json/src/main/java/net/java/html/json/Context.java
json/src/main/java/org/apidesign/html/json/impl/Bindings.java
json/src/main/java/org/apidesign/html/json/impl/ContextAccessor.java
json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java
json/src/main/java/org/apidesign/html/json/spi/ContextBuilder.java
json/src/main/java/org/apidesign/html/json/spi/Technology.java
json/src/test/java/net/java/html/json/ModelTest.java
     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;