Merge of recent work on model. @Model classes can have @OnFunction methods and their properties are accessible from the knockout bindings
1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Tue Apr 02 15:52:25 2013 +0200
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Wed Apr 03 09:17:58 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/Knockout.java Tue Apr 02 15:52:25 2013 +0200
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Wed Apr 03 09:17:58 2013 +0200
2.3 @@ -30,45 +30,40 @@
2.4 public class Knockout {
2.5 /** used by tests */
2.6 static Knockout next;
2.7 + private final Object model;
2.8
2.9 - Knockout() {
2.10 + Knockout(Object model) {
2.11 + this.model = model == null ? this : model;
2.12 }
2.13
2.14 public static <M> Knockout applyBindings(
2.15 + Object model, String[] propsGettersAndSetters,
2.16 + String[] methodsAndSignatures
2.17 + ) {
2.18 + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
2.19 + return new Knockout(model);
2.20 + }
2.21 + public static <M> Knockout applyBindings(
2.22 Class<M> modelClass, M model, String[] propsGettersAndSetters,
2.23 String[] methodsAndSignatures
2.24 ) {
2.25 Knockout bindings = next;
2.26 next = null;
2.27 if (bindings == null) {
2.28 - bindings = new Knockout();
2.29 + bindings = new Knockout(null);
2.30 }
2.31 - for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
2.32 - try {
2.33 - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
2.34 - bind(bindings, model, propsGettersAndSetters[i],
2.35 - propsGettersAndSetters[i + 1],
2.36 - propsGettersAndSetters[i + 2],
2.37 - getter.getReturnType().isPrimitive(),
2.38 - List.class.isAssignableFrom(getter.getReturnType())
2.39 - );
2.40 - } catch (NoSuchMethodException ex) {
2.41 - throw new IllegalStateException(ex.getMessage());
2.42 - }
2.43 - }
2.44 - for (int i = 0; i < methodsAndSignatures.length; i += 2) {
2.45 - expose(
2.46 - bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]
2.47 - );
2.48 - }
2.49 + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
2.50 applyBindings(bindings);
2.51 return bindings;
2.52 }
2.53
2.54 - @JavaScriptBody(args = { "prop" }, body =
2.55 - "this[prop].valueHasMutated();"
2.56 + public void valueHasMutated(String prop) {
2.57 + valueHasMutated(model, prop);
2.58 + }
2.59 + @JavaScriptBody(args = { "self", "prop" }, body =
2.60 + "self[prop].valueHasMutated();"
2.61 )
2.62 - public void valueHasMutated(String prop) {
2.63 + public void valueHasMutated(Object self, String prop) {
2.64 }
2.65
2.66
2.67 @@ -107,4 +102,29 @@
2.68
2.69 @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
2.70 private static void applyBindings(Object bindings) {}
2.71 +
2.72 + private static void applyImpl(
2.73 + String[] propsGettersAndSetters,
2.74 + Class<?> modelClass,
2.75 + Object bindings,
2.76 + Object model,
2.77 + String[] methodsAndSignatures
2.78 + ) throws IllegalStateException, SecurityException {
2.79 + for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
2.80 + try {
2.81 + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
2.82 + bind(bindings, model, propsGettersAndSetters[i],
2.83 + propsGettersAndSetters[i + 1],
2.84 + propsGettersAndSetters[i + 2],
2.85 + getter.getReturnType().isPrimitive(),
2.86 + List.class.isAssignableFrom(getter.getReturnType()));
2.87 + } catch (NoSuchMethodException ex) {
2.88 + throw new IllegalStateException(ex.getMessage());
2.89 + }
2.90 + }
2.91 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
2.92 + expose(
2.93 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
2.94 + }
2.95 + }
2.96 }
3.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Apr 02 15:52:25 2013 +0200
3.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Wed Apr 03 09:17:58 2013 +0200
3.3 @@ -28,9 +28,9 @@
3.4 import java.util.HashMap;
3.5 import java.util.LinkedHashSet;
3.6 import java.util.List;
3.7 -import java.util.Locale;
3.8 import java.util.Map;
3.9 import java.util.Set;
3.10 +import java.util.WeakHashMap;
3.11 import javax.annotation.processing.AbstractProcessor;
3.12 import javax.annotation.processing.Completion;
3.13 import javax.annotation.processing.Completions;
3.14 @@ -73,6 +73,7 @@
3.15 "org.apidesign.bck2brwsr.htmlpage.api.On"
3.16 })
3.17 public final class PageProcessor extends AbstractProcessor {
3.18 + private final Map<Element,String> models = new WeakHashMap<>();
3.19 @Override
3.20 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
3.21 boolean ok = true;
3.22 @@ -86,6 +87,9 @@
3.23 ok = false;
3.24 }
3.25 }
3.26 + if (roundEnv.processingOver()) {
3.27 + models.clear();
3.28 + }
3.29 return ok;
3.30 }
3.31
3.32 @@ -111,6 +115,7 @@
3.33 try {
3.34 StringWriter body = new StringWriter();
3.35 List<String> propsGetSet = new ArrayList<>();
3.36 + List<String> functions = new ArrayList<>();
3.37 Map<String, Collection<String>> propsDeps = new HashMap<>();
3.38 if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
3.39 ok = false;
3.40 @@ -118,17 +123,29 @@
3.41 if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps)) {
3.42 ok = false;
3.43 }
3.44 + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
3.45 + ok = false;
3.46 + }
3.47 FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
3.48 w = new OutputStreamWriter(java.openOutputStream());
3.49 try {
3.50 w.append("package " + pkg + ";\n");
3.51 w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
3.52 w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
3.53 + w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n");
3.54 w.append("final class ").append(className).append(" {\n");
3.55 w.append(" private Object json;\n");
3.56 w.append(" private boolean locked;\n");
3.57 w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
3.58 w.append(body.toString());
3.59 + w.append(" private static Class<" + e.getSimpleName() + "> modelFor() { return null; }\n");
3.60 + w.append(" public ").append(className).append("() {\n");
3.61 + w.append(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
3.62 + writeStringArray(propsGetSet, w);
3.63 + w.append(", ");
3.64 + writeStringArray(functions, w);
3.65 + w.append(" );\n");
3.66 + w.append(" };\n");
3.67 w.append("}\n");
3.68 } finally {
3.69 w.close();
3.70 @@ -203,25 +220,10 @@
3.71 w.write("public " + className + " applyBindings() {\n");
3.72 w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
3.73 w.write(className + ".class, this, ");
3.74 - w.write("new String[] {\n");
3.75 - String sep = "";
3.76 - for (String n : propsGetSet) {
3.77 - w.write(sep);
3.78 - if (n == null) {
3.79 - w.write(" null");
3.80 - } else {
3.81 - w.write(" \"" + n + "\"");
3.82 - }
3.83 - sep = ",\n";
3.84 - }
3.85 - w.write("\n }, new String[] {\n");
3.86 - sep = "";
3.87 - for (String n : functions) {
3.88 - w.write(sep);
3.89 - w.write(n);
3.90 - sep = ",\n";
3.91 - }
3.92 - w.write("\n });\n return this;\n}\n");
3.93 + writeStringArray(propsGetSet, w);
3.94 + w.append(", ");
3.95 + writeStringArray(functions, w);
3.96 + w.write(");\n return this;\n}\n");
3.97
3.98 w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
3.99 w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
3.100 @@ -537,6 +539,7 @@
3.101 private String typeName(Element where, Property p) {
3.102 String ret;
3.103 boolean isModel = false;
3.104 + boolean isEnum = false;
3.105 try {
3.106 ret = p.type().getName();
3.107 } catch (MirroredTypeException ex) {
3.108 @@ -546,9 +549,13 @@
3.109 if (m != null) {
3.110 ret = findPkgName(e) + '.' + m.className();
3.111 isModel = true;
3.112 + models.put(e, m.className());
3.113 } else {
3.114 ret = tm.toString();
3.115 }
3.116 + TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
3.117 + enm = processingEnv.getTypeUtils().erasure(enm);
3.118 + isEnum = processingEnv.getTypeUtils().isSubtype(tm, enm);
3.119 }
3.120 if (p.array()) {
3.121 String bt = findBoxedType(ret);
3.122 @@ -556,7 +563,7 @@
3.123 return bt;
3.124 }
3.125 }
3.126 - if (!isModel && !"java.lang.String".equals(ret)) {
3.127 + if (!isModel && !"java.lang.String".equals(ret) && !isEnum) {
3.128 String bt = findBoxedType(ret);
3.129 if (bt == null) {
3.130 processingEnv.getMessager().printMessage(
3.131 @@ -664,8 +671,8 @@
3.132 body.append(");\n");
3.133 body.append("}\n");
3.134
3.135 - functions.add('\"' + n + '\"');
3.136 - functions.add('\"' + n + "__VLjava_lang_Object_2Ljava_lang_Object_2" + '\"');
3.137 + functions.add(n);
3.138 + functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
3.139 }
3.140 return true;
3.141 }
3.142 @@ -687,18 +694,21 @@
3.143 params.append('"').append(id).append('"');
3.144 continue;
3.145 }
3.146 - toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString";
3.147 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(";
3.148 }
3.149 if (ve.asType().getKind() == TypeKind.DOUBLE) {
3.150 - toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble";
3.151 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(";
3.152 }
3.153 if (ve.asType().getKind() == TypeKind.INT) {
3.154 - toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt";
3.155 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt(";
3.156 + }
3.157 + if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
3.158 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toModel(" + ve.asType() + ".class, ";
3.159 }
3.160
3.161 if (toCall != null) {
3.162 - params.append(toCall).append('(');
3.163 - if (dataName != null && ve.getSimpleName().contentEquals("data")) {
3.164 + params.append(toCall);
3.165 + if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
3.166 params.append(dataName);
3.167 params.append(", null");
3.168 } else {
3.169 @@ -726,4 +736,35 @@
3.170 }
3.171 return params;
3.172 }
3.173 +
3.174 + private boolean isModel(TypeMirror tm) {
3.175 + final Element e = processingEnv.getTypeUtils().asElement(tm);
3.176 + if (e == null) {
3.177 + return false;
3.178 + }
3.179 + for (Element ch : e.getEnclosedElements()) {
3.180 + if (ch.getKind() == ElementKind.METHOD) {
3.181 + ExecutableElement ee = (ExecutableElement)ch;
3.182 + if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
3.183 + return true;
3.184 + }
3.185 + }
3.186 + }
3.187 + return models.values().contains(e.getSimpleName().toString());
3.188 + }
3.189 +
3.190 + private void writeStringArray(List<String> strings, Writer w) throws IOException {
3.191 + w.write("new String[] {\n");
3.192 + String sep = "";
3.193 + for (String n : strings) {
3.194 + w.write(sep);
3.195 + if (n == null) {
3.196 + w.write(" null");
3.197 + } else {
3.198 + w.write(" \"" + n + "\"");
3.199 + }
3.200 + sep = ",\n";
3.201 + }
3.202 + w.write("\n }");
3.203 + }
3.204 }
4.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Tue Apr 02 15:52:25 2013 +0200
4.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Wed Apr 03 09:17:58 2013 +0200
4.3 @@ -36,7 +36,8 @@
4.4 @Page(xhtml="Knockout.xhtml", className="KnockoutModel", properties={
4.5 @Property(name="name", type=String.class),
4.6 @Property(name="results", type=String.class, array = true),
4.7 - @Property(name="callbackCount", type=int.class)
4.8 + @Property(name="callbackCount", type=int.class),
4.9 + @Property(name="people", type=PersonImpl.class, array = true)
4.10 })
4.11 public class KnockoutTest {
4.12
4.13 @@ -98,11 +99,83 @@
4.14 assert cnt == 2 : "Two children now, but was " + cnt;
4.15 }
4.16
4.17 + @HtmlFragment(
4.18 + "<ul id='ul' data-bind='foreach: people'>\n"
4.19 + + " <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
4.20 + + "</ul>\n"
4.21 + )
4.22 + @BrwsrTest public void displayContentOfArrayOfPeople() {
4.23 + KnockoutModel m = new KnockoutModel();
4.24 +
4.25 + final Person first = new Person();
4.26 + first.setFirstName("first");
4.27 + m.getPeople().add(first);
4.28 +
4.29 + m.applyBindings();
4.30 +
4.31 + int cnt = countChildren("ul");
4.32 + assert cnt == 1 : "One child, but was " + cnt;
4.33 +
4.34 + final Person second = new Person();
4.35 + second.setFirstName("second");
4.36 + m.getPeople().add(second);
4.37 +
4.38 + cnt = countChildren("ul");
4.39 + assert cnt == 2 : "Two children now, but was " + cnt;
4.40 +
4.41 + triggerChildClick("ul", 1);
4.42 +
4.43 + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
4.44 +
4.45 + cnt = countChildren("ul");
4.46 + assert cnt == 1 : "Again one child, but was " + cnt;
4.47 +
4.48 + String txt = childText("ul", 0);
4.49 + assert "first".equals(txt) : "Expecting 'first': " + txt;
4.50 +
4.51 + first.setFirstName("changed");
4.52 +
4.53 + txt = childText("ul", 0);
4.54 + assert "changed".equals(txt) : "Expecting 'changed': " + txt;
4.55 + }
4.56 +
4.57 + @HtmlFragment(
4.58 + "<ul id='ul' data-bind='foreach: people'>\n"
4.59 + + " <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
4.60 + + "</ul>\n"
4.61 + )
4.62 + @BrwsrTest public void onPersonFunction() {
4.63 + KnockoutModel m = new KnockoutModel();
4.64 +
4.65 + final Person first = new Person();
4.66 + first.setFirstName("first");
4.67 + first.setSex(Sex.MALE);
4.68 + m.getPeople().add(first);
4.69 +
4.70 +
4.71 + m.applyBindings();
4.72 +
4.73 + int cnt = countChildren("ul");
4.74 + assert cnt == 1 : "One child, but was " + cnt;
4.75 +
4.76 +
4.77 + triggerChildClick("ul", 0);
4.78 +
4.79 + assert first.getSex() == Sex.FEMALE : "Transverted to female: " + first.getSex();
4.80 + }
4.81 +
4.82 @OnFunction
4.83 static void call(KnockoutModel m, String data) {
4.84 m.setName(data);
4.85 m.setCallbackCount(m.getCallbackCount() + 1);
4.86 }
4.87 +
4.88 + @OnFunction
4.89 + static void removePerson(KnockoutModel model, Person data) {
4.90 + model.setCallbackCount(model.getCallbackCount() + 1);
4.91 + model.getPeople().remove(data);
4.92 + }
4.93 +
4.94
4.95 @ComputedProperty
4.96 static String helloMessage(String name) {
4.97 @@ -133,4 +206,11 @@
4.98 + "e.children[pos].dispatchEvent(ev);\n "
4.99 )
4.100 private static native void triggerChildClick(String id, int pos);
4.101 +
4.102 + @JavaScriptBody(args = { "id", "pos" }, body =
4.103 + "var e = window.document.getElementById(id);\n "
4.104 + + "var t = e.children[pos].innerHTML;\n "
4.105 + + "return t ? t : null;"
4.106 + )
4.107 + private static native String childText(String id, int pos);
4.108 }
5.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Tue Apr 02 15:52:25 2013 +0200
5.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Wed Apr 03 09:17:58 2013 +0200
5.3 @@ -197,6 +197,10 @@
5.4 static class MockKnockout extends Knockout {
5.5 List<String> mutated = new ArrayList<>();
5.6
5.7 + MockKnockout() {
5.8 + super(null);
5.9 + }
5.10 +
5.11 @Override
5.12 public void valueHasMutated(String prop) {
5.13 mutated.add(prop);
6.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Tue Apr 02 15:52:25 2013 +0200
6.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Wed Apr 03 09:17:58 2013 +0200
6.3 @@ -19,6 +19,7 @@
6.4
6.5 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
6.6 import org.apidesign.bck2brwsr.htmlpage.api.Model;
6.7 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
6.8 import org.apidesign.bck2brwsr.htmlpage.api.Property;
6.9
6.10 /**
6.11 @@ -28,7 +29,7 @@
6.12 @Model(className = "Person", properties = {
6.13 @Property(name = "firstName", type = String.class),
6.14 @Property(name = "lastName", type = String.class),
6.15 - @Property(name = "male", type = boolean.class)
6.16 + @Property(name = "sex", type = Sex.class)
6.17 })
6.18 final class PersonImpl {
6.19 @ComputedProperty
6.20 @@ -37,7 +38,16 @@
6.21 }
6.22
6.23 @ComputedProperty
6.24 - public static String sex(boolean male) {
6.25 - return male ? "Male" : "Female";
6.26 + public static String sexType(Sex sex) {
6.27 + return sex == null ? "unknown" : sex.toString();
6.28 + }
6.29 +
6.30 + @OnFunction
6.31 + static void changeSex(Person p) {
6.32 + if (p.getSex() == Sex.MALE) {
6.33 + p.setSex(Sex.FEMALE);
6.34 + } else {
6.35 + p.setSex(Sex.MALE);
6.36 + }
6.37 }
6.38 }
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Sex.java Wed Apr 03 09:17:58 2013 +0200
7.3 @@ -0,0 +1,26 @@
7.4 +/**
7.5 + * Back 2 Browser Bytecode Translator
7.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
7.7 + *
7.8 + * This program is free software: you can redistribute it and/or modify
7.9 + * it under the terms of the GNU General Public License as published by
7.10 + * the Free Software Foundation, version 2 of the License.
7.11 + *
7.12 + * This program is distributed in the hope that it will be useful,
7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.15 + * GNU General Public License for more details.
7.16 + *
7.17 + * You should have received a copy of the GNU General Public License
7.18 + * along with this program. Look for COPYING file in the top folder.
7.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
7.20 + */
7.21 +package org.apidesign.bck2brwsr.htmlpage;
7.22 +
7.23 +/**
7.24 + *
7.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
7.26 + */
7.27 +public enum Sex {
7.28 + MALE, FEMALE;
7.29 +}
8.1 --- a/javaquery/demo-calculator-dynamic/nbactions.xml Tue Apr 02 15:52:25 2013 +0200
8.2 +++ b/javaquery/demo-calculator-dynamic/nbactions.xml Wed Apr 03 09:17:58 2013 +0200
8.3 @@ -23,7 +23,7 @@
8.4 <actionName>run</actionName>
8.5 <goals>
8.6 <goal>process-classes</goal>
8.7 - <goal>org.apidesign.bck2brwsr:mojo:0.5-SNAPSHOT:brwsr</goal>
8.8 + <goal>org.apidesign.bck2brwsr:mojo:0.6-SNAPSHOT:brwsr</goal>
8.9 </goals>
8.10 </action>
8.11 </actions>
9.1 --- a/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Tue Apr 02 15:52:25 2013 +0200
9.2 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Wed Apr 03 09:17:58 2013 +0200
9.3 @@ -36,7 +36,7 @@
9.4 @Property(name = "display", type = double.class),
9.5 @Property(name = "operation", type = String.class),
9.6 @Property(name = "hover", type = boolean.class),
9.7 - @Property(name = "history", type = double.class, array = true)
9.8 + @Property(name = "history", type = HistoryImpl.class, array = true)
9.9 })
9.10 public class Calc {
9.11 static {
9.12 @@ -74,19 +74,22 @@
9.13 c.getDisplay()
9.14 );
9.15 c.setDisplay(newValue);
9.16 - if (!c.getHistory().contains(newValue)) {
9.17 - c.getHistory().add(newValue);
9.18 + if (!containsValue(c.getHistory(), newValue)) {
9.19 + History h = new History();
9.20 + h.setValue(newValue);
9.21 + h.setOperation(c.getOperation());
9.22 + c.getHistory().add(h);
9.23 }
9.24 c.setMemory(0);
9.25 }
9.26
9.27 @OnFunction
9.28 - static void recoverMemory(Calculator c, double data) {
9.29 - c.setDisplay(data);
9.30 + static void recoverMemory(Calculator c, History data) {
9.31 + c.setDisplay(data.getValue());
9.32 }
9.33
9.34 @OnFunction
9.35 - static void removeMemory(Calculator c, double data) {
9.36 + static void removeMemory(Calculator c, History data) {
9.37 c.getHistory().remove(data);
9.38 }
9.39
9.40 @@ -131,4 +134,13 @@
9.41 static boolean emptyHistory(List<?> history) {
9.42 return history.isEmpty();
9.43 }
9.44 +
9.45 + private static boolean containsValue(List<History> arr, final double newValue) {
9.46 + for (History history : arr) {
9.47 + if (history.getValue() == newValue) {
9.48 + return true;
9.49 + }
9.50 + }
9.51 + return false;
9.52 + }
9.53 }
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/HistoryImpl.java Wed Apr 03 09:17:58 2013 +0200
10.3 @@ -0,0 +1,43 @@
10.4 +/**
10.5 + * Back 2 Browser Bytecode Translator
10.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
10.7 + *
10.8 + * This program is free software: you can redistribute it and/or modify
10.9 + * it under the terms of the GNU General Public License as published by
10.10 + * the Free Software Foundation, version 2 of the License.
10.11 + *
10.12 + * This program is distributed in the hope that it will be useful,
10.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
10.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10.15 + * GNU General Public License for more details.
10.16 + *
10.17 + * You should have received a copy of the GNU General Public License
10.18 + * along with this program. Look for COPYING file in the top folder.
10.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
10.20 + */
10.21 +package org.apidesign.bck2brwsr.demo.calc;
10.22 +
10.23 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
10.24 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
10.25 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
10.26 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
10.27 +
10.28 +/**
10.29 + *
10.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
10.31 + */
10.32 +@Model(className = "History", properties = {
10.33 + @Property(name = "value", type = double.class),
10.34 + @Property(name = "operation", type = String.class)
10.35 +})
10.36 +public class HistoryImpl {
10.37 + @ComputedProperty
10.38 + static String resultOf(String operation) {
10.39 + return "result of " + operation;
10.40 + }
10.41 +
10.42 + @OnFunction
10.43 + static void twice(History data) {
10.44 + data.setValue(2.0 * data.getValue());
10.45 + }
10.46 +}
11.1 --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Tue Apr 02 15:52:25 2013 +0200
11.2 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Wed Apr 03 09:17:58 2013 +0200
11.3 @@ -83,9 +83,11 @@
11.4 <div data-bind="if: emptyHistory">No results yet.</div>
11.5 <ul data-bind="foreach: history">
11.6 <li>
11.7 - <span data-bind="text: $data"></span> -
11.8 + <span data-bind="text: $data.value"></span> -
11.9 <a href="#" data-bind="click: $root.recoverMemory">Use</a>
11.10 <a href="#" data-bind="click: $root.removeMemory">Remove</a>
11.11 + <a href="#" data-bind="click: $data.twice">Double</a> -
11.12 + <span data-bind="text: $data.resultOf"></span>
11.13 </li>
11.14 </ul>
11.15