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: <span data-bind="text: fullName">
1.73 + * <div data-bind="foreach: addresses">
1.74 + * Lives in <span data-bind="text: town"/>
1.75 + * </div>
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 }