# HG changeset patch # User Jaroslav Tulach # Date 1364724098 -7200 # Node ID e51a474fcf79873b111d2cf936e5c22e9a8201cb # Parent 3e023bea2da40a6e4a185058b1e4d6b503292f55 Mutable properties on model classes diff -r 3e023bea2da4 -r e51a474fcf79 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Sun Mar 31 06:46:25 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Sun Mar 31 12:01:38 2013 +0200 @@ -30,45 +30,40 @@ public class Knockout { /** used by tests */ static Knockout next; + private final Object model; - Knockout() { + Knockout(Object model) { + this.model = model == null ? this : model; } public static Knockout applyBindings( + Object model, String[] propsGettersAndSetters, + String[] methodsAndSignatures + ) { + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures); + return new Knockout(model); + } + public static Knockout applyBindings( Class modelClass, M model, String[] propsGettersAndSetters, String[] methodsAndSignatures ) { Knockout bindings = next; next = null; if (bindings == null) { - bindings = new Knockout(); + bindings = new Knockout(null); } - for (int i = 0; i < propsGettersAndSetters.length; i += 4) { - try { - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]); - bind(bindings, model, propsGettersAndSetters[i], - propsGettersAndSetters[i + 1], - propsGettersAndSetters[i + 2], - getter.getReturnType().isPrimitive(), - List.class.isAssignableFrom(getter.getReturnType()) - ); - } catch (NoSuchMethodException ex) { - throw new IllegalStateException(ex.getMessage()); - } - } - for (int i = 0; i < methodsAndSignatures.length; i += 2) { - expose( - bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1] - ); - } + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures); applyBindings(bindings); return bindings; } - @JavaScriptBody(args = { "prop" }, body = - "this[prop].valueHasMutated();" + public void valueHasMutated(String prop) { + valueHasMutated(model, prop); + } + @JavaScriptBody(args = { "self", "prop" }, body = + "self[prop].valueHasMutated();" ) - public void valueHasMutated(String prop) { + public void valueHasMutated(Object self, String prop) { } @@ -107,4 +102,29 @@ @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);") private static void applyBindings(Object bindings) {} + + private static void applyImpl( + String[] propsGettersAndSetters, + Class modelClass, + Object bindings, + Object model, + String[] methodsAndSignatures + ) throws IllegalStateException, SecurityException { + for (int i = 0; i < propsGettersAndSetters.length; i += 4) { + try { + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]); + bind(bindings, model, propsGettersAndSetters[i], + propsGettersAndSetters[i + 1], + propsGettersAndSetters[i + 2], + getter.getReturnType().isPrimitive(), + List.class.isAssignableFrom(getter.getReturnType())); + } catch (NoSuchMethodException ex) { + throw new IllegalStateException(ex.getMessage()); + } + } + for (int i = 0; i < methodsAndSignatures.length; i += 2) { + expose( + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]); + } + } } diff -r 3e023bea2da4 -r e51a474fcf79 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Sun Mar 31 06:46:25 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Sun Mar 31 12:01:38 2013 +0200 @@ -135,16 +135,13 @@ w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n"); w.append(body.toString()); w.append(" private static Class<" + e.getSimpleName() + "> modelFor() { return null; }\n"); - for (int i = 0; i < propsGetSet.size(); i += 4) { - w.append(" @JavaScriptOnly(name=\"" + propsGetSet.get(i) + "\",\n"); - w.append(" value=\"function() { "); - final String setter = propsGetSet.get(i + 2); - if (setter != null) { - w.append("if (arguments.length == 1) this." + setter + "(arguments[0]); "); - } - w.append("return this." + propsGetSet.get(i + 1) + "();}\")\n"); - w.append(" private static native void __accessor" + i + "();"); - } + w.append(" public ").append(className).append("() {\n"); + w.append(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, "); + writeStringArray(propsGetSet, w); + w.append(", "); + writeStringArray(Collections.emptyList(), w); + w.append(" );\n"); + w.append(" };\n"); w.append("}\n"); } finally { w.close(); @@ -219,25 +216,10 @@ w.write("public " + className + " applyBindings() {\n"); w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings("); w.write(className + ".class, this, "); - w.write("new String[] {\n"); - String sep = ""; - for (String n : propsGetSet) { - w.write(sep); - if (n == null) { - w.write(" null"); - } else { - w.write(" \"" + n + "\""); - } - sep = ",\n"; - } - w.write("\n }, new String[] {\n"); - sep = ""; - for (String n : functions) { - w.write(sep); - w.write(n); - sep = ",\n"; - } - w.write("\n });\n return this;\n}\n"); + writeStringArray(propsGetSet, w); + w.append(", "); + writeStringArray(functions, w); + w.write(");\n return this;\n}\n"); w.write("public void triggerEvent(Element e, OnEvent ev) {\n"); w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n"); @@ -681,8 +663,8 @@ body.append(");\n"); body.append("}\n"); - functions.add('\"' + n + '\"'); - functions.add('\"' + n + "__VLjava_lang_Object_2Ljava_lang_Object_2" + '\"'); + functions.add(n); + functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2"); } return true; } @@ -762,4 +744,19 @@ } return models.values().contains(e.getSimpleName().toString()); } + + private void writeStringArray(List strings, Writer w) throws IOException { + w.write("new String[] {\n"); + String sep = ""; + for (String n : strings) { + w.write(sep); + if (n == null) { + w.write(" null"); + } else { + w.write(" \"" + n + "\""); + } + sep = ",\n"; + } + w.write("\n }"); + } } diff -r 3e023bea2da4 -r e51a474fcf79 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Sun Mar 31 06:46:25 2013 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Sun Mar 31 12:01:38 2013 +0200 @@ -101,7 +101,7 @@ @HtmlFragment( "
    \n" - + "
  • \n" + + "
  • \n" + "
\n" ) @BrwsrTest public void displayContentOfArrayOfPeople() { @@ -132,6 +132,11 @@ String txt = childText("ul", 0); assert "first".equals(txt) : "Expecting 'first': " + txt; + + first.setFirstName("changed"); + + txt = childText("ul", 0); + assert "changed".equals(txt) : "Expecting 'changed': " + txt; } @OnFunction diff -r 3e023bea2da4 -r e51a474fcf79 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Sun Mar 31 06:46:25 2013 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Sun Mar 31 12:01:38 2013 +0200 @@ -197,6 +197,10 @@ static class MockKnockout extends Knockout { List mutated = new ArrayList<>(); + MockKnockout() { + super(null); + } + @Override public void valueHasMutated(String prop) { mutated.add(prop);