1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Sat Mar 30 07:58:50 2013 +0100
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Sun Mar 31 05:34:15 2013 +0200
1.3 @@ -41,6 +41,14 @@
1.4 Object ret = getProperty(object, property);
1.5 return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE;
1.6 }
1.7 +
1.8 + public static <T> T toModel(Class<T> modelClass, Object object, String property) {
1.9 + Object ret = getProperty(object, property);
1.10 + if (ret == null || modelClass.isInstance(ret)) {
1.11 + return modelClass.cast(ret);
1.12 + }
1.13 + throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
1.14 + }
1.15
1.16 @JavaScriptBody(args = { "object", "property" },
1.17 body = "if (property === null) return object;\n"
2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Sat Mar 30 07:58:50 2013 +0100
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Sun Mar 31 05:34:15 2013 +0200
2.3 @@ -28,9 +28,9 @@
2.4 import java.util.HashMap;
2.5 import java.util.LinkedHashSet;
2.6 import java.util.List;
2.7 -import java.util.Locale;
2.8 import java.util.Map;
2.9 import java.util.Set;
2.10 +import java.util.WeakHashMap;
2.11 import javax.annotation.processing.AbstractProcessor;
2.12 import javax.annotation.processing.Completion;
2.13 import javax.annotation.processing.Completions;
2.14 @@ -73,6 +73,7 @@
2.15 "org.apidesign.bck2brwsr.htmlpage.api.On"
2.16 })
2.17 public final class PageProcessor extends AbstractProcessor {
2.18 + private final Map<Element,String> models = new WeakHashMap<>();
2.19 @Override
2.20 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
2.21 boolean ok = true;
2.22 @@ -86,6 +87,9 @@
2.23 ok = false;
2.24 }
2.25 }
2.26 + if (roundEnv.processingOver()) {
2.27 + models.clear();
2.28 + }
2.29 return ok;
2.30 }
2.31
2.32 @@ -129,6 +133,7 @@
2.33 w.append(" private boolean locked;\n");
2.34 w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
2.35 w.append(body.toString());
2.36 + w.append(" private static Class<" + e.getSimpleName() + "> modelFor() { return null; }\n");
2.37 w.append("}\n");
2.38 } finally {
2.39 w.close();
2.40 @@ -546,6 +551,7 @@
2.41 if (m != null) {
2.42 ret = findPkgName(e) + '.' + m.className();
2.43 isModel = true;
2.44 + models.put(e, m.className());
2.45 } else {
2.46 ret = tm.toString();
2.47 }
2.48 @@ -687,18 +693,21 @@
2.49 params.append('"').append(id).append('"');
2.50 continue;
2.51 }
2.52 - toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString";
2.53 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(";
2.54 }
2.55 if (ve.asType().getKind() == TypeKind.DOUBLE) {
2.56 - toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble";
2.57 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(";
2.58 }
2.59 if (ve.asType().getKind() == TypeKind.INT) {
2.60 - toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt";
2.61 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt(";
2.62 + }
2.63 + if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
2.64 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toModel(" + ve.asType() + ".class, ";
2.65 }
2.66
2.67 if (toCall != null) {
2.68 - params.append(toCall).append('(');
2.69 - if (dataName != null && ve.getSimpleName().contentEquals("data")) {
2.70 + params.append(toCall);
2.71 + if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
2.72 params.append(dataName);
2.73 params.append(", null");
2.74 } else {
2.75 @@ -726,4 +735,17 @@
2.76 }
2.77 return params;
2.78 }
2.79 +
2.80 + private boolean isModel(TypeMirror tm) {
2.81 + final Element e = processingEnv.getTypeUtils().asElement(tm);
2.82 + for (Element ch : e.getEnclosedElements()) {
2.83 + if (ch.getKind() == ElementKind.METHOD) {
2.84 + ExecutableElement ee = (ExecutableElement)ch;
2.85 + if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
2.86 + return true;
2.87 + }
2.88 + }
2.89 + }
2.90 + return models.values().contains(e.getSimpleName().toString());
2.91 + }
2.92 }
3.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Sat Mar 30 07:58:50 2013 +0100
3.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Sun Mar 31 05:34:15 2013 +0200
3.3 @@ -36,7 +36,8 @@
3.4 @Page(xhtml="Knockout.xhtml", className="KnockoutModel", properties={
3.5 @Property(name="name", type=String.class),
3.6 @Property(name="results", type=String.class, array = true),
3.7 - @Property(name="callbackCount", type=int.class)
3.8 + @Property(name="callbackCount", type=int.class),
3.9 + @Property(name="people", type=PersonImpl.class, array = true)
3.10 })
3.11 public class KnockoutTest {
3.12
3.13 @@ -98,11 +99,44 @@
3.14 assert cnt == 2 : "Two children now, but was " + cnt;
3.15 }
3.16
3.17 + @HtmlFragment(
3.18 + "<ul id='ul' data-bind='foreach: people'>\n"
3.19 + + " <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
3.20 + + "</ul>\n"
3.21 + )
3.22 + @BrwsrTest public void displayContentOfArrayOfPeople() {
3.23 + KnockoutModel m = new KnockoutModel();
3.24 + m.getPeople().add(new Person());
3.25 + m.applyBindings();
3.26 +
3.27 + int cnt = countChildren("ul");
3.28 + assert cnt == 1 : "One child, but was " + cnt;
3.29 +
3.30 + m.getPeople().add(new Person());
3.31 +
3.32 + cnt = countChildren("ul");
3.33 + assert cnt == 2 : "Two children now, but was " + cnt;
3.34 +
3.35 + triggerChildClick("ul", 1);
3.36 +
3.37 + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
3.38 +
3.39 + cnt = countChildren("ul");
3.40 + assert cnt == 1 : "Again one child, but was " + cnt;
3.41 + }
3.42 +
3.43 @OnFunction
3.44 static void call(KnockoutModel m, String data) {
3.45 m.setName(data);
3.46 m.setCallbackCount(m.getCallbackCount() + 1);
3.47 }
3.48 +
3.49 + @OnFunction
3.50 + static void removePerson(KnockoutModel model, Person data) {
3.51 + model.setCallbackCount(model.getCallbackCount() + 1);
3.52 + model.getPeople().remove(data);
3.53 + }
3.54 +
3.55
3.56 @ComputedProperty
3.57 static String helloMessage(String name) {