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}<{@link String}> or {@link java.util.List List}<{@link Integer}>).
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: <span data-bind="text: selectedName"></span>
5.35 + * <ul data-bind="foreach: names">
5.36 + * <li data-bind="text: $data, click: <b>$root.nameSelected</b>"></li>
5.37 + * </ul>
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: <span data-bind="text: fullName">
6.90 + * <div data-bind="foreach: addresses">
6.91 + * Lives in <span data-bind="text: town"/>
6.92 + * </div>
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 +}