Bringing in latest improvements in default branch ios
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 09 Aug 2013 16:57:52 +0200
branchios
changeset 23703c9f1fb5ad4
parent 228 0f34c04bec7d
parent 236 c1a4be1cd7e8
Bringing in latest improvements in default branch
     1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Thu Aug 08 13:30:23 2013 +0200
     1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Fri Aug 09 16:57:52 2013 +0200
     1.3 @@ -98,6 +98,7 @@
     1.4              js = Models.bind(new JSONik(), newContext());
     1.5              js.applyBindings();
     1.6  
     1.7 +            js.setFetched(null);
     1.8              js.fetch(url);
     1.9          }
    1.10      
    1.11 @@ -128,6 +129,7 @@
    1.12              js = Models.bind(new JSONik(), newContext());
    1.13              js.applyBindings();
    1.14  
    1.15 +            js.setFetched(null);
    1.16              js.fetchViaJSONP(url);
    1.17          }
    1.18      
    1.19 @@ -211,6 +213,7 @@
    1.20              js = Models.bind(new JSONik(), newContext());
    1.21              js.applyBindings();
    1.22  
    1.23 +            js.setFetched(null);
    1.24              js.fetchArray(url);
    1.25          }
    1.26          
    1.27 @@ -232,6 +235,7 @@
    1.28              js = Models.bind(new JSONik(), newContext());
    1.29              js.applyBindings();
    1.30          
    1.31 +            js.setFetched(null);
    1.32              js.fetch(url);
    1.33          }
    1.34          
    1.35 @@ -328,6 +332,8 @@
    1.36              );
    1.37              js = Models.bind(new JSONik(), newContext());
    1.38              js.applyBindings();
    1.39 +            js.setFetched(null);
    1.40 +            
    1.41              js.fetchArray(url);
    1.42          }
    1.43          
     2.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Thu Aug 08 13:30:23 2013 +0200
     2.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Fri Aug 09 16:57:52 2013 +0200
     2.3 @@ -161,6 +161,73 @@
     2.4              Utils.exposeHTML(KnockoutTest.class, "");
     2.5          }
     2.6      }
     2.7 +    
     2.8 +    @KOTest public void displayContentOfComputedArray() throws Exception {
     2.9 +        Object exp = Utils.exposeHTML(KnockoutTest.class, 
    2.10 +            "<ul id='ul' data-bind='foreach: bothNames'>\n"
    2.11 +            + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
    2.12 +            + "</ul>\n"
    2.13 +        );
    2.14 +        try {
    2.15 +            Pair m = Models.bind(new Pair("First", "Last", null), newContext());
    2.16 +            m.applyBindings();
    2.17 +
    2.18 +            int cnt = countChildren("ul");
    2.19 +            assert cnt == 2 : "Two children now, but was " + cnt;
    2.20 +
    2.21 +            triggerChildClick("ul", 1);
    2.22 +
    2.23 +            assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
    2.24 +        } finally {
    2.25 +            Utils.exposeHTML(KnockoutTest.class, "");
    2.26 +        }
    2.27 +    }
    2.28 +    
    2.29 +    @KOTest public void displayContentOfComputedArrayOnASubpair() throws Exception {
    2.30 +        Object exp = Utils.exposeHTML(KnockoutTest.class, 
    2.31 +              "<div data-bind='with: next'>\n"
    2.32 +            + "<ul id='ul' data-bind='foreach: bothNames'>\n"
    2.33 +            + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
    2.34 +            + "</ul>"
    2.35 +            + "</div>\n"
    2.36 +        );
    2.37 +        try {
    2.38 +            Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), newContext());
    2.39 +            m.applyBindings();
    2.40 +
    2.41 +            int cnt = countChildren("ul");
    2.42 +            assert cnt == 2 : "Two children now, but was " + cnt;
    2.43 +
    2.44 +            triggerChildClick("ul", 1);
    2.45 +
    2.46 +            assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
    2.47 +        } finally {
    2.48 +            Utils.exposeHTML(KnockoutTest.class, "");
    2.49 +        }
    2.50 +    }
    2.51 +    
    2.52 +    @KOTest public void displayContentOfComputedArrayOnComputedASubpair() throws Exception {
    2.53 +        Object exp = Utils.exposeHTML(KnockoutTest.class, 
    2.54 +              "<div data-bind='with: nextOne'>\n"
    2.55 +            + "<ul id='ul' data-bind='foreach: bothNames'>\n"
    2.56 +            + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
    2.57 +            + "</ul>"
    2.58 +            + "</div>\n"
    2.59 +        );
    2.60 +        try {
    2.61 +            Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), newContext());
    2.62 +            m.applyBindings();
    2.63 +
    2.64 +            int cnt = countChildren("ul");
    2.65 +            assert cnt == 2 : "Two children now, but was " + cnt;
    2.66 +
    2.67 +            triggerChildClick("ul", 1);
    2.68 +
    2.69 +            assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
    2.70 +        } finally {
    2.71 +            Utils.exposeHTML(KnockoutTest.class, "");
    2.72 +        }
    2.73 +    }
    2.74  
    2.75      @KOTest public void checkBoxToBooleanBinding() throws Exception {
    2.76          Object exp = Utils.exposeHTML(KnockoutTest.class, 
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/PairModel.java	Fri Aug 09 16:57:52 2013 +0200
     3.3 @@ -0,0 +1,54 @@
     3.4 +/**
     3.5 + * HTML via Java(tm) Language Bindings
     3.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, version 2 of the License.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details. apidesign.org
    3.16 + * designates this particular file as subject to the
    3.17 + * "Classpath" exception as provided by apidesign.org
    3.18 + * in the License file that accompanied this code.
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License
    3.21 + * along with this program. Look for COPYING file in the top folder.
    3.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    3.23 + */
    3.24 +package net.java.html.json.tests;
    3.25 +
    3.26 +import java.util.Arrays;
    3.27 +import java.util.List;
    3.28 +import net.java.html.json.ComputedProperty;
    3.29 +import net.java.html.json.Function;
    3.30 +import net.java.html.json.Model;
    3.31 +import net.java.html.json.Property;
    3.32 +
    3.33 +/**
    3.34 + *
    3.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.36 + */
    3.37 +@Model(className = "Pair", properties = {
    3.38 +    @Property(name = "firstName", type = String.class),
    3.39 +    @Property(name = "lastName", type = String.class),
    3.40 +    @Property(name = "next", type = Pair.class)
    3.41 +})
    3.42 +class PairModel {
    3.43 +    @ComputedProperty 
    3.44 +    static List<String> bothNames(String firstName, String lastName) {
    3.45 +        return Arrays.asList(firstName, lastName);
    3.46 +    }
    3.47 +    
    3.48 +    @ComputedProperty
    3.49 +    static Pair nextOne(Pair next) {
    3.50 +        return next;
    3.51 +    }
    3.52 +    
    3.53 +    @Function 
    3.54 +    static void assignFirstName(Pair m, String data) {
    3.55 +        m.setFirstName(data);
    3.56 +    }
    3.57 +}
     4.1 --- a/json/src/main/java/net/java/html/json/ComputedProperty.java	Thu Aug 08 13:30:23 2013 +0200
     4.2 +++ b/json/src/main/java/net/java/html/json/ComputedProperty.java	Fri Aug 09 16:57:52 2013 +0200
     4.3 @@ -30,8 +30,15 @@
     4.4   * of {@link Property} as enumerated by {@link Model#properties()}.
     4.5   * <p>
     4.6   * The name of the derived property is the name of the method. The arguments
     4.7 - * of the method define the property names (from {@link Model#properties()} list)
     4.8 - * the value of property depends on. 
     4.9 + * of the method must match names and types of some of the properties 
    4.10 + * from {@link Model#properties()} list. As soon as one of these properties
    4.11 + * changes, the method is called to recompute its new value.
    4.12 + * <p>
    4.13 + * Method's return type defines the type of the derived property. It may be
    4.14 + * any primitive type, {@link String}, {@link Enum enum type} or a 
    4.15 + * type generated by {@link Model @Model} annotation. One may 
    4.16 + * also return an array by returning a list of such (boxed) type
    4.17 + * (for example {@link java.util.List List}&lt;{@link String}&gt; or {@link java.util.List List}&lt;{@link Integer}&gt;).
    4.18   *
    4.19   * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.20   */
     5.1 --- a/json/src/main/java/net/java/html/json/Function.java	Thu Aug 08 13:30:23 2013 +0200
     5.2 +++ b/json/src/main/java/net/java/html/json/Function.java	Fri Aug 09 16:57:52 2013 +0200
     5.3 @@ -27,8 +27,50 @@
     5.4  
     5.5  /** Methods in class annotated by {@link Model} can be 
     5.6   * annotated by this annotation to signal that they should be available
     5.7 - * as functions to users of the model classes.
     5.8 - *
     5.9 + * as functions to users of the model classes. The method
    5.10 + * should be non-private, static and return <code>void</code>.
    5.11 + * It may take up to two arguments. One argument can be the type of
    5.12 + * the associated model class, the other argument can be of any type,
    5.13 + * but has to be named <code>data</code> - this one represents the
    5.14 + * actual data the function was invoked on. Example:
    5.15 + * <pre>
    5.16 + * 
    5.17 + * {@link Model @Model}(className="Names", properties={
    5.18 + *   {@link Property @Property}(name = "selectedName", type=String.class),
    5.19 + *   {@link Property @Property}(name = "names", type=String.class, array = true)
    5.20 + * })
    5.21 + * static class NamesModel {
    5.22 + *   {@link Function @Function} static void <b>nameSelected</b>(Names myModel, String data) {
    5.23 + *     myModel.setSelectedName(data);
    5.24 + *   }
    5.25 + * 
    5.26 + *   static void initialize() {
    5.27 + *     Names pageModel = new Names("---", "Jarda", "Pepa", "Honza", "Jirka", "Tomáš");
    5.28 + *     pageModel.applyBindings();
    5.29 + *   }
    5.30 + * }
    5.31 + * 
    5.32 + * // associated <a href="http://knockoutjs.com/">Knockout</a> HTML page:
    5.33 + * 
    5.34 + * Selected name: &lt;span data-bind="text: selectedName">&lt;/span&gt;
    5.35 + * &lt;ul data-bind="foreach: names"&gt;
    5.36 + *   &lt;li data-bind="text: $data, click: <b>$root.nameSelected</b>">&lt;/li&gt;
    5.37 + * &lt;/ul&gt;
    5.38 + * </pre>
    5.39 + * The above example would render:
    5.40 + * <hr>
    5.41 + * Selected name: <span>---</span>
    5.42 + * <ul>
    5.43 + *   <li>Jarda</li>
    5.44 + *   <li>Pepa</li>
    5.45 + *   <li>Honza</li>
    5.46 + *   <li>Jirka</li>
    5.47 + *   <li>Tomáš</li>
    5.48 + * </ul>
    5.49 + * <hr>
    5.50 + * and after clicking on one of the names the <code>---</code> would be replaced
    5.51 + * by selected name.
    5.52 + * 
    5.53   * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.54   */
    5.55  @Target(ElementType.METHOD)
     6.1 --- a/json/src/main/java/net/java/html/json/Model.java	Thu Aug 08 13:30:23 2013 +0200
     6.2 +++ b/json/src/main/java/net/java/html/json/Model.java	Fri Aug 09 16:57:52 2013 +0200
     6.3 @@ -24,31 +24,85 @@
     6.4  import java.lang.annotation.Retention;
     6.5  import java.lang.annotation.RetentionPolicy;
     6.6  import java.lang.annotation.Target;
     6.7 +import java.net.URL;
     6.8  
     6.9 -/** Defines a model class named {@link #className()} which contains
    6.10 - * properties defined via {@link #properties()}. This class can have methods 
    6.11 - * annotated by {@link ComputedProperty} which define derived
    6.12 - * properties in the model class.
    6.13 +/** Defines a model class that represents a single 
    6.14 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
    6.15 + * named {@link #className()}. The generated class contains
    6.16 + * getters and setters for properties defined via {@link #properties()} and
    6.17 + * getters for other, derived properties defined by annotating methods
    6.18 + * of this class by {@link ComputedProperty}. Each property
    6.19 + * can be of primitive type, an {@link Enum enum type} or (in order to create 
    6.20 + * nested <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> structure)
    6.21 + * of another {@link Model class generated by @Model} annotation. Each property
    6.22 + * can either represent a single value or be an array of its values.
    6.23   * <p>
    6.24 - * The {@link #className() generated class}'s <code>toString</code>
    6.25 + * The {@link #className() generated class}'s <code>toString</code> method
    6.26   * converts the state of the object into 
    6.27 - * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. 
    6.28 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. One can
    6.29 + * use {@link Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream)}
    6.30 + * method to read the JSON text stored in a file or other stream back into the Java model. 
    6.31 + * One can also use {@link OnReceive @OnReceive} annotation to read the model
    6.32 + * asynchronously from a {@link URL}.
    6.33   * <p>
    6.34 - * An example where one defines class <code>Person</code> with three 
    6.35 - * properties (<code>firstName</code>, <code>lastName</code> and
    6.36 + * An example where one defines class <code>Person</code> with four
    6.37 + * properties (<code>firstName</code>, <code>lastName</code>, array of <code>addresses</code> and
    6.38   * <code>fullName</code>) follows:
    6.39   * <pre>
    6.40   * {@link Model @Model}(className="Person", properties={
    6.41   *   {@link Property @Property}(name = "firstName", type=String.class),
    6.42   *   {@link Property @Property}(name = "lastName", type=String.class)
    6.43 + *   {@link Property @Property}(name = "addresses", type=Address.class, array = true)
    6.44   * })
    6.45 - * static class PersonImpl {
    6.46 + * static class PersonModel {
    6.47   *   {@link ComputedProperty @ComputedProperty}
    6.48   *   static String fullName(String firstName, String lastName) {
    6.49   *     return firstName + " " + lastName;
    6.50   *   }
    6.51 + * 
    6.52 + *   {@link Model @Model}(className="Address", properties={
    6.53 + *     {@link Property @Property}(name = "street", type=String.class),
    6.54 + *     {@link Property @Property}(name = "town", type=String.class)
    6.55 + *   })
    6.56 + *   static class AddressModel {
    6.57 + *   }
    6.58   * }
    6.59   * </pre>
    6.60 + * The generated model class has a default constructor, and also <em>quick
    6.61 + * instantiation</em> constructor that accepts all non-array properties 
    6.62 + * (in the order used in the {@link #properties()} attribute) and vararg list
    6.63 + * for the first array property (if any). One can thus use following code
    6.64 + * to create an instance of the Person and Address classes:
    6.65 + * <pre>
    6.66 + * 
    6.67 + * Person p = new Person("Jaroslav", "Tulach",
    6.68 + *   new Address("Markoušovice", "Úpice"),
    6.69 + *   new Address("V Parku", "Praha")
    6.70 + * );
    6.71 + * // p.toString() then returns equivalent of following <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> object
    6.72 + * {
    6.73 + *   "firstName" : "Jaroslav",
    6.74 + *   "lastName" : "Tulach",
    6.75 + *   "addresses" : [
    6.76 + *     { "street" : "Markoušovice", "town" : "Úpice" },
    6.77 + *     { "street" : "V Parku", "town" : "Praha" },
    6.78 + *   ]
    6.79 + * }
    6.80 + * </pre>
    6.81 + * <p>
    6.82 + * In case you are using <a href="http://knockoutjs.com/">Knockout technology</a>
    6.83 + * for Java then you can associate such model object with surrounding HTML page by
    6.84 + * calling: <code>p.applyBindings();</code>. The page can then use regular
    6.85 + * <a href="http://knockoutjs.com/">Knockout</a> bindings to reference your
    6.86 + * model and create dynamic connection between your model in Java and 
    6.87 + * live DOM structure in the browser:
    6.88 + * <pre>
    6.89 + * Name: &lt;span data-bind="text: fullName"&gt;
    6.90 + * &lt;div data-bind="foreach: addresses"&gt;
    6.91 + *   Lives in &lt;span data-bind="text: town"/&gt;
    6.92 + * &lt;/div&gt;
    6.93 + * </pre>
    6.94 + * 
    6.95   *
    6.96   * @author Jaroslav Tulach <jtulach@netbeans.org>
    6.97   */
     7.1 --- a/json/src/main/java/org/apidesign/html/json/impl/Bindings.java	Thu Aug 08 13:30:23 2013 +0200
     7.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/Bindings.java	Fri Aug 09 16:57:52 2013 +0200
     7.3 @@ -41,7 +41,7 @@
     7.4      }
     7.5      
     7.6      public <M> PropertyBinding registerProperty(String propName, M model, SetAndGet<M> access, boolean readOnly) {
     7.7 -        PropertyBinding pb = PropertyBindingAccessor.create(new PBData<M>(propName, model, access, readOnly));
     7.8 +        PropertyBinding pb = PropertyBindingAccessor.create(new PBData<M>(this, propName, model, access, readOnly));
     7.9          bp.bind(pb, model, data);
    7.10          return pb;
    7.11      }
     8.1 --- a/json/src/main/java/org/apidesign/html/json/impl/JSONList.java	Thu Aug 08 13:30:23 2013 +0200
     8.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/JSONList.java	Fri Aug 09 16:57:52 2013 +0200
     8.3 @@ -166,15 +166,18 @@
     8.4          return ko;
     8.5      }
     8.6  
     8.7 -    final Object koData() {
     8.8 -        Object[] arr = toArray();
     8.9 +    static final Object koData(Collection<?> c, Bindings m) {
    8.10 +        Object[] arr = c.toArray();
    8.11          for (int i = 0; i < arr.length; i++) {
    8.12 -            Object r = WrapperObject.find(arr[i]);
    8.13 +            Object r = WrapperObject.find(arr[i], m);
    8.14              if (r != null) {
    8.15                  arr[i] = r;
    8.16              }
    8.17          }
    8.18 -        return model.wrapArray(arr);
    8.19 +        return m.wrapArray(arr);
    8.20      }
    8.21 -    
    8.22 +
    8.23 +    final Object koData() {
    8.24 +        return koData(this, model);
    8.25 +    }
    8.26  }
     9.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Thu Aug 08 13:30:23 2013 +0200
     9.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Fri Aug 09 16:57:52 2013 +0200
     9.3 @@ -196,6 +196,56 @@
     9.4                  w.append("  };\n");
     9.5                  w.append("  public ").append(className).append("() {\n");
     9.6                  w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
     9.7 +                for (Prprt p : props) {
     9.8 +                    if (p.array()) {
     9.9 +                        continue;
    9.10 +                    }
    9.11 +                    boolean[] isModel = {false};
    9.12 +                    boolean[] isEnum = {false};
    9.13 +                    boolean isPrimitive[] = {false};
    9.14 +                    String tn = checkType(p, isModel, isEnum, isPrimitive);
    9.15 +                    if (isModel[0]) {
    9.16 +                        w.write("    prop_" + p.name() + " = new " + tn + "();\n");
    9.17 +                    }
    9.18 +                }
    9.19 +                w.append("  };\n");
    9.20 +                w.append("  public ").append(className).append("(");
    9.21 +                Prprt firstArray = null;
    9.22 +                String sep = "";
    9.23 +                for (Prprt p : props) {
    9.24 +                    if (p.array()) {
    9.25 +                        if (firstArray == null) {
    9.26 +                            firstArray = p;
    9.27 +                        }
    9.28 +                        continue;
    9.29 +                    }
    9.30 +                    String tn = typeName(e, p);
    9.31 +                    w.write(sep);
    9.32 +                    w.write(tn);
    9.33 +                    w.write(" " + p.name());
    9.34 +                    sep = ", ";
    9.35 +                }
    9.36 +                if (firstArray != null) {
    9.37 +                    String tn = typeName(e, firstArray);
    9.38 +                    w.write(sep);
    9.39 +                    w.write(tn);
    9.40 +                    w.write("... " + firstArray.name());
    9.41 +                }
    9.42 +                w.append(") {\n");
    9.43 +                w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
    9.44 +                for (Prprt p : props) {
    9.45 +                    if (p.array()) {
    9.46 +                        continue;
    9.47 +                    }
    9.48 +                    w.write("    this.prop_" + p.name() + " = " + p.name() + ";\n");
    9.49 +                }
    9.50 +                if (firstArray != null) {
    9.51 +                    String tn = typeName(e, firstArray);
    9.52 +                    String[] gs = toGetSet(firstArray.name(), tn, true);
    9.53 +                    w.write("    for(" + tn + " $item : " + firstArray.name() + ") {\n");
    9.54 +                    w.write("      " + gs[0] + "().add($item);\n");
    9.55 +                    w.write("    }\n");
    9.56 +                }
    9.57                  w.append("  };\n");
    9.58                  w.append("  private org.apidesign.html.json.impl.Bindings intKnckt() {\n");
    9.59                  w.append("    if (ko != null) return ko;\n");
    9.60 @@ -338,6 +388,13 @@
    9.61                  w.append("  };\n");
    9.62                  writeToString(props, w);
    9.63                  writeClone(className, props, w);
    9.64 +                w.write("  /** Activates this model instance in the current {@link "
    9.65 +                    + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. "
    9.66 +                    + "In case of using Knockout technology, this means to "
    9.67 +                    + "bind JSON like data in this model instance with Knockout tags in "
    9.68 +                    + "the surrounding HTML page."
    9.69 +                    + "*/"
    9.70 +                );
    9.71                  w.write("  public " + className + " applyBindings() {\n");
    9.72                  w.write("    intKnckt().applyBindings();\n");
    9.73                  w.write("    return this;\n");
    9.74 @@ -466,11 +523,34 @@
    9.75              TypeMirror ert = tu.erasure(rt);
    9.76              String tn = fqn(ert, ee);
    9.77              boolean array = false;
    9.78 +            final TypeMirror toCheck;
    9.79              if (tn.equals("java.util.List")) {
    9.80                  array = true;
    9.81 +                toCheck = ((DeclaredType)rt).getTypeArguments().get(0);
    9.82 +            } else {
    9.83 +                toCheck = rt;
    9.84              }
    9.85              
    9.86              final String sn = ee.getSimpleName().toString();
    9.87 +            
    9.88 +            if (toCheck.getKind().isPrimitive()) {
    9.89 +                // OK
    9.90 +            } else {
    9.91 +                TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
    9.92 +                TypeMirror enumType = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
    9.93 +
    9.94 +                if (tu.isSubtype(toCheck, stringType)) {
    9.95 +                    // OK
    9.96 +                } else if (tu.isSubtype(toCheck, enumType)) {
    9.97 +                    // OK
    9.98 +                } else if (isModel(toCheck)) {
    9.99 +                    // OK
   9.100 +                } else {
   9.101 +                    ok = false;
   9.102 +                    error(sn + " cannot return " + toCheck, e);
   9.103 +                }
   9.104 +            }
   9.105 +            
   9.106              String[] gs = toGetSet(sn, tn, array);
   9.107              
   9.108              w.write("  public " + tn + " " + gs[0] + "() {\n");
   9.109 @@ -952,7 +1032,7 @@
   9.110                  continue;
   9.111              }
   9.112              error(
   9.113 -                "@On method can only accept String named 'id', " + className + " argument or argument named 'data'",
   9.114 +                "The annotated method can only accept " + className + " argument or argument named 'data'",
   9.115                  ee
   9.116              );
   9.117          }
    10.1 --- a/json/src/main/java/org/apidesign/html/json/impl/PropertyBindingAccessor.java	Thu Aug 08 13:30:23 2013 +0200
    10.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/PropertyBindingAccessor.java	Fri Aug 09 16:57:52 2013 +0200
    10.3 @@ -62,8 +62,10 @@
    10.4          public final boolean readOnly;
    10.5          private final M model;
    10.6          private final SetAndGet<M> access;
    10.7 +        private final Bindings<?> bindings;
    10.8  
    10.9 -        public PBData(String name, M model, SetAndGet<M> access, boolean readOnly) {
   10.10 +        public PBData(Bindings<?> bindings, String name, M model, SetAndGet<M> access, boolean readOnly) {
   10.11 +            this.bindings = bindings;
   10.12              this.name = name;
   10.13              this.model = model;
   10.14              this.access = access;
   10.15 @@ -81,6 +83,10 @@
   10.16          public boolean isReadOnly() {
   10.17              return readOnly;
   10.18          }
   10.19 +
   10.20 +        public Bindings getBindings() {
   10.21 +            return bindings;
   10.22 +        }
   10.23      } // end of PBData
   10.24      
   10.25      public static final class FBData<M> {
    11.1 --- a/json/src/main/java/org/apidesign/html/json/impl/WrapperObject.java	Thu Aug 08 13:30:23 2013 +0200
    11.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/WrapperObject.java	Fri Aug 09 16:57:52 2013 +0200
    11.3 @@ -21,6 +21,9 @@
    11.4  
    11.5  package org.apidesign.html.json.impl;
    11.6  
    11.7 +import java.util.Collection;
    11.8 +import org.apidesign.html.json.impl.PropertyBindingAccessor.PBData;
    11.9 +
   11.10  /** A way to extract real object from a model classes.
   11.11   *
   11.12   * @author Jaroslav Tulach <jtulach@netbeans.org>
   11.13 @@ -35,17 +38,24 @@
   11.14          this.ko = ko;
   11.15      }
   11.16      
   11.17 +    public static Object find(Object object) {
   11.18 +        return find(object, null);
   11.19 +    }
   11.20      
   11.21 -    public static Object find(Object model) {
   11.22 -        if (model == null) {
   11.23 +    public static Object find(Object object, Bindings model) {
   11.24 +        if (object == null) {
   11.25              return null;
   11.26          }
   11.27 -        if (model instanceof JSONList) {
   11.28 -            return ((JSONList<?>)model).koData();
   11.29 +        
   11.30 +        if (object instanceof JSONList) {
   11.31 +            return ((JSONList<?>)object).koData();
   11.32 +        }
   11.33 +        if (object instanceof Collection) {
   11.34 +            return JSONList.koData((Collection<?>)object, model);
   11.35          }
   11.36          
   11.37          WrapperObject ro = new WrapperObject();
   11.38 -        model.equals(ro);
   11.39 +        object.equals(ro);
   11.40          return ro.ko;
   11.41      }
   11.42  }
    12.1 --- a/json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java	Thu Aug 08 13:30:23 2013 +0200
    12.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java	Fri Aug 09 16:57:52 2013 +0200
    12.3 @@ -65,7 +65,7 @@
    12.4      
    12.5      public Object getValue() {
    12.6          Object v = data.getValue();
    12.7 -        Object r = WrapperObject.find(v);
    12.8 +        Object r = WrapperObject.find(v, data.getBindings());
    12.9          return r == null ? v : r;
   12.10      }
   12.11      
    13.1 --- a/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Thu Aug 08 13:30:23 2013 +0200
    13.2 +++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Fri Aug 09 16:57:52 2013 +0200
    13.3 @@ -92,6 +92,69 @@
    13.4          }
    13.5      }
    13.6      
    13.7 +    @Test public void computedCantReturnVoid() throws IOException {
    13.8 +        String html = "<html><body>"
    13.9 +            + "</body></html>";
   13.10 +        String code = "package x.y.z;\n"
   13.11 +            + "import net.java.html.json.Model;\n"
   13.12 +            + "import net.java.html.json.Property;\n"
   13.13 +            + "import net.java.html.json.ComputedProperty;\n"
   13.14 +            + "@Model(className=\"XModel\", properties={\n"
   13.15 +            + "  @Property(name=\"prop\", type=int.class)\n"
   13.16 +            + "})\n"
   13.17 +            + "class X {\n"
   13.18 +            + "    @ComputedProperty static void y(int prop) {\n"
   13.19 +            + "    }\n"
   13.20 +            + "}\n";
   13.21 +        
   13.22 +        Compile c = Compile.create(html, code);
   13.23 +        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   13.24 +        boolean ok = false;
   13.25 +        StringBuilder msgs = new StringBuilder();
   13.26 +        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
   13.27 +            String msg = e.getMessage(Locale.ENGLISH);
   13.28 +            if (msg.contains("y cannot return void")) {
   13.29 +                ok = true;
   13.30 +            }
   13.31 +            msgs.append("\n").append(msg);
   13.32 +        }
   13.33 +        if (!ok) {
   13.34 +            fail("Should contain warning about non-static method:" + msgs);
   13.35 +        }
   13.36 +    }
   13.37 +    
   13.38 +    @Test public void computedCantReturnRunnable() throws IOException {
   13.39 +        String html = "<html><body>"
   13.40 +            + "</body></html>";
   13.41 +        String code = "package x.y.z;\n"
   13.42 +            + "import net.java.html.json.Model;\n"
   13.43 +            + "import net.java.html.json.Property;\n"
   13.44 +            + "import net.java.html.json.ComputedProperty;\n"
   13.45 +            + "@Model(className=\"XModel\", properties={\n"
   13.46 +            + "  @Property(name=\"prop\", type=int.class)\n"
   13.47 +            + "})\n"
   13.48 +            + "class X {\n"
   13.49 +            + "    @ComputedProperty static Runnable y(int prop) {\n"
   13.50 +            + "       return null;\n"
   13.51 +            + "    }\n"
   13.52 +            + "}\n";
   13.53 +        
   13.54 +        Compile c = Compile.create(html, code);
   13.55 +        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   13.56 +        boolean ok = false;
   13.57 +        StringBuilder msgs = new StringBuilder();
   13.58 +        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
   13.59 +            String msg = e.getMessage(Locale.ENGLISH);
   13.60 +            if (msg.contains("y cannot return java.lang.Runnable")) {
   13.61 +                ok = true;
   13.62 +            }
   13.63 +            msgs.append("\n").append(msg);
   13.64 +        }
   13.65 +        if (!ok) {
   13.66 +            fail("Should contain warning about non-static method:" + msgs);
   13.67 +        }
   13.68 +    }
   13.69 +    
   13.70      @Test public void canWeCompileWithJDK1_5SourceLevel() throws IOException {
   13.71          String html = "<html><body>"
   13.72              + "</body></html>";
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/json/src/test/java/org/apidesign/html/json/impl/ConstructorTest.java	Fri Aug 09 16:57:52 2013 +0200
    14.3 @@ -0,0 +1,58 @@
    14.4 +/**
    14.5 + * HTML via Java(tm) Language Bindings
    14.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    14.7 + *
    14.8 + * This program is free software: you can redistribute it and/or modify
    14.9 + * it under the terms of the GNU General Public License as published by
   14.10 + * the Free Software Foundation, version 2 of the License.
   14.11 + *
   14.12 + * This program is distributed in the hope that it will be useful,
   14.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14.15 + * GNU General Public License for more details. apidesign.org
   14.16 + * designates this particular file as subject to the
   14.17 + * "Classpath" exception as provided by apidesign.org
   14.18 + * in the License file that accompanied this code.
   14.19 + *
   14.20 + * You should have received a copy of the GNU General Public License
   14.21 + * along with this program. Look for COPYING file in the top folder.
   14.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   14.23 + */
   14.24 +package org.apidesign.html.json.impl;
   14.25 +
   14.26 +import net.java.html.json.Model;
   14.27 +import net.java.html.json.Property;
   14.28 +import static org.testng.Assert.assertNotNull;
   14.29 +import static org.testng.Assert.assertEquals;
   14.30 +import org.testng.annotations.Test;
   14.31 +
   14.32 +/**
   14.33 + *
   14.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   14.35 + */
   14.36 +@Model(className="Man", properties={
   14.37 +    @Property(name = "name", type = String.class),
   14.38 +    @Property(name = "other", type = Address.class, array = true),
   14.39 +    @Property(name = "primary", type = Address.class),
   14.40 +    @Property(name = "childrenNames", type = String.class, array = true)
   14.41 +})
   14.42 +public class ConstructorTest {
   14.43 +    @Model(className = "Address", properties = {
   14.44 +        @Property(name = "place", type = String.class)
   14.45 +    })
   14.46 +    static final class AddressModel {
   14.47 +    }
   14.48 +    
   14.49 +    @Test public void initializedByDefault() {
   14.50 +        Man m = new Man();
   14.51 +        assertNotNull(m.getPrimary(), "Single subobjects are initialized");
   14.52 +    }
   14.53 +    
   14.54 +    @Test public void hasRichConstructor() {
   14.55 +        Man m = new Man("Jarda", new Address("home"), new Address("work"), new Address("hotel"));
   14.56 +        assertEquals(m.getName(), "Jarda");
   14.57 +        assertNotNull(m.getPrimary(), "Primary address specified");
   14.58 +        assertNotNull(m.getPrimary().getPlace(), "home");
   14.59 +        assertEquals(m.getOther().size(), 2, "Two other addresses");
   14.60 +    }
   14.61 +}