Polishing function bindings
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 22 Apr 2013 22:30:30 +0200
changeset 2021b5d931f4c1
parent 19 744b2ae904f8
child 21 4a593b351de2
Polishing function bindings
json/src/main/java/org/apidesign/html/json/impl/Bindings.java
json/src/main/java/org/apidesign/html/json/impl/Callback.java
json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java
json/src/main/java/org/apidesign/html/json/impl/PropertyBindingAccessor.java
json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java
json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java
json/src/test/java/net/java/html/json/MapModelTest.java
     1.1 --- a/json/src/main/java/org/apidesign/html/json/impl/Bindings.java	Mon Apr 22 21:36:07 2013 +0200
     1.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/Bindings.java	Mon Apr 22 22:30:30 2013 +0200
     1.3 @@ -24,6 +24,7 @@
     1.4  import java.util.List;
     1.5  import org.apidesign.html.json.spi.PropertyBinding;
     1.6  import net.java.html.json.Context;
     1.7 +import org.apidesign.html.json.impl.PropertyBindingAccessor.FBData;
     1.8  import org.apidesign.html.json.impl.PropertyBindingAccessor.PBData;
     1.9  import org.apidesign.html.json.spi.FunctionBinding;
    1.10  import org.apidesign.html.json.spi.Technology;
    1.11 @@ -46,31 +47,22 @@
    1.12          bp.bind(pb, model, data);
    1.13          return pb;
    1.14      }
    1.15 +
    1.16 +    public <M> FunctionBinding registerFunction(String name, M model, Callback<M> access) {
    1.17 +        FunctionBinding fb = PropertyBindingAccessor.createFunction(new FBData<>(name, model, access));
    1.18 +        bp.expose(fb, model, data);
    1.19 +        return fb;
    1.20 +    }
    1.21      
    1.22 -    public static Bindings<?> apply(Context c, Object model, String[] functions) {
    1.23 +    public static Bindings<?> apply(Context c, Object model) {
    1.24          Technology<?> bp = ContextAccessor.findTechnology(c);
    1.25 -        return apply(bp, model, null, functions);
    1.26 +        return apply(bp, model);
    1.27      }
    1.28      
    1.29      private static <Data> Bindings<Data> apply(
    1.30 -        Technology<Data> bp, Object model, 
    1.31 -        PropertyBinding[] propBindings, String[] methodsAndSignatures
    1.32 +        Technology<Data> bp, Object model
    1.33      ) {
    1.34          Data d = bp.wrapModel(model);
    1.35 -        
    1.36 -        if (propBindings != null) {
    1.37 -            for (int i = 0; i < propBindings.length; i++) {
    1.38 -                PropertyBinding pb = propBindings[i];
    1.39 -                bp.bind(pb, model, d);
    1.40 -            }
    1.41 -        }
    1.42 -        
    1.43 -        List<String> arr = Arrays.asList(methodsAndSignatures);
    1.44 -        for (int i = 0; i < methodsAndSignatures.length; i += 2) {
    1.45 -            FunctionBinding fb = PropertyBindingAccessor.createFunction(arr.subList(i, i + 2));
    1.46 -            bp.expose(fb, model, d);
    1.47 -        }
    1.48 -        
    1.49          return new Bindings<>(d, bp);
    1.50      }
    1.51      
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/Callback.java	Mon Apr 22 22:30:30 2013 +0200
     2.3 @@ -0,0 +1,30 @@
     2.4 +/**
     2.5 + * HTML via Java(tm) Language Bindings
     2.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     2.7 + *
     2.8 + * This program is free software: you can redistribute it and/or modify
     2.9 + * it under the terms of the GNU General Public License as published by
    2.10 + * the Free Software Foundation, version 2 of the License.
    2.11 + *
    2.12 + * This program is distributed in the hope that it will be useful,
    2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.15 + * GNU General Public License for more details. apidesign.org
    2.16 + * designates this particular file as subject to the
    2.17 + * "Classpath" exception as provided by apidesign.org
    2.18 + * in the License file that accompanied this code.
    2.19 + *
    2.20 + * You should have received a copy of the GNU General Public License
    2.21 + * along with this program. Look for COPYING file in the top folder.
    2.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    2.23 + */
    2.24 +
    2.25 +package org.apidesign.html.json.impl;
    2.26 +
    2.27 +/**
    2.28 + *
    2.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.30 + */
    2.31 +public interface Callback<Data> {
    2.32 +    public void call(Data model, Object data, Object ev);
    2.33 +}
     3.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Mon Apr 22 21:36:07 2013 +0200
     3.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Mon Apr 22 22:30:30 2013 +0200
     3.3 @@ -197,16 +197,19 @@
     3.4                  w.append("  };\n");
     3.5                  w.append("  private org.apidesign.html.json.impl.Bindings intKnckt() {\n");
     3.6                  w.append("    if (ko != null) return ko;\n");
     3.7 -                w.append("    ko = org.apidesign.html.json.impl.Bindings.apply(context, this, ");
     3.8 -                writeStringArray(functions, w);
     3.9 -                w.append("    );\n");
    3.10 +                w.append("    ko = org.apidesign.html.json.impl.Bindings.apply(context, this);\n");
    3.11                  for (int i = 0; i < propsGetSet.size(); i += 5) {
    3.12                      w.append("    ko.registerProperty(\"").append(propsGetSet.get(i)).append("\", this, new P(");
    3.13                      w.append((i / 5) + "), " + (propsGetSet.get(i + 2) == null) + ");\n");
    3.14                  }
    3.15 +                for (int i = 0; i < functions.size(); i += 2) {
    3.16 +                    w.append("    ko.registerFunction(\"").append(functions.get(i)).append("\", this, new P(");
    3.17 +                    w.append((i / 2) + "));\n");
    3.18 +                }
    3.19                  w.append("    return ko;\n");
    3.20                  w.append("  };\n");
    3.21 -                w.append("  private static final class P implements org.apidesign.html.json.impl.SetAndGet<" + className + "> {\n");
    3.22 +                w.append("  private static final class P implements org.apidesign.html.json.impl.SetAndGet<" + className + ">,\n");
    3.23 +                w.append("  org.apidesign.html.json.impl.Callback<" + className + "> {\n");
    3.24                  w.append("    private final int type;\n");
    3.25                  w.append("    P(int t) { type = t; };\n");
    3.26                  w.append("    public void setValue(" + className + " data, Object value) {\n");
    3.27 @@ -231,6 +234,15 @@
    3.28                  w.append("      }\n");
    3.29                  w.append("      throw new UnsupportedOperationException();\n");
    3.30                  w.append("    }\n");
    3.31 +                w.append("    public void call(" + className + " model, Object data, Object ev) {\n");
    3.32 +                w.append("      switch (type) {\n");
    3.33 +                for (int i = 0; i < functions.size(); i += 2) {
    3.34 +                    final String name = functions.get(i);
    3.35 +                    w.append("        case " + (i / 2) + ": model." + name + "(data, ev); return;\n");
    3.36 +                }
    3.37 +                w.append("      }\n");
    3.38 +                w.append("      throw new UnsupportedOperationException();\n");
    3.39 +                w.append("    }\n");
    3.40                  w.append("  }\n");
    3.41                  w.append("  ").append(className).append("(Object json) {\n");
    3.42                  int values = 0;
     4.1 --- a/json/src/main/java/org/apidesign/html/json/impl/PropertyBindingAccessor.java	Mon Apr 22 21:36:07 2013 +0200
     4.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/PropertyBindingAccessor.java	Mon Apr 22 22:30:30 2013 +0200
     4.3 @@ -48,20 +48,20 @@
     4.4      }
     4.5  
     4.6      protected abstract <M> PropertyBinding newBinding(PBData<M> d);
     4.7 -    protected abstract FunctionBinding newFunction(List<String> params);
     4.8 +    protected abstract <M> FunctionBinding newFunction(FBData<M> d);
     4.9      
    4.10      static <M> PropertyBinding create(PBData<M> d) {
    4.11          return DEFAULT.newBinding(d);
    4.12      }
    4.13 -    static FunctionBinding createFunction(List<String> subList) {
    4.14 -        return DEFAULT.newFunction(subList);
    4.15 +    static <M> FunctionBinding createFunction(FBData<M> d) {
    4.16 +        return DEFAULT.newFunction(d);
    4.17      }
    4.18  
    4.19      public static final class PBData<M> {
    4.20          public final String name;
    4.21 -        public final M model;
    4.22 -        public final SetAndGet<M> access;
    4.23          public final boolean readOnly;
    4.24 +        private final M model;
    4.25 +        private final SetAndGet<M> access;
    4.26  
    4.27          public PBData(String name, M model, SetAndGet<M> access, boolean readOnly) {
    4.28              this.name = name;
    4.29 @@ -81,5 +81,22 @@
    4.30          public boolean isReadOnly() {
    4.31              return readOnly;
    4.32          }
    4.33 -    }
    4.34 +    } // end of PBData
    4.35 +    
    4.36 +    public static final class FBData<M> {
    4.37 +        public final String name;
    4.38 +        private final M model;
    4.39 +        private final Callback<M> access;
    4.40 +
    4.41 +        public FBData(String name, M model, Callback<M> access) {
    4.42 +            this.name = name;
    4.43 +            this.model = model;
    4.44 +            this.access = access;
    4.45 +        }
    4.46 +
    4.47 +
    4.48 +        public void call(Object data, Object ev) {
    4.49 +            access.call(model, data, ev);
    4.50 +        }
    4.51 +    } // end of FBData
    4.52  }
     5.1 --- a/json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java	Mon Apr 22 21:36:07 2013 +0200
     5.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java	Mon Apr 22 22:30:30 2013 +0200
     5.3 @@ -20,9 +20,9 @@
     5.4   */
     5.5  package org.apidesign.html.json.spi;
     5.6  
     5.7 -import java.util.List;
     5.8  import net.java.html.json.Function;
     5.9  import net.java.html.json.Model;
    5.10 +import org.apidesign.html.json.impl.PropertyBindingAccessor.FBData;
    5.11  
    5.12  /** Describes a function provided by the {@link Model} and 
    5.13   * annotated by {@link Function} annotation.
    5.14 @@ -30,13 +30,23 @@
    5.15   * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.16   */
    5.17  public final class FunctionBinding {
    5.18 -    private final List<String> params;
    5.19 +    private final FBData<?> fb;
    5.20      
    5.21 -    FunctionBinding(List<String> p) {
    5.22 -        this.params = p;
    5.23 +    FunctionBinding(FBData<?> fb) {
    5.24 +        this.fb = fb;
    5.25      }
    5.26  
    5.27      public String getFunctionName() {
    5.28 -        return params.get(0);
    5.29 +        return fb.name;
    5.30 +    }
    5.31 +    
    5.32 +    /** Calls the function provided data associated with current element,
    5.33 +     * as well as information about the event that triggered the event.
    5.34 +     * 
    5.35 +     * @param data data associated with selected element
    5.36 +     * @param ev event (with additional properties) that triggered the event
    5.37 +     */
    5.38 +    public void call(Object data, Object ev) {
    5.39 +        fb.call(data, ev);
    5.40      }
    5.41  }
     6.1 --- a/json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java	Mon Apr 22 21:36:07 2013 +0200
     6.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java	Mon Apr 22 22:30:30 2013 +0200
     6.3 @@ -45,8 +45,8 @@
     6.4              }
     6.5  
     6.6              @Override
     6.7 -            protected FunctionBinding newFunction(List<String> params) {
     6.8 -                return new FunctionBinding(params);
     6.9 +            protected <M> FunctionBinding newFunction(FBData<M> d) {
    6.10 +                return new FunctionBinding(d);
    6.11              }
    6.12          };
    6.13      }
     7.1 --- a/json/src/test/java/net/java/html/json/MapModelTest.java	Mon Apr 22 21:36:07 2013 +0200
     7.2 +++ b/json/src/test/java/net/java/html/json/MapModelTest.java	Mon Apr 22 22:30:30 2013 +0200
     7.3 @@ -21,7 +21,6 @@
     7.4  package net.java.html.json;
     7.5  
     7.6  import java.lang.reflect.InvocationTargetException;
     7.7 -import java.lang.reflect.Method;
     7.8  import java.util.HashMap;
     7.9  import java.util.Map;
    7.10  import org.apidesign.html.json.impl.WrapperObject;
    7.11 @@ -85,16 +84,28 @@
    7.12          Map m = (Map)WrapperObject.find(p);
    7.13          Object o = m.get("changeSex");
    7.14          assertNotNull(o, "Function registered in the model");
    7.15 -
    7.16 -        // TBD: invoke
    7.17 +        assertEquals(o.getClass(), One.class);
    7.18 +        
    7.19 +        One one = (One)o;
    7.20 +        assertNotNull(one.fb, "Function binding specified");
    7.21 +        
    7.22 +        one.fb.call("Hello", new Object());
    7.23 +        
    7.24 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
    7.25      }
    7.26  
    7.27      private static final class One {
    7.28          int changes;
    7.29          final PropertyBinding pb;
    7.30 +        final FunctionBinding fb;
    7.31      
    7.32          One(Object m, PropertyBinding pb) throws NoSuchMethodException {
    7.33              this.pb = pb;
    7.34 +            this.fb = null;
    7.35 +        }
    7.36 +        One(Object m, FunctionBinding fb) throws NoSuchMethodException {
    7.37 +            this.pb = null;
    7.38 +            this.fb = fb;
    7.39          }
    7.40          
    7.41          Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    7.42 @@ -134,7 +145,7 @@
    7.43          @Override
    7.44          public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
    7.45              try {
    7.46 -                data.put(fb.getFunctionName(), new One(model, null));
    7.47 +                data.put(fb.getFunctionName(), new One(model, fb));
    7.48              } catch (NoSuchMethodException ex) {
    7.49                  throw new IllegalStateException(ex);
    7.50              }