Generating quick instantiation constructor
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 09 Aug 2013 13:44:30 +0200
changeset 232c595893ea929
parent 231 abed4da1d5c7
child 233 7d43d81de35e
Generating quick instantiation constructor
json/src/main/java/net/java/html/json/Model.java
json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java
json/src/test/java/org/apidesign/html/json/impl/ConstructorTest.java
     1.1 --- a/json/src/main/java/net/java/html/json/Model.java	Fri Aug 09 13:08:36 2013 +0200
     1.2 +++ b/json/src/main/java/net/java/html/json/Model.java	Fri Aug 09 13:44:30 2013 +0200
     1.3 @@ -24,15 +24,26 @@
     1.4  import java.lang.annotation.Retention;
     1.5  import java.lang.annotation.RetentionPolicy;
     1.6  import java.lang.annotation.Target;
     1.7 +import java.net.URL;
     1.8  
     1.9 -/** Defines a model class named {@link #className()} which contains
    1.10 - * properties defined via {@link #properties()}. This class can have methods 
    1.11 - * annotated by {@link ComputedProperty} which define derived
    1.12 - * properties in the model class.
    1.13 +/** Defines a model class that represents a single 
    1.14 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
    1.15 + * named {@link #className()}. The generated class contains
    1.16 + * getters and setters for properties defined via {@link #properties()} and
    1.17 + * getters for other, derived properties defined by annotating methods
    1.18 + * of this class by {@link ComputedProperty}. Each property
    1.19 + * can be of primitive type, an {@link Enum enum type} or (in order to create 
    1.20 + * nested <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> structure)
    1.21 + * of another {@link Model class generated by @Model} annotation. Each property
    1.22 + * can either represent a single value or be an array of its values.
    1.23   * <p>
    1.24 - * The {@link #className() generated class}'s <code>toString</code>
    1.25 + * The {@link #className() generated class}'s <code>toString</code> method
    1.26   * converts the state of the object into 
    1.27 - * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. 
    1.28 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. One can
    1.29 + * use {@link Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream)}
    1.30 + * method to read the JSON text stored in a file or other stream back into the Java model. 
    1.31 + * One can also use {@link OnReceive @OnReceive} annotation to read the model
    1.32 + * asynchronously from a {@link URL}.
    1.33   * <p>
    1.34   * An example where one defines class <code>Person</code> with four
    1.35   * properties (<code>firstName</code>, <code>lastName</code>, array of <code>addresses</code> and
    1.36 @@ -57,17 +68,41 @@
    1.37   *   }
    1.38   * }
    1.39   * </pre>
    1.40 - * The previous model class could then represent a 
    1.41 - * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> object like this:
    1.42 + * The generated model class has a default constructor, and also <em>quick
    1.43 + * instantiation</em> constructor that accepts all non-array properties 
    1.44 + * (in the order used in the {@link #properties()} attribute) and vararg list
    1.45 + * for the first array property (if any). One can thus use following code
    1.46 + * to create an instance of the Person and Address classes:
    1.47   * <pre>
    1.48 + * 
    1.49 + * Person p = new Person("Jaroslav", "Tulach",
    1.50 + *   new Address("Markoušovice", "Úpice"),
    1.51 + *   new Address("V Parku", "Praha")
    1.52 + * );
    1.53 + * // p.toString() then returns equivalent of following <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> object
    1.54   * {
    1.55   *   "firstName" : "Jaroslav",
    1.56   *   "lastName" : "Tulach",
    1.57   *   "addresses" : [
    1.58 - *     { "street" : "Markoušovice", "town" : "Úpice" }
    1.59 + *     { "street" : "Markoušovice", "town" : "Úpice" },
    1.60 + *     { "street" : "V Parku", "town" : "Praha" },
    1.61   *   ]
    1.62   * }
    1.63   * </pre>
    1.64 + * <p>
    1.65 + * In case you are using <a href="http://knockoutjs.com/">Knockout technology</a>
    1.66 + * for Java then you can associate such model object with surrounding HTML page by
    1.67 + * calling: <code>p.applyBindings();</code>. The page can then use regular
    1.68 + * <a href="http://knockoutjs.com/">Knockout</a> bindings to reference your
    1.69 + * model and create dynamic connection between your model in Java and 
    1.70 + * live DOM structure in the browser:
    1.71 + * <pre>
    1.72 + * Name: &lt;span data-bind="text: fullName"&gt;
    1.73 + * &lt;div data-bind="foreach: addresses"&gt;
    1.74 + *   Lives in &lt;span data-bind="text: town"/&gt;
    1.75 + * &lt;/div&gt;
    1.76 + * </pre>
    1.77 + * 
    1.78   *
    1.79   * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.80   */
     2.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Fri Aug 09 13:08:36 2013 +0200
     2.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Fri Aug 09 13:44:30 2013 +0200
     2.3 @@ -209,6 +209,44 @@
     2.4                      }
     2.5                  }
     2.6                  w.append("  };\n");
     2.7 +                w.append("  public ").append(className).append("(");
     2.8 +                Prprt firstArray = null;
     2.9 +                String sep = "";
    2.10 +                for (Prprt p : props) {
    2.11 +                    if (p.array()) {
    2.12 +                        if (firstArray == null) {
    2.13 +                            firstArray = p;
    2.14 +                        }
    2.15 +                        continue;
    2.16 +                    }
    2.17 +                    String tn = typeName(e, p);
    2.18 +                    w.write(sep);
    2.19 +                    w.write(tn);
    2.20 +                    w.write(" " + p.name());
    2.21 +                    sep = ", ";
    2.22 +                }
    2.23 +                if (firstArray != null) {
    2.24 +                    String tn = typeName(e, firstArray);
    2.25 +                    w.write(sep);
    2.26 +                    w.write(tn);
    2.27 +                    w.write("... " + firstArray.name());
    2.28 +                }
    2.29 +                w.append(") {\n");
    2.30 +                w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
    2.31 +                for (Prprt p : props) {
    2.32 +                    if (p.array()) {
    2.33 +                        continue;
    2.34 +                    }
    2.35 +                    w.write("    this.prop_" + p.name() + " = " + p.name() + ";\n");
    2.36 +                }
    2.37 +                if (firstArray != null) {
    2.38 +                    String tn = typeName(e, firstArray);
    2.39 +                    String[] gs = toGetSet(firstArray.name(), tn, true);
    2.40 +                    w.write("    for(" + tn + " $item : " + firstArray.name() + ") {\n");
    2.41 +                    w.write("      " + gs[0] + "().add($item);\n");
    2.42 +                    w.write("    }\n");
    2.43 +                }
    2.44 +                w.append("  };\n");
    2.45                  w.append("  private org.apidesign.html.json.impl.Bindings intKnckt() {\n");
    2.46                  w.append("    if (ko != null) return ko;\n");
    2.47                  w.append("    ko = org.apidesign.html.json.impl.Bindings.apply(context, this);\n");
     3.1 --- a/json/src/test/java/org/apidesign/html/json/impl/ConstructorTest.java	Fri Aug 09 13:08:36 2013 +0200
     3.2 +++ b/json/src/test/java/org/apidesign/html/json/impl/ConstructorTest.java	Fri Aug 09 13:44:30 2013 +0200
     3.3 @@ -23,6 +23,7 @@
     3.4  import net.java.html.json.Model;
     3.5  import net.java.html.json.Property;
     3.6  import static org.testng.Assert.assertNotNull;
     3.7 +import static org.testng.Assert.assertEquals;
     3.8  import org.testng.annotations.Test;
     3.9  
    3.10  /**
    3.11 @@ -37,7 +38,7 @@
    3.12  })
    3.13  public class ConstructorTest {
    3.14      @Model(className = "Address", properties = {
    3.15 -        @Property(name = "town", type = String.class)
    3.16 +        @Property(name = "place", type = String.class)
    3.17      })
    3.18      static final class AddressModel {
    3.19      }
    3.20 @@ -46,4 +47,12 @@
    3.21          Man m = new Man();
    3.22          assertNotNull(m.getPrimary(), "Single subobjects are initialized");
    3.23      }
    3.24 +    
    3.25 +    @Test public void hasRichConstructor() {
    3.26 +        Man m = new Man("Jarda", new Address("home"), new Address("work"), new Address("hotel"));
    3.27 +        assertEquals(m.getName(), "Jarda");
    3.28 +        assertNotNull(m.getPrimary(), "Primary address specified");
    3.29 +        assertNotNull(m.getPrimary().getPlace(), "home");
    3.30 +        assertEquals(m.getOther().size(), 2, "Two other addresses");
    3.31 +    }
    3.32  }