Merging the array/complex type knockout mapping into default branch for version 0.5
1.1 --- a/javaquery/api/pom.xml Mon Mar 25 13:33:03 2013 +0100
1.2 +++ b/javaquery/api/pom.xml Mon Mar 25 18:36:50 2013 +0100
1.3 @@ -18,8 +18,8 @@
1.4 <artifactId>maven-compiler-plugin</artifactId>
1.5 <version>2.3.2</version>
1.6 <configuration>
1.7 - <source>1.6</source>
1.8 - <target>1.6</target>
1.9 + <source>1.7</source>
1.10 + <target>1.7</target>
1.11 </configuration>
1.12 </plugin>
1.13 <plugin>
2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Mon Mar 25 13:33:03 2013 +0100
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Mon Mar 25 18:36:50 2013 +0100
2.3 @@ -43,7 +43,8 @@
2.4 }
2.5
2.6 @JavaScriptBody(args = { "object", "property" },
2.7 - body = "var p = object[property]; return p ? p : null;"
2.8 + body = "if (property === null) return object;\n"
2.9 + + "var p = object[property]; return p ? p : null;"
2.10 )
2.11 private static Object getProperty(Object object, String property) {
2.12 return null;
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/KOList.java Mon Mar 25 18:36:50 2013 +0100
3.3 @@ -0,0 +1,113 @@
3.4 +/**
3.5 + * Back 2 Browser Bytecode Translator
3.6 + * Copyright (C) 2012 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.
3.16 + *
3.17 + * You should have received a copy of the GNU General Public License
3.18 + * along with this program. Look for COPYING file in the top folder.
3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
3.20 + */
3.21 +package org.apidesign.bck2brwsr.htmlpage;
3.22 +
3.23 +import java.util.ArrayList;
3.24 +import java.util.Collection;
3.25 +import org.apidesign.bck2brwsr.core.JavaScriptOnly;
3.26 +
3.27 +/**
3.28 + *
3.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
3.30 + */
3.31 +public final class KOList<T> extends ArrayList<T> {
3.32 + private final String name;
3.33 + private final String[] deps;
3.34 + private Knockout model;
3.35 +
3.36 + public KOList(String name, String... deps) {
3.37 + this.name = name;
3.38 + this.deps = deps;
3.39 + }
3.40 +
3.41 + public void assign(Knockout model) {
3.42 + this.model = model;
3.43 + }
3.44 +
3.45 + @Override
3.46 + public boolean add(T e) {
3.47 + boolean ret = super.add(e);
3.48 + notifyChange();
3.49 + return ret;
3.50 + }
3.51 +
3.52 + @Override
3.53 + public boolean remove(Object o) {
3.54 + boolean ret = super.remove(o);
3.55 + notifyChange();
3.56 + return ret;
3.57 + }
3.58 +
3.59 + @Override
3.60 + public void clear() {
3.61 + super.clear();
3.62 + notifyChange();
3.63 + }
3.64 +
3.65 + @Override
3.66 + public boolean removeAll(Collection<?> c) {
3.67 + boolean ret = super.removeAll(c);
3.68 + notifyChange();
3.69 + return ret;
3.70 + }
3.71 +
3.72 + @Override
3.73 + public boolean retainAll(Collection<?> c) {
3.74 + boolean ret = super.retainAll(c);
3.75 + notifyChange();
3.76 + return ret;
3.77 + }
3.78 +
3.79 + @Override
3.80 + public T set(int index, T element) {
3.81 + T ret = super.set(index, element);
3.82 + notifyChange();
3.83 + return ret;
3.84 + }
3.85 +
3.86 + @Override
3.87 + public void add(int index, T element) {
3.88 + super.add(index, element);
3.89 + notifyChange();
3.90 + }
3.91 +
3.92 + @Override
3.93 + public T remove(int index) {
3.94 + T ret = super.remove(index);
3.95 + notifyChange();
3.96 + return ret;
3.97 + }
3.98 +
3.99 +
3.100 +
3.101 + @JavaScriptOnly(name = "koArray", value = "function() { return this.toArray___3Ljava_lang_Object_2(); }")
3.102 + private static native int koArray();
3.103 +
3.104 + private void notifyChange() {
3.105 + Knockout m = model;
3.106 + if (m == null) {
3.107 + return;
3.108 + }
3.109 + m.valueHasMutated(name);
3.110 + for (String dependant : deps) {
3.111 + m.valueHasMutated(dependant);
3.112 + }
3.113 + }
3.114 +
3.115 +
3.116 +}
4.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Mar 25 13:33:03 2013 +0100
4.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Mar 25 18:36:50 2013 +0100
4.3 @@ -18,6 +18,7 @@
4.4 package org.apidesign.bck2brwsr.htmlpage;
4.5
4.6 import java.lang.reflect.Method;
4.7 +import java.util.List;
4.8 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
4.9 import org.apidesign.bck2brwsr.core.JavaScriptBody;
4.10
4.11 @@ -29,12 +30,13 @@
4.12 public class Knockout {
4.13 /** used by tests */
4.14 static Knockout next;
4.15 -
4.16 +
4.17 Knockout() {
4.18 }
4.19
4.20 public static <M> Knockout applyBindings(
4.21 - Class<M> modelClass, M model, String[] propsGettersAndSetters
4.22 + Class<M> modelClass, M model, String[] propsGettersAndSetters,
4.23 + String[] methodsAndSignatures
4.24 ) {
4.25 Knockout bindings = next;
4.26 next = null;
4.27 @@ -47,12 +49,18 @@
4.28 bind(bindings, model, propsGettersAndSetters[i],
4.29 propsGettersAndSetters[i + 1],
4.30 propsGettersAndSetters[i + 2],
4.31 - getter.getReturnType().isPrimitive()
4.32 + getter.getReturnType().isPrimitive(),
4.33 + List.class.isAssignableFrom(getter.getReturnType())
4.34 );
4.35 } catch (NoSuchMethodException ex) {
4.36 throw new IllegalStateException(ex.getMessage());
4.37 }
4.38 }
4.39 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
4.40 + expose(
4.41 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]
4.42 + );
4.43 + }
4.44 applyBindings(bindings);
4.45 return bindings;
4.46 }
4.47 @@ -68,10 +76,11 @@
4.48 public static void triggerEvent(String id, String ev) {
4.49 }
4.50
4.51 - @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive" }, body =
4.52 + @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
4.53 "var bnd = {\n"
4.54 + " 'read': function() {\n"
4.55 + " var v = model[getter]();\n"
4.56 + + " if (array) v = v.koArray();\n"
4.57 + " return v;\n"
4.58 + " },\n"
4.59 + " 'owner': bindings\n"
4.60 @@ -84,7 +93,15 @@
4.61 + "bindings[prop] = ko['computed'](bnd);"
4.62 )
4.63 private static void bind(
4.64 - Object bindings, Object model, String prop, String getter, String setter, boolean primitive
4.65 + Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
4.66 + ) {
4.67 + }
4.68 +
4.69 + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
4.70 + "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
4.71 + )
4.72 + private static void expose(
4.73 + Object bindings, Object model, String prop, String sig
4.74 ) {
4.75 }
4.76
5.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Mar 25 13:33:03 2013 +0100
5.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Mar 25 18:36:50 2013 +0100
5.3 @@ -20,6 +20,7 @@
5.4 import java.io.IOException;
5.5 import java.io.InputStream;
5.6 import java.io.OutputStreamWriter;
5.7 +import java.io.StringWriter;
5.8 import java.io.Writer;
5.9 import java.util.ArrayList;
5.10 import java.util.Collection;
5.11 @@ -47,11 +48,14 @@
5.12 import javax.lang.model.type.MirroredTypeException;
5.13 import javax.lang.model.type.TypeKind;
5.14 import javax.lang.model.type.TypeMirror;
5.15 +import javax.lang.model.util.Types;
5.16 import javax.tools.Diagnostic;
5.17 import javax.tools.FileObject;
5.18 import javax.tools.StandardLocation;
5.19 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
5.20 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
5.21 import org.apidesign.bck2brwsr.htmlpage.api.On;
5.22 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
5.23 import org.apidesign.bck2brwsr.htmlpage.api.Page;
5.24 import org.apidesign.bck2brwsr.htmlpage.api.Property;
5.25 import org.openide.util.lookup.ServiceProvider;
5.26 @@ -63,89 +67,26 @@
5.27 */
5.28 @ServiceProvider(service=Processor.class)
5.29 @SupportedAnnotationTypes({
5.30 + "org.apidesign.bck2brwsr.htmlpage.api.Model",
5.31 "org.apidesign.bck2brwsr.htmlpage.api.Page",
5.32 + "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
5.33 "org.apidesign.bck2brwsr.htmlpage.api.On"
5.34 })
5.35 public final class PageProcessor extends AbstractProcessor {
5.36 @Override
5.37 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
5.38 - for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
5.39 - Page p = e.getAnnotation(Page.class);
5.40 - if (p == null) {
5.41 - continue;
5.42 - }
5.43 - PackageElement pe = (PackageElement)e.getEnclosingElement();
5.44 - String pkg = pe.getQualifiedName().toString();
5.45 -
5.46 - ProcessPage pp;
5.47 - try {
5.48 - InputStream is = openStream(pkg, p.xhtml());
5.49 - pp = ProcessPage.readPage(is);
5.50 - is.close();
5.51 - } catch (IOException iOException) {
5.52 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
5.53 - return false;
5.54 - }
5.55 - Writer w;
5.56 - String className = p.className();
5.57 - if (className.isEmpty()) {
5.58 - int indx = p.xhtml().indexOf('.');
5.59 - className = p.xhtml().substring(0, indx);
5.60 - }
5.61 - try {
5.62 - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
5.63 - w = new OutputStreamWriter(java.openOutputStream());
5.64 - try {
5.65 - w.append("package " + pkg + ";\n");
5.66 - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
5.67 - w.append("final class ").append(className).append(" {\n");
5.68 - w.append(" private boolean locked;\n");
5.69 - if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
5.70 - return false;
5.71 - }
5.72 - for (String id : pp.ids()) {
5.73 - String tag = pp.tagNameForId(id);
5.74 - String type = type(tag);
5.75 - w.append(" ").append("public final ").
5.76 - append(type).append(' ').append(cnstnt(id)).append(" = new ").
5.77 - append(type).append("(\"").append(id).append("\");\n");
5.78 - }
5.79 - List<String> propsGetSet = new ArrayList<String>();
5.80 - Map<String,Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
5.81 - generateComputedProperties(w, e.getEnclosedElements(), propsGetSet, propsDeps);
5.82 - generateProperties(w, p.properties(), propsGetSet, propsDeps);
5.83 - w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
5.84 - if (!propsGetSet.isEmpty()) {
5.85 - w.write("public " + className + " applyBindings() {\n");
5.86 - w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
5.87 - w.write(className + ".class, this, ");
5.88 - w.write("new String[] {\n");
5.89 - String sep = "";
5.90 - for (String n : propsGetSet) {
5.91 - w.write(sep);
5.92 - if (n == null) {
5.93 - w.write(" null");
5.94 - } else {
5.95 - w.write(" \"" + n + "\"");
5.96 - }
5.97 - sep = ",\n";
5.98 - }
5.99 - w.write("\n });\n return this;\n}\n");
5.100 -
5.101 - w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
5.102 - w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
5.103 - w.write("}\n");
5.104 - }
5.105 - w.append("}\n");
5.106 - } finally {
5.107 - w.close();
5.108 - }
5.109 - } catch (IOException ex) {
5.110 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
5.111 - return false;
5.112 + boolean ok = true;
5.113 + for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
5.114 + if (!processModel(e)) {
5.115 + ok = false;
5.116 }
5.117 }
5.118 - return true;
5.119 + for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
5.120 + if (!processPage(e)) {
5.121 + ok = false;
5.122 + }
5.123 + }
5.124 + return ok;
5.125 }
5.126
5.127 private InputStream openStream(String pkg, String name) throws IOException {
5.128 @@ -157,6 +98,145 @@
5.129 return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
5.130 }
5.131 }
5.132 +
5.133 + private boolean processModel(Element e) {
5.134 + boolean ok = true;
5.135 + Model m = e.getAnnotation(Model.class);
5.136 + if (m == null) {
5.137 + return true;
5.138 + }
5.139 + String pkg = findPkgName(e);
5.140 + Writer w;
5.141 + String className = m.className();
5.142 + try {
5.143 + StringWriter body = new StringWriter();
5.144 + List<String> propsGetSet = new ArrayList<>();
5.145 + Map<String, Collection<String>> propsDeps = new HashMap<>();
5.146 + if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
5.147 + ok = false;
5.148 + }
5.149 + if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps)) {
5.150 + ok = false;
5.151 + }
5.152 + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
5.153 + w = new OutputStreamWriter(java.openOutputStream());
5.154 + try {
5.155 + w.append("package " + pkg + ";\n");
5.156 + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
5.157 + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
5.158 + w.append("final class ").append(className).append(" {\n");
5.159 + w.append(" private Object json;\n");
5.160 + w.append(" private boolean locked;\n");
5.161 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
5.162 + w.append(body.toString());
5.163 + w.append("}\n");
5.164 + } finally {
5.165 + w.close();
5.166 + }
5.167 + } catch (IOException ex) {
5.168 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
5.169 + return false;
5.170 + }
5.171 + return ok;
5.172 + }
5.173 +
5.174 + private boolean processPage(Element e) {
5.175 + boolean ok = true;
5.176 + Page p = e.getAnnotation(Page.class);
5.177 + if (p == null) {
5.178 + return true;
5.179 + }
5.180 + String pkg = findPkgName(e);
5.181 +
5.182 + ProcessPage pp;
5.183 + try (InputStream is = openStream(pkg, p.xhtml())) {
5.184 + pp = ProcessPage.readPage(is);
5.185 + is.close();
5.186 + } catch (IOException iOException) {
5.187 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
5.188 + ok = false;
5.189 + pp = null;
5.190 + }
5.191 + Writer w;
5.192 + String className = p.className();
5.193 + if (className.isEmpty()) {
5.194 + int indx = p.xhtml().indexOf('.');
5.195 + className = p.xhtml().substring(0, indx);
5.196 + }
5.197 + try {
5.198 + StringWriter body = new StringWriter();
5.199 + List<String> propsGetSet = new ArrayList<>();
5.200 + List<String> functions = new ArrayList<>();
5.201 + Map<String, Collection<String>> propsDeps = new HashMap<>();
5.202 + if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
5.203 + ok = false;
5.204 + }
5.205 + if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps)) {
5.206 + ok = false;
5.207 + }
5.208 + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
5.209 + ok = false;
5.210 + }
5.211 +
5.212 + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
5.213 + w = new OutputStreamWriter(java.openOutputStream());
5.214 + try {
5.215 + w.append("package " + pkg + ";\n");
5.216 + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
5.217 + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
5.218 + w.append("final class ").append(className).append(" {\n");
5.219 + w.append(" private boolean locked;\n");
5.220 + if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
5.221 + ok = false;
5.222 + } else {
5.223 + for (String id : pp.ids()) {
5.224 + String tag = pp.tagNameForId(id);
5.225 + String type = type(tag);
5.226 + w.append(" ").append("public final ").
5.227 + append(type).append(' ').append(cnstnt(id)).append(" = new ").
5.228 + append(type).append("(\"").append(id).append("\");\n");
5.229 + }
5.230 + }
5.231 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
5.232 + w.append(body.toString());
5.233 + if (!propsGetSet.isEmpty()) {
5.234 + w.write("public " + className + " applyBindings() {\n");
5.235 + w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
5.236 + w.write(className + ".class, this, ");
5.237 + w.write("new String[] {\n");
5.238 + String sep = "";
5.239 + for (String n : propsGetSet) {
5.240 + w.write(sep);
5.241 + if (n == null) {
5.242 + w.write(" null");
5.243 + } else {
5.244 + w.write(" \"" + n + "\"");
5.245 + }
5.246 + sep = ",\n";
5.247 + }
5.248 + w.write("\n }, new String[] {\n");
5.249 + sep = "";
5.250 + for (String n : functions) {
5.251 + w.write(sep);
5.252 + w.write(n);
5.253 + sep = ",\n";
5.254 + }
5.255 + w.write("\n });\n return this;\n}\n");
5.256 +
5.257 + w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
5.258 + w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
5.259 + w.write("}\n");
5.260 + }
5.261 + w.append("}\n");
5.262 + } finally {
5.263 + w.close();
5.264 + }
5.265 + } catch (IOException ex) {
5.266 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
5.267 + return false;
5.268 + }
5.269 + return ok;
5.270 + }
5.271
5.272 private static String type(String tag) {
5.273 if (tag.equals("title")) {
5.274 @@ -184,6 +264,7 @@
5.275 private boolean initializeOnClick(
5.276 String className, TypeElement type, Writer w, ProcessPage pp
5.277 ) throws IOException {
5.278 + boolean ok = true;
5.279 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
5.280 { //for (Element clazz : pe.getEnclosedElements()) {
5.281 // if (clazz.getKind() != ElementKind.CLASS) {
5.282 @@ -196,58 +277,27 @@
5.283 On oc = method.getAnnotation(On.class);
5.284 if (oc != null) {
5.285 for (String id : oc.id()) {
5.286 + if (pp == null) {
5.287 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
5.288 + ok = false;
5.289 + continue;
5.290 + }
5.291 if (pp.tagNameForId(id) == null) {
5.292 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
5.293 - return false;
5.294 + ok = false;
5.295 + continue;
5.296 }
5.297 ExecutableElement ee = (ExecutableElement)method;
5.298 - StringBuilder params = new StringBuilder();
5.299 - {
5.300 - boolean first = true;
5.301 - for (VariableElement ve : ee.getParameters()) {
5.302 - if (!first) {
5.303 - params.append(", ");
5.304 - }
5.305 - first = false;
5.306 - if (ve.asType() == stringType) {
5.307 - if (ve.getSimpleName().contentEquals("id")) {
5.308 - params.append('"').append(id).append('"');
5.309 - continue;
5.310 - }
5.311 - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(ev, \"");
5.312 - params.append(ve.getSimpleName().toString());
5.313 - params.append("\")");
5.314 - continue;
5.315 - }
5.316 - if (processingEnv.getTypeUtils().getPrimitiveType(TypeKind.DOUBLE) == ve.asType()) {
5.317 - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(ev, \"");
5.318 - params.append(ve.getSimpleName().toString());
5.319 - params.append("\")");
5.320 - continue;
5.321 - }
5.322 - String rn = ve.asType().toString();
5.323 - int last = rn.lastIndexOf('.');
5.324 - if (last >= 0) {
5.325 - rn = rn.substring(last + 1);
5.326 - }
5.327 - if (rn.equals(className)) {
5.328 - params.append(className).append(".this");
5.329 - continue;
5.330 - }
5.331 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
5.332 - "@On method can only accept String named 'id' or " + className + " arguments",
5.333 - ee
5.334 - );
5.335 - return false;
5.336 - }
5.337 - }
5.338 + CharSequence params = wrapParams(ee, id, className, "ev", null);
5.339 if (!ee.getModifiers().contains(Modifier.STATIC)) {
5.340 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
5.341 - return false;
5.342 + ok = false;
5.343 + continue;
5.344 }
5.345 if (ee.getModifiers().contains(Modifier.PRIVATE)) {
5.346 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
5.347 - return false;
5.348 + ok = false;
5.349 + continue;
5.350 }
5.351 w.append(" OnEvent." + oc.event()).append(".of(").append(cnstnt(id)).
5.352 append(").perform(new OnDispatch(" + dispatchCnt + "));\n");
5.353 @@ -278,7 +328,7 @@
5.354
5.355
5.356 }
5.357 - return true;
5.358 + return ok;
5.359 }
5.360
5.361 @Override
5.362 @@ -292,8 +342,7 @@
5.363
5.364 Element cls = findClass(element);
5.365 Page p = cls.getAnnotation(Page.class);
5.366 - PackageElement pe = (PackageElement) cls.getEnclosingElement();
5.367 - String pkg = pe.getQualifiedName().toString();
5.368 + String pkg = findPkgName(cls);
5.369 ProcessPage pp;
5.370 try {
5.371 InputStream is = openStream(pkg, p.xhtml());
5.372 @@ -303,7 +352,7 @@
5.373 return Collections.emptyList();
5.374 }
5.375
5.376 - List<Completion> cc = new ArrayList<Completion>();
5.377 + List<Completion> cc = new ArrayList<>();
5.378 userText = userText.substring(1);
5.379 for (String id : pp.ids()) {
5.380 if (id.startsWith(userText)) {
5.381 @@ -324,44 +373,70 @@
5.382 return e.getEnclosingElement();
5.383 }
5.384
5.385 - private static void generateProperties(
5.386 - Writer w, Property[] properties, Collection<String> props,
5.387 - Map<String,Collection<String>> deps
5.388 + private boolean generateProperties(
5.389 + Element where,
5.390 + Writer w, Property[] properties,
5.391 + Collection<String> props, Map<String,Collection<String>> deps
5.392 ) throws IOException {
5.393 + boolean ok = true;
5.394 for (Property p : properties) {
5.395 - final String tn = typeName(p);
5.396 - String[] gs = toGetSet(p.name(), tn);
5.397 + final String tn;
5.398 + tn = typeName(where, p);
5.399 + String[] gs = toGetSet(p.name(), tn, p.array());
5.400
5.401 - w.write("private " + tn + " prop_" + p.name() + ";\n");
5.402 - w.write("public " + tn + " " + gs[0] + "() {\n");
5.403 - w.write(" if (locked) throw new IllegalStateException();\n");
5.404 - w.write(" return prop_" + p.name() + ";\n");
5.405 - w.write("}\n");
5.406 - w.write("public void " + gs[1] + "(" + tn + " v) {\n");
5.407 - w.write(" if (locked) throw new IllegalStateException();\n");
5.408 - w.write(" prop_" + p.name() + " = v;\n");
5.409 - w.write(" if (ko != null) {\n");
5.410 - w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n");
5.411 - final Collection<String> dependants = deps.get(p.name());
5.412 - if (dependants != null) {
5.413 - for (String depProp : dependants) {
5.414 - w.write(" ko.valueHasMutated(\"" + depProp + "\");\n");
5.415 + if (p.array()) {
5.416 + w.write("private KOList<" + tn + "> prop_" + p.name() + " = new KOList<" + tn + ">(\""
5.417 + + p.name() + "\"");
5.418 + final Collection<String> dependants = deps.get(p.name());
5.419 + if (dependants != null) {
5.420 + for (String depProp : dependants) {
5.421 + w.write(", ");
5.422 + w.write('\"');
5.423 + w.write(depProp);
5.424 + w.write('\"');
5.425 + }
5.426 }
5.427 + w.write(");\n");
5.428 + w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n");
5.429 + w.write(" if (locked) throw new IllegalStateException();\n");
5.430 + w.write(" prop_" + p.name() + ".assign(ko);\n");
5.431 + w.write(" return prop_" + p.name() + ";\n");
5.432 + w.write("}\n");
5.433 + } else {
5.434 + w.write("private " + tn + " prop_" + p.name() + ";\n");
5.435 + w.write("public " + tn + " " + gs[0] + "() {\n");
5.436 + w.write(" if (locked) throw new IllegalStateException();\n");
5.437 + w.write(" return prop_" + p.name() + ";\n");
5.438 + w.write("}\n");
5.439 + w.write("public void " + gs[1] + "(" + tn + " v) {\n");
5.440 + w.write(" if (locked) throw new IllegalStateException();\n");
5.441 + w.write(" prop_" + p.name() + " = v;\n");
5.442 + w.write(" if (ko != null) {\n");
5.443 + w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n");
5.444 + final Collection<String> dependants = deps.get(p.name());
5.445 + if (dependants != null) {
5.446 + for (String depProp : dependants) {
5.447 + w.write(" ko.valueHasMutated(\"" + depProp + "\");\n");
5.448 + }
5.449 + }
5.450 + w.write(" }\n");
5.451 + w.write("}\n");
5.452 }
5.453 - w.write(" }\n");
5.454 - w.write("}\n");
5.455
5.456 props.add(p.name());
5.457 props.add(gs[2]);
5.458 props.add(gs[3]);
5.459 props.add(gs[0]);
5.460 }
5.461 + return ok;
5.462 }
5.463
5.464 private boolean generateComputedProperties(
5.465 - Writer w, Collection<? extends Element> arr, Collection<String> props,
5.466 + Writer w, Property[] fixedProps,
5.467 + Collection<? extends Element> arr, Collection<String> props,
5.468 Map<String,Collection<String>> deps
5.469 ) throws IOException {
5.470 + boolean ok = true;
5.471 for (Element e : arr) {
5.472 if (e.getKind() != ElementKind.METHOD) {
5.473 continue;
5.474 @@ -370,23 +445,36 @@
5.475 continue;
5.476 }
5.477 ExecutableElement ee = (ExecutableElement)e;
5.478 - final String tn = ee.getReturnType().toString();
5.479 + final TypeMirror rt = ee.getReturnType();
5.480 + final Types tu = processingEnv.getTypeUtils();
5.481 + TypeMirror ert = tu.erasure(rt);
5.482 + String tn = ert.toString();
5.483 + boolean array = false;
5.484 + if (tn.equals("java.util.List")) {
5.485 + array = true;
5.486 + }
5.487 +
5.488 final String sn = ee.getSimpleName().toString();
5.489 - String[] gs = toGetSet(sn, tn);
5.490 + String[] gs = toGetSet(sn, tn, array);
5.491
5.492 w.write("public " + tn + " " + gs[0] + "() {\n");
5.493 w.write(" if (locked) throw new IllegalStateException();\n");
5.494 int arg = 0;
5.495 for (VariableElement pe : ee.getParameters()) {
5.496 final String dn = pe.getSimpleName().toString();
5.497 +
5.498 + if (!verifyPropName(pe, dn, fixedProps)) {
5.499 + ok = false;
5.500 + }
5.501 +
5.502 final String dt = pe.asType().toString();
5.503 - String[] call = toGetSet(dn, dt);
5.504 + String[] call = toGetSet(dn, dt, false);
5.505 w.write(" " + dt + " arg" + (++arg) + " = ");
5.506 w.write(call[0] + "();\n");
5.507
5.508 Collection<String> depends = deps.get(dn);
5.509 if (depends == null) {
5.510 - depends = new LinkedHashSet<String>();
5.511 + depends = new LinkedHashSet<>();
5.512 deps.put(dn, depends);
5.513 }
5.514 depends.add(sn);
5.515 @@ -405,17 +493,17 @@
5.516 w.write(" locked = false;\n");
5.517 w.write(" }\n");
5.518 w.write("}\n");
5.519 -
5.520 +
5.521 props.add(e.getSimpleName().toString());
5.522 props.add(gs[2]);
5.523 props.add(null);
5.524 props.add(gs[0]);
5.525 }
5.526
5.527 - return true;
5.528 + return ok;
5.529 }
5.530
5.531 - private static String[] toGetSet(String name, String type) {
5.532 + private static String[] toGetSet(String name, String type, boolean array) {
5.533 String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
5.534 String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
5.535 if ("int".equals(type)) {
5.536 @@ -430,6 +518,14 @@
5.537 bck2brwsrType = "Z";
5.538 }
5.539 final String nu = n.replace('.', '_');
5.540 + if (array) {
5.541 + return new String[] {
5.542 + "get" + n,
5.543 + null,
5.544 + "get" + nu + "__Ljava_util_List_2",
5.545 + null
5.546 + };
5.547 + }
5.548 return new String[]{
5.549 pref + n,
5.550 "set" + n,
5.551 @@ -438,11 +534,196 @@
5.552 };
5.553 }
5.554
5.555 - private static String typeName(Property p) {
5.556 + private String typeName(Element where, Property p) {
5.557 + String ret;
5.558 + boolean isModel = false;
5.559 try {
5.560 - return p.type().getName();
5.561 + ret = p.type().getName();
5.562 } catch (MirroredTypeException ex) {
5.563 - return ex.getTypeMirror().toString();
5.564 + TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
5.565 + final Element e = processingEnv.getTypeUtils().asElement(tm);
5.566 + final Model m = e == null ? null : e.getAnnotation(Model.class);
5.567 + if (m != null) {
5.568 + ret = findPkgName(e) + '.' + m.className();
5.569 + isModel = true;
5.570 + } else {
5.571 + ret = tm.toString();
5.572 + }
5.573 + }
5.574 + if (p.array()) {
5.575 + String bt = findBoxedType(ret);
5.576 + if (bt != null) {
5.577 + return bt;
5.578 + }
5.579 + }
5.580 + if (!isModel && !"java.lang.String".equals(ret)) {
5.581 + String bt = findBoxedType(ret);
5.582 + if (bt == null) {
5.583 + processingEnv.getMessager().printMessage(
5.584 + Diagnostic.Kind.ERROR,
5.585 + "Only primitive types supported in the mapping. Not " + ret,
5.586 + where
5.587 + );
5.588 + }
5.589 + }
5.590 + return ret;
5.591 + }
5.592 +
5.593 + private static String findBoxedType(String ret) {
5.594 + if (ret.equals("boolean")) {
5.595 + return Boolean.class.getName();
5.596 + }
5.597 + if (ret.equals("byte")) {
5.598 + return Byte.class.getName();
5.599 + }
5.600 + if (ret.equals("short")) {
5.601 + return Short.class.getName();
5.602 + }
5.603 + if (ret.equals("char")) {
5.604 + return Character.class.getName();
5.605 + }
5.606 + if (ret.equals("int")) {
5.607 + return Integer.class.getName();
5.608 + }
5.609 + if (ret.equals("long")) {
5.610 + return Long.class.getName();
5.611 + }
5.612 + if (ret.equals("float")) {
5.613 + return Float.class.getName();
5.614 + }
5.615 + if (ret.equals("double")) {
5.616 + return Double.class.getName();
5.617 + }
5.618 + return null;
5.619 + }
5.620 +
5.621 + private boolean verifyPropName(Element e, String propName, Property[] existingProps) {
5.622 + StringBuilder sb = new StringBuilder();
5.623 + String sep = "";
5.624 + for (Property property : existingProps) {
5.625 + if (property.name().equals(propName)) {
5.626 + return true;
5.627 + }
5.628 + sb.append(sep);
5.629 + sb.append('"');
5.630 + sb.append(property.name());
5.631 + sb.append('"');
5.632 + sep = ", ";
5.633 + }
5.634 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
5.635 + propName + " is not one of known properties: " + sb
5.636 + , e
5.637 + );
5.638 + return false;
5.639 + }
5.640 +
5.641 + private static String findPkgName(Element e) {
5.642 + for (;;) {
5.643 + if (e.getKind() == ElementKind.PACKAGE) {
5.644 + return ((PackageElement)e).getQualifiedName().toString();
5.645 + }
5.646 + e = e.getEnclosingElement();
5.647 }
5.648 }
5.649 +
5.650 + private boolean generateFunctions(
5.651 + Element clazz, StringWriter body, String className,
5.652 + List<? extends Element> enclosedElements, List<String> functions
5.653 + ) {
5.654 + for (Element m : enclosedElements) {
5.655 + if (m.getKind() != ElementKind.METHOD) {
5.656 + continue;
5.657 + }
5.658 + ExecutableElement e = (ExecutableElement)m;
5.659 + OnFunction onF = e.getAnnotation(OnFunction.class);
5.660 + if (onF == null) {
5.661 + continue;
5.662 + }
5.663 + if (!e.getModifiers().contains(Modifier.STATIC)) {
5.664 + processingEnv.getMessager().printMessage(
5.665 + Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
5.666 + );
5.667 + return false;
5.668 + }
5.669 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
5.670 + processingEnv.getMessager().printMessage(
5.671 + Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
5.672 + );
5.673 + return false;
5.674 + }
5.675 + if (e.getReturnType().getKind() != TypeKind.VOID) {
5.676 + processingEnv.getMessager().printMessage(
5.677 + Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
5.678 + );
5.679 + return false;
5.680 + }
5.681 + String n = e.getSimpleName().toString();
5.682 + body.append("void ").append(n).append("(Object data, Object ev) {\n");
5.683 + body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
5.684 + body.append(wrapParams(e, null, className, "ev", "data"));
5.685 + body.append(");\n");
5.686 + body.append("}\n");
5.687 +
5.688 + functions.add('\"' + n + '\"');
5.689 + functions.add('\"' + n + "__VLjava_lang_Object_2Ljava_lang_Object_2" + '\"');
5.690 + }
5.691 + return true;
5.692 + }
5.693 +
5.694 + private CharSequence wrapParams(
5.695 + ExecutableElement ee, String id, String className, String evName, String dataName
5.696 + ) {
5.697 + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
5.698 + StringBuilder params = new StringBuilder();
5.699 + boolean first = true;
5.700 + for (VariableElement ve : ee.getParameters()) {
5.701 + if (!first) {
5.702 + params.append(", ");
5.703 + }
5.704 + first = false;
5.705 + String toCall = null;
5.706 + if (ve.asType() == stringType) {
5.707 + if (ve.getSimpleName().contentEquals("id")) {
5.708 + params.append('"').append(id).append('"');
5.709 + continue;
5.710 + }
5.711 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString";
5.712 + }
5.713 + if (ve.asType().getKind() == TypeKind.DOUBLE) {
5.714 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble";
5.715 + }
5.716 + if (ve.asType().getKind() == TypeKind.INT) {
5.717 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt";
5.718 + }
5.719 +
5.720 + if (toCall != null) {
5.721 + params.append(toCall).append('(');
5.722 + if (dataName != null && ve.getSimpleName().contentEquals("data")) {
5.723 + params.append(dataName);
5.724 + params.append(", null");
5.725 + } else {
5.726 + params.append(evName);
5.727 + params.append(", \"");
5.728 + params.append(ve.getSimpleName().toString());
5.729 + params.append("\"");
5.730 + }
5.731 + params.append(")");
5.732 + continue;
5.733 + }
5.734 + String rn = ve.asType().toString();
5.735 + int last = rn.lastIndexOf('.');
5.736 + if (last >= 0) {
5.737 + rn = rn.substring(last + 1);
5.738 + }
5.739 + if (rn.equals(className)) {
5.740 + params.append(className).append(".this");
5.741 + continue;
5.742 + }
5.743 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
5.744 + "@On method can only accept String named 'id' or " + className + " arguments",
5.745 + ee
5.746 + );
5.747 + }
5.748 + return params;
5.749 + }
5.750 }
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Model.java Mon Mar 25 18:36:50 2013 +0100
6.3 @@ -0,0 +1,43 @@
6.4 +/**
6.5 + * Back 2 Browser Bytecode Translator
6.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.7 + *
6.8 + * This program is free software: you can redistribute it and/or modify
6.9 + * it under the terms of the GNU General Public License as published by
6.10 + * the Free Software Foundation, version 2 of the License.
6.11 + *
6.12 + * This program is distributed in the hope that it will be useful,
6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.15 + * GNU General Public License for more details.
6.16 + *
6.17 + * You should have received a copy of the GNU General Public License
6.18 + * along with this program. Look for COPYING file in the top folder.
6.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
6.20 + */
6.21 +package org.apidesign.bck2brwsr.htmlpage.api;
6.22 +
6.23 +import java.lang.annotation.ElementType;
6.24 +import java.lang.annotation.Retention;
6.25 +import java.lang.annotation.RetentionPolicy;
6.26 +import java.lang.annotation.Target;
6.27 +
6.28 +/** Defines a model class named {@link #className()} which contains
6.29 + * defined {@link #properties()}. This class can have methods
6.30 + * annotated by {@link ComputedProperty} which define derived
6.31 + * properties in the model class.
6.32 + * <p>
6.33 + * The {@link #className() generated class} will have methods
6.34 + * to convert the object <code>toJSON</code> and <code>fromJSON</code>.
6.35 + *
6.36 + * @author Jaroslav Tulach <jtulach@netbeans.org>
6.37 + */
6.38 +@Retention(RetentionPolicy.SOURCE)
6.39 +@Target(ElementType.TYPE)
6.40 +public @interface Model {
6.41 + /** Name of the model class */
6.42 + String className();
6.43 + /** List of properties in the model.
6.44 + */
6.45 + Property[] properties();
6.46 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnFunction.java Mon Mar 25 18:36:50 2013 +0100
7.3 @@ -0,0 +1,34 @@
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.api;
7.22 +
7.23 +import java.lang.annotation.ElementType;
7.24 +import java.lang.annotation.Retention;
7.25 +import java.lang.annotation.RetentionPolicy;
7.26 +import java.lang.annotation.Target;
7.27 +
7.28 +/** Methods in class annotated by {@link Model} or {@link Page} can be
7.29 + * annotated by this annotation to signal that they should be available
7.30 + * as functions to users of the model classes.
7.31 + *
7.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
7.33 + */
7.34 +@Target(ElementType.METHOD)
7.35 +@Retention(RetentionPolicy.SOURCE)
7.36 +public @interface OnFunction {
7.37 +}
8.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java Mon Mar 25 13:33:03 2013 +0100
8.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java Mon Mar 25 18:36:50 2013 +0100
8.3 @@ -20,15 +20,36 @@
8.4 import java.lang.annotation.Retention;
8.5 import java.lang.annotation.RetentionPolicy;
8.6 import java.lang.annotation.Target;
8.7 +import java.util.List;
8.8
8.9 -/** Represents a property in a generated model of an HTML
8.10 - * {@link Page}.
8.11 +/** Represents a property. Either in a generated model of an HTML
8.12 + * {@link Page} or in a class defined by {@link Model}.
8.13 *
8.14 * @author Jaroslav Tulach <jtulach@netbeans.org>
8.15 */
8.16 @Retention(RetentionPolicy.SOURCE)
8.17 @Target({})
8.18 public @interface Property {
8.19 + /** Name of the property. Will be used to define proper getter and setter
8.20 + * in the associated class.
8.21 + *
8.22 + * @return valid java identifier
8.23 + */
8.24 String name();
8.25 +
8.26 + /** Type of the property. Can either be primitive type (like <code>int.class</code>,
8.27 + * <code>double.class</code>, etc.), {@link String} or complex model
8.28 + * class (defined by {@link Model} property).
8.29 + *
8.30 + * @return the class of the property
8.31 + */
8.32 Class<?> type();
8.33 +
8.34 + /** Is this property an array of the {@link #type()} or a single value?
8.35 + * If the property is an array, only its getter (returning mutable {@link List} of
8.36 + * the boxed {@link #type()}).
8.37 + *
8.38 + * @return true, if this is supposed to be an array of values.
8.39 + */
8.40 + boolean array() default false;
8.41 }
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java Mon Mar 25 18:36:50 2013 +0100
9.3 @@ -0,0 +1,203 @@
9.4 +/**
9.5 + * Back 2 Browser Bytecode Translator
9.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
9.7 + *
9.8 + * This program is free software: you can redistribute it and/or modify
9.9 + * it under the terms of the GNU General Public License as published by
9.10 + * the Free Software Foundation, version 2 of the License.
9.11 + *
9.12 + * This program is distributed in the hope that it will be useful,
9.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
9.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9.15 + * GNU General Public License for more details.
9.16 + *
9.17 + * You should have received a copy of the GNU General Public License
9.18 + * along with this program. Look for COPYING file in the top folder.
9.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
9.20 + */
9.21 +package org.apidesign.bck2brwsr.htmlpage;
9.22 +
9.23 +import java.io.ByteArrayInputStream;
9.24 +import java.io.ByteArrayOutputStream;
9.25 +import java.io.IOException;
9.26 +import java.io.InputStream;
9.27 +import java.io.OutputStream;
9.28 +import java.net.URI;
9.29 +import java.net.URISyntaxException;
9.30 +import java.util.ArrayList;
9.31 +import java.util.Arrays;
9.32 +import java.util.HashMap;
9.33 +import java.util.List;
9.34 +import java.util.Map;
9.35 +import java.util.regex.Matcher;
9.36 +import java.util.regex.Pattern;
9.37 +import javax.tools.Diagnostic;
9.38 +import javax.tools.DiagnosticListener;
9.39 +import javax.tools.FileObject;
9.40 +import javax.tools.ForwardingJavaFileManager;
9.41 +import javax.tools.JavaFileManager;
9.42 +import javax.tools.JavaFileObject;
9.43 +import javax.tools.JavaFileObject.Kind;
9.44 +import javax.tools.SimpleJavaFileObject;
9.45 +import javax.tools.StandardJavaFileManager;
9.46 +import javax.tools.StandardLocation;
9.47 +import javax.tools.ToolProvider;
9.48 +
9.49 +/**
9.50 + *
9.51 + * @author Jaroslav Tulach <jtulach@netbeans.org>
9.52 + */
9.53 +final class Compile implements DiagnosticListener<JavaFileObject> {
9.54 + private final List<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<>();
9.55 + private final Map<String, byte[]> classes;
9.56 + private final String pkg;
9.57 + private final String cls;
9.58 + private final String html;
9.59 +
9.60 + private Compile(String html, String code) throws IOException {
9.61 + this.pkg = findPkg(code);
9.62 + this.cls = findCls(code);
9.63 + this.html = html;
9.64 + classes = compile(html, code);
9.65 + }
9.66 +
9.67 + /** Performs compilation of given HTML page and associated Java code
9.68 + */
9.69 + public static Compile create(String html, String code) throws IOException {
9.70 + return new Compile(html, code);
9.71 + }
9.72 +
9.73 + /** Checks for given class among compiled resources */
9.74 + public byte[] get(String res) {
9.75 + return classes.get(res);
9.76 + }
9.77 +
9.78 + /** Obtains errors created during compilation.
9.79 + */
9.80 + public List<Diagnostic<? extends JavaFileObject>> getErrors() {
9.81 + List<Diagnostic<? extends JavaFileObject>> err = new ArrayList<>();
9.82 + for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
9.83 + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
9.84 + err.add(diagnostic);
9.85 + }
9.86 + }
9.87 + return err;
9.88 + }
9.89 +
9.90 + private Map<String, byte[]> compile(final String html, final String code) throws IOException {
9.91 + StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
9.92 +
9.93 + final Map<String, ByteArrayOutputStream> class2BAOS = new HashMap<>();
9.94 +
9.95 + JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
9.96 + @Override
9.97 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
9.98 + return code;
9.99 + }
9.100 + };
9.101 + final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
9.102 + @Override
9.103 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
9.104 + return html;
9.105 + }
9.106 +
9.107 + @Override
9.108 + public InputStream openInputStream() throws IOException {
9.109 + return new ByteArrayInputStream(html.getBytes());
9.110 + }
9.111 + };
9.112 +
9.113 + final URI scratch;
9.114 + try {
9.115 + scratch = new URI("mem://mem3");
9.116 + } catch (URISyntaxException ex) {
9.117 + throw new IOException(ex);
9.118 + }
9.119 +
9.120 + JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
9.121 + @Override
9.122 + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
9.123 + if (kind == Kind.CLASS) {
9.124 + final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
9.125 +
9.126 + class2BAOS.put(className.replace('.', '/') + ".class", buffer);
9.127 + return new SimpleJavaFileObject(sibling.toUri(), kind) {
9.128 + @Override
9.129 + public OutputStream openOutputStream() throws IOException {
9.130 + return buffer;
9.131 + }
9.132 + };
9.133 + }
9.134 +
9.135 + if (kind == Kind.SOURCE) {
9.136 + return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) {
9.137 + private final ByteArrayOutputStream data = new ByteArrayOutputStream();
9.138 + @Override
9.139 + public OutputStream openOutputStream() throws IOException {
9.140 + return data;
9.141 + }
9.142 +
9.143 + @Override
9.144 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
9.145 + data.close();
9.146 + return new String(data.toByteArray());
9.147 + }
9.148 + };
9.149 + }
9.150 +
9.151 + throw new IllegalStateException();
9.152 + }
9.153 +
9.154 + @Override
9.155 + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
9.156 + if (location == StandardLocation.SOURCE_PATH) {
9.157 + if (packageName.equals(pkg)) {
9.158 + return htmlFile;
9.159 + }
9.160 + }
9.161 +
9.162 + return null;
9.163 + }
9.164 +
9.165 + };
9.166 +
9.167 + ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call();
9.168 +
9.169 + Map<String, byte[]> result = new HashMap<>();
9.170 +
9.171 + for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
9.172 + result.put(e.getKey(), e.getValue().toByteArray());
9.173 + }
9.174 +
9.175 + return result;
9.176 + }
9.177 +
9.178 +
9.179 + @Override
9.180 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
9.181 + errors.add(diagnostic);
9.182 + }
9.183 + private static String findPkg(String java) throws IOException {
9.184 + Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
9.185 + Matcher m = p.matcher(java);
9.186 + if (!m.find()) {
9.187 + throw new IOException("Can't find package declaration in the java file");
9.188 + }
9.189 + String pkg = m.group(1);
9.190 + return pkg;
9.191 + }
9.192 + private static String findCls(String java) throws IOException {
9.193 + Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
9.194 + Matcher m = p.matcher(java);
9.195 + if (!m.find()) {
9.196 + throw new IOException("Can't find package declaration in the java file");
9.197 + }
9.198 + String cls = m.group(1);
9.199 + return cls;
9.200 + }
9.201 +
9.202 + String getHtml() {
9.203 + String fqn = "'" + pkg + '.' + cls + "'";
9.204 + return html.replace("'${fqn}'", fqn);
9.205 + }
9.206 +}
10.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Mon Mar 25 13:33:03 2013 +0100
10.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Mon Mar 25 18:36:50 2013 +0100
10.3 @@ -17,8 +17,11 @@
10.4 */
10.5 package org.apidesign.bck2brwsr.htmlpage;
10.6
10.7 +import java.util.List;
10.8 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
10.9 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
10.10 import org.apidesign.bck2brwsr.htmlpage.api.OnEvent;
10.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
10.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
10.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
10.14 import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
10.15 @@ -31,7 +34,9 @@
10.16 * @author Jaroslav Tulach <jtulach@netbeans.org>
10.17 */
10.18 @Page(xhtml="Knockout.xhtml", className="KnockoutModel", properties={
10.19 - @Property(name="name", type=String.class)
10.20 + @Property(name="name", type=String.class),
10.21 + @Property(name="results", type=String.class, array = true),
10.22 + @Property(name="callbackCount", type=int.class)
10.23 })
10.24 public class KnockoutTest {
10.25
10.26 @@ -50,13 +55,82 @@
10.27 assert "Jardo".equals(m.getName()) : "Name property updated: " + m.getName();
10.28 }
10.29
10.30 + @HtmlFragment(
10.31 + "<ul id='ul' data-bind='foreach: results'>\n"
10.32 + + " <li data-bind='text: $data, click: $root.call'/>\n"
10.33 + + "</ul>\n"
10.34 + )
10.35 + @BrwsrTest public void displayContentOfArray() {
10.36 + KnockoutModel m = new KnockoutModel();
10.37 + m.getResults().add("Ahoj");
10.38 + m.applyBindings();
10.39 +
10.40 + int cnt = countChildren("ul");
10.41 + assert cnt == 1 : "One child, but was " + cnt;
10.42 +
10.43 + m.getResults().add("Hi");
10.44 +
10.45 + cnt = countChildren("ul");
10.46 + assert cnt == 2 : "Two children now, but was " + cnt;
10.47 +
10.48 + triggerChildClick("ul", 1);
10.49 +
10.50 + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
10.51 + assert "Hi".equals(m.getName()) : "We got callback from 2nd child " + m.getName();
10.52 + }
10.53 +
10.54 + @HtmlFragment(
10.55 + "<ul id='ul' data-bind='foreach: cmpResults'>\n"
10.56 + + " <li><b data-bind='text: $data'></b></li>\n"
10.57 + + "</ul>\n"
10.58 + )
10.59 + @BrwsrTest public void displayContentOfDerivedArray() {
10.60 + KnockoutModel m = new KnockoutModel();
10.61 + m.getResults().add("Ahoj");
10.62 + m.applyBindings();
10.63 +
10.64 + int cnt = countChildren("ul");
10.65 + assert cnt == 1 : "One child, but was " + cnt;
10.66 +
10.67 + m.getResults().add("hello");
10.68 +
10.69 + cnt = countChildren("ul");
10.70 + assert cnt == 2 : "Two children now, but was " + cnt;
10.71 + }
10.72 +
10.73 + @OnFunction
10.74 + static void call(KnockoutModel m, String data) {
10.75 + m.setName(data);
10.76 + m.setCallbackCount(m.getCallbackCount() + 1);
10.77 + }
10.78 +
10.79 @ComputedProperty
10.80 static String helloMessage(String name) {
10.81 return "Hello " + name + "!";
10.82 }
10.83
10.84 + @ComputedProperty
10.85 + static List<String> cmpResults(List<String> results) {
10.86 + return results;
10.87 + }
10.88 +
10.89 @Factory
10.90 public static Object[] create() {
10.91 return VMTest.create(KnockoutTest.class);
10.92 }
10.93 +
10.94 + @JavaScriptBody(args = { "id" }, body =
10.95 + "var e = window.document.getElementById(id);\n "
10.96 + + "if (typeof e === 'undefined') return -2;\n "
10.97 + + "return e.children.length;\n "
10.98 + )
10.99 + private static native int countChildren(String id);
10.100 +
10.101 + @JavaScriptBody(args = { "id", "pos" }, body =
10.102 + "var e = window.document.getElementById(id);\n "
10.103 + + "var ev = window.document.createEvent('MouseEvents');\n "
10.104 + + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
10.105 + + "e.children[pos].dispatchEvent(ev);\n "
10.106 + )
10.107 + private static native void triggerChildClick(String id, int pos);
10.108 }
11.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Mar 25 13:33:03 2013 +0100
11.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Mar 25 18:36:50 2013 +0100
11.3 @@ -18,8 +18,12 @@
11.4 package org.apidesign.bck2brwsr.htmlpage;
11.5
11.6 import java.util.ArrayList;
11.7 +import java.util.Collections;
11.8 +import java.util.Iterator;
11.9 import java.util.List;
11.10 +import java.util.ListIterator;
11.11 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
11.12 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
11.13 import org.apidesign.bck2brwsr.htmlpage.api.Page;
11.14 import org.apidesign.bck2brwsr.htmlpage.api.Property;
11.15 import static org.testng.Assert.*;
11.16 @@ -30,17 +34,21 @@
11.17 *
11.18 * @author Jaroslav Tulach <jtulach@netbeans.org>
11.19 */
11.20 -@Page(xhtml = "Empty.html", className = "Model", properties = {
11.21 +@Page(xhtml = "Empty.html", className = "Modelik", properties = {
11.22 @Property(name = "value", type = int.class),
11.23 - @Property(name = "unrelated", type = long.class)
11.24 + @Property(name = "count", type = int.class),
11.25 + @Property(name = "unrelated", type = long.class),
11.26 + @Property(name = "names", type = String.class, array = true),
11.27 + @Property(name = "values", type = int.class, array = true),
11.28 + @Property(name = "people", type = PersonImpl.class, array = true)
11.29 })
11.30 public class ModelTest {
11.31 - private Model model;
11.32 - private static Model leakedModel;
11.33 + private Modelik model;
11.34 + private static Modelik leakedModel;
11.35
11.36 @BeforeMethod
11.37 public void createModel() {
11.38 - model = new Model();
11.39 + model = new Modelik();
11.40 }
11.41
11.42 @Test public void classGeneratedWithSetterGetter() {
11.43 @@ -53,6 +61,75 @@
11.44 assertEquals(16, model.getPowerValue());
11.45 }
11.46
11.47 + @Test public void arrayIsMutable() {
11.48 + assertEquals(model.getNames().size(), 0, "Is empty");
11.49 + model.getNames().add("Jarda");
11.50 + assertEquals(model.getNames().size(), 1, "One element");
11.51 + }
11.52 +
11.53 + @Test public void arrayChangesNotified() {
11.54 + MockKnockout my = new MockKnockout();
11.55 + MockKnockout.next = my;
11.56 +
11.57 + model.applyBindings();
11.58 +
11.59 + model.getNames().add("Hello");
11.60 +
11.61 + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
11.62 + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
11.63 +
11.64 + my.mutated.clear();
11.65 +
11.66 + Iterator<String> it = model.getNames().iterator();
11.67 + assertEquals(it.next(), "Hello");
11.68 + it.remove();
11.69 +
11.70 + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
11.71 + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
11.72 +
11.73 + my.mutated.clear();
11.74 +
11.75 + ListIterator<String> lit = model.getNames().listIterator();
11.76 + lit.add("Jarda");
11.77 +
11.78 + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
11.79 + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
11.80 + }
11.81 +
11.82 + @Test public void autoboxedArray() {
11.83 + MockKnockout my = new MockKnockout();
11.84 + MockKnockout.next = my;
11.85 +
11.86 + model.applyBindings();
11.87 +
11.88 + model.getValues().add(10);
11.89 +
11.90 + assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
11.91 + }
11.92 +
11.93 + @Test public void derivedArrayProp() {
11.94 + MockKnockout my = new MockKnockout();
11.95 + MockKnockout.next = my;
11.96 +
11.97 + model.applyBindings();
11.98 +
11.99 + model.setCount(10);
11.100 +
11.101 + List<String> arr = model.getRepeat();
11.102 + assertEquals(arr.size(), 10, "Ten items: " + arr);
11.103 +
11.104 + my.mutated.clear();
11.105 +
11.106 + model.setCount(5);
11.107 +
11.108 + arr = model.getRepeat();
11.109 + assertEquals(arr.size(), 5, "Five items: " + arr);
11.110 +
11.111 + assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
11.112 + assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
11.113 + assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
11.114 + }
11.115 +
11.116 @Test public void derivedPropertiesAreNotified() {
11.117 MockKnockout my = new MockKnockout();
11.118 MockKnockout.next = my;
11.119 @@ -92,6 +169,10 @@
11.120 }
11.121 }
11.122
11.123 + @OnFunction
11.124 + static void doSomething() {
11.125 + }
11.126 +
11.127 @ComputedProperty
11.128 static int powerValue(int value) {
11.129 return value * value;
11.130 @@ -108,12 +189,27 @@
11.131 return "Not allowed callback!";
11.132 }
11.133
11.134 + @ComputedProperty
11.135 + static List<String> repeat(int count) {
11.136 + return Collections.nCopies(count, "Hello");
11.137 + }
11.138 +
11.139 static class MockKnockout extends Knockout {
11.140 - List<String> mutated = new ArrayList<String>();
11.141 + List<String> mutated = new ArrayList<>();
11.142
11.143 @Override
11.144 public void valueHasMutated(String prop) {
11.145 mutated.add(prop);
11.146 }
11.147 }
11.148 +
11.149 + public @Test void hasPersonPropertyAndComputedFullName() {
11.150 + List<Person> arr = model.getPeople();
11.151 + assertEquals(arr.size(), 0, "By default empty");
11.152 + Person p = null;
11.153 + if (p != null) {
11.154 + String fullNameGenerated = p.getFullName();
11.155 + assertNotNull(fullNameGenerated);
11.156 + }
11.157 + }
11.158 }
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Mon Mar 25 18:36:50 2013 +0100
12.3 @@ -0,0 +1,50 @@
12.4 +/**
12.5 + * Back 2 Browser Bytecode Translator
12.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
12.7 + *
12.8 + * This program is free software: you can redistribute it and/or modify
12.9 + * it under the terms of the GNU General Public License as published by
12.10 + * the Free Software Foundation, version 2 of the License.
12.11 + *
12.12 + * This program is distributed in the hope that it will be useful,
12.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
12.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12.15 + * GNU General Public License for more details.
12.16 + *
12.17 + * You should have received a copy of the GNU General Public License
12.18 + * along with this program. Look for COPYING file in the top folder.
12.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
12.20 + */
12.21 +package org.apidesign.bck2brwsr.htmlpage;
12.22 +
12.23 +import java.io.IOException;
12.24 +import java.util.Locale;
12.25 +import static org.testng.Assert.*;
12.26 +import org.testng.annotations.Test;
12.27 +
12.28 +/** Verify errors emitted by the processor.
12.29 + *
12.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
12.31 + */
12.32 +public class PageTest {
12.33 + @Test public void verifyWrongType() throws IOException {
12.34 + String html = "<html><body>"
12.35 + + "</body></html>";
12.36 + String code = "package x.y.z;\n"
12.37 + + "import org.apidesign.bck2brwsr.htmlpage.api.*;\n"
12.38 + + "@Page(xhtml=\"index.xhtml\", className=\"Model\", properties={\n"
12.39 + + " @Property(name=\"prop\", type=Runnable.class)\n"
12.40 + + "})\n"
12.41 + + "class X {\n"
12.42 + + "}\n";
12.43 +
12.44 + Compile c = Compile.create(html, code);
12.45 + assertEquals(c.getErrors().size(), 1, "One error: " + c.getErrors());
12.46 +
12.47 + String msg = c.getErrors().get(0).getMessage(Locale.ENGLISH);
12.48 + if (!msg.contains("Runnable")) {
12.49 + fail("Should contain warning about Runnable: " + msg);
12.50 + }
12.51 + }
12.52 +
12.53 +}
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Mon Mar 25 18:36:50 2013 +0100
13.3 @@ -0,0 +1,43 @@
13.4 +/**
13.5 + * Back 2 Browser Bytecode Translator
13.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
13.7 + *
13.8 + * This program is free software: you can redistribute it and/or modify
13.9 + * it under the terms of the GNU General Public License as published by
13.10 + * the Free Software Foundation, version 2 of the License.
13.11 + *
13.12 + * This program is distributed in the hope that it will be useful,
13.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
13.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13.15 + * GNU General Public License for more details.
13.16 + *
13.17 + * You should have received a copy of the GNU General Public License
13.18 + * along with this program. Look for COPYING file in the top folder.
13.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
13.20 + */
13.21 +package org.apidesign.bck2brwsr.htmlpage;
13.22 +
13.23 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
13.24 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
13.25 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
13.26 +
13.27 +/**
13.28 + *
13.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
13.30 + */
13.31 +@Model(className = "Person", properties = {
13.32 + @Property(name = "firstName", type = String.class),
13.33 + @Property(name = "lastName", type = String.class),
13.34 + @Property(name = "male", type = boolean.class)
13.35 +})
13.36 +final class PersonImpl {
13.37 + @ComputedProperty
13.38 + public static String fullName(String firstName, String lastName) {
13.39 + return firstName + " " + lastName;
13.40 + }
13.41 +
13.42 + @ComputedProperty
13.43 + public static String sex(boolean male) {
13.44 + return male ? "Male" : "Female";
13.45 + }
13.46 +}
14.1 --- a/javaquery/demo-calculator-dynamic/pom.xml Mon Mar 25 13:33:03 2013 +0100
14.2 +++ b/javaquery/demo-calculator-dynamic/pom.xml Mon Mar 25 18:36:50 2013 +0100
14.3 @@ -27,8 +27,7 @@
14.4 </execution>
14.5 </executions>
14.6 <configuration>
14.7 - <directory>${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html</directory>
14.8 - <startpage>index.xhtml</startpage>
14.9 + <startpage>org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml</startpage>
14.10 </configuration>
14.11 </plugin>
14.12 <plugin>
15.1 --- a/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Mon Mar 25 13:33:03 2013 +0100
15.2 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Mon Mar 25 18:36:50 2013 +0100
15.3 @@ -17,9 +17,11 @@
15.4 */
15.5 package org.apidesign.bck2brwsr.demo.calc;
15.6
15.7 +import java.util.List;
15.8 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
15.9 import org.apidesign.bck2brwsr.htmlpage.api.On;
15.10 import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
15.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
15.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
15.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
15.14
15.15 @@ -33,11 +35,12 @@
15.16 @Property(name = "memory", type = double.class),
15.17 @Property(name = "display", type = double.class),
15.18 @Property(name = "operation", type = String.class),
15.19 - @Property(name = "hover", type = boolean.class)
15.20 + @Property(name = "hover", type = boolean.class),
15.21 + @Property(name = "history", type = double.class, array = true)
15.22 })
15.23 public class Calc {
15.24 static {
15.25 - new Calculator().applyBindings();
15.26 + new Calculator().applyBindings().setOperation("plus");
15.27 }
15.28
15.29 @On(event = CLICK, id="clear")
15.30 @@ -65,14 +68,28 @@
15.31
15.32 @On(event = CLICK, id="result")
15.33 static void computeTheValue(Calculator c) {
15.34 - c.setDisplay(compute(
15.35 + final double newValue = compute(
15.36 c.getOperation(),
15.37 c.getMemory(),
15.38 c.getDisplay()
15.39 - ));
15.40 + );
15.41 + c.setDisplay(newValue);
15.42 + if (!c.getHistory().contains(newValue)) {
15.43 + c.getHistory().add(newValue);
15.44 + }
15.45 c.setMemory(0);
15.46 }
15.47
15.48 + @OnFunction
15.49 + static void recoverMemory(Calculator c, double data) {
15.50 + c.setDisplay(data);
15.51 + }
15.52 +
15.53 + @OnFunction
15.54 + static void removeMemory(Calculator c, double data) {
15.55 + c.getHistory().remove(data);
15.56 + }
15.57 +
15.58 private static double compute(String op, double memory, double display) {
15.59 switch (op) {
15.60 case "plus": return memory + display;
15.61 @@ -109,4 +126,9 @@
15.62 }
15.63 return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
15.64 }
15.65 +
15.66 + @ComputedProperty
15.67 + static boolean emptyHistory(List<?> history) {
15.68 + return history.isEmpty();
15.69 + }
15.70 }
16.1 --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Mon Mar 25 13:33:03 2013 +0100
16.2 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Mon Mar 25 18:36:50 2013 +0100
16.3 @@ -78,6 +78,17 @@
16.4 </table>
16.5 <div data-bind="text: displayPreview"></div>
16.6
16.7 + <h4>Previous Results</h4>
16.8 +
16.9 + <div data-bind="if: emptyHistory">No results yet.</div>
16.10 + <ul data-bind="foreach: history">
16.11 + <li>
16.12 + <span data-bind="text: $data"/> -
16.13 + <a href="#" data-bind="click: $root.recoverMemory">Use</a>
16.14 + <a href="#" data-bind="click: $root.removeMemory">Remove</a>
16.15 + </li>
16.16 + </ul>
16.17 +
16.18 <script src="bck2brwsr.js"></script>
16.19 <script type="text/javascript">
16.20 var vm = bck2brwsr('demo.calculator-0.5-SNAPSHOT.jar');
16.21 @@ -85,75 +96,5 @@
16.22 </script>
16.23
16.24 <hr/>
16.25 - <pre>
16.26 - <span class="keyword-directive">package</span> org.apidesign.bck2brwsr.mavenhtml;
16.27 -
16.28 - <span class="keyword-directive">import</span> org.apidesign.bck2brwsr.htmlpage.api.OnClick;
16.29 - <span class="keyword-directive">import</span> org.apidesign.bck2brwsr.htmlpage.api.Page;
16.30 -
16.31 - <span class="comment">/**</span> <span class="comment">HTML5</span><span class="comment"> & </span><span class="comment">Java</span> <span class="comment">demo</span> <span class="comment">showing</span> <span class="comment">the</span> <span class="comment">power</span> <span class="comment">of</span> <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">annotation processors</a>
16.32 - <span class="comment"> * </span><span class="comment">as</span> <span class="comment">well</span> <span class="comment">as</span> <span class="comment">other</span> <span class="comment">goodies</span><span class="comment">, including type-safe association between</span>
16.33 - <span class="comment"> * </span><span class="comment">an XHTML page and Java.</span>
16.34 - <span class="comment"> * </span>
16.35 - <span class="comment"> * </span><span class="ST1">@author</span> <span class="comment">Jaroslav</span> <span class="comment">Tulach</span> <span class="ST0"><jaroslav.tulach@apidesign.org></span>
16.36 - <span class="comment">*/</span>
16.37 - @Page(xhtml=<span class="string">"</span><span class="string">Calculator.xhtml</span><span class="string">"</span>)
16.38 - <span class="keyword-directive">public</span> <span class="keyword-directive">class</span> App {
16.39 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> <span class="keyword-directive">double</span> memory;
16.40 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> String operation;
16.41 -
16.42 - @OnClick(id=<span class="string">"</span><span class="string">clear</span><span class="string">"</span>)
16.43 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> clear() {
16.44 - memory = <span class="number">0</span>;
16.45 - operation = <span class="keyword-directive">null</span>;
16.46 - Calculator.DISPLAY.setValue(<span class="string">"</span><span class="string">0</span><span class="string">"</span>);
16.47 - }
16.48 -
16.49 - @OnClick(id= { <span class="string">"</span><span class="string">plus</span><span class="string">"</span>, <span class="string">"</span><span class="string">minus</span><span class="string">"</span>, <span class="string">"</span><span class="string">mul</span><span class="string">"</span>, <span class="string">"</span><span class="string">div</span><span class="string">"</span> })
16.50 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> applyOp(String op) {
16.51 - memory = getValue();
16.52 - operation = op;
16.53 - Calculator.DISPLAY.setValue(<span class="string">"</span><span class="string">0</span><span class="string">"</span>);
16.54 - }
16.55 -
16.56 - @OnClick(id=<span class="string">"</span><span class="string">result</span><span class="string">"</span>)
16.57 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> computeTheValue() {
16.58 - <span class="keyword-directive">switch</span> (operation) {
16.59 - <span class="keyword-directive">case</span> <span class="string">"</span><span class="string">plus</span><span class="string">"</span>: setValue(memory + getValue()); <span class="keyword-directive">break</span>;
16.60 - <span class="keyword-directive">case</span> <span class="string">"</span><span class="string">minus</span><span class="string">"</span>: setValue(memory - getValue()); <span class="keyword-directive">break</span>;
16.61 - <span class="keyword-directive">case</span> <span class="string">"</span><span class="string">mul</span><span class="string">"</span>: setValue(memory * getValue()); <span class="keyword-directive">break</span>;
16.62 - <span class="keyword-directive">case</span> <span class="string">"</span><span class="string">div</span><span class="string">"</span>: setValue(memory / getValue()); <span class="keyword-directive">break</span>;
16.63 - <span class="keyword-directive">default</span>: <span class="keyword-directive">throw</span> <span class="keyword-directive">new</span> IllegalStateException(operation);
16.64 - }
16.65 - }
16.66 -
16.67 - @OnClick(id={<span class="string">"</span><span class="string">n0</span><span class="string">"</span>, <span class="string">"</span><span class="string">n1</span><span class="string">"</span>, <span class="string">"</span><span class="string">n2</span><span class="string">"</span>, <span class="string">"</span><span class="string">n3</span><span class="string">"</span>, <span class="string">"</span><span class="string">n4</span><span class="string">"</span>, <span class="string">"</span><span class="string">n5</span><span class="string">"</span>, <span class="string">"</span><span class="string">n6</span><span class="string">"</span>, <span class="string">"</span><span class="string">n7</span><span class="string">"</span>, <span class="string">"</span><span class="string">n8</span><span class="string">"</span>, <span class="string">"</span><span class="string">n9</span><span class="string">"</span>})
16.68 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> addDigit(String digit) {
16.69 - digit = digit.substring(<span class="number">1</span>);
16.70 - String v = Calculator.DISPLAY.getValue();
16.71 - <span class="keyword-directive">if</span> (getValue() == <span class="number">0.0</span>) {
16.72 - Calculator.DISPLAY.setValue(digit);
16.73 - } <span class="keyword-directive">else</span> {
16.74 - Calculator.DISPLAY.setValue(v + digit);
16.75 - }
16.76 - }
16.77 -
16.78 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> setValue(<span class="keyword-directive">double</span> v) {
16.79 - StringBuilder sb = <span class="keyword-directive">new</span> StringBuilder();
16.80 - sb.append(v);
16.81 - Calculator.DISPLAY.setValue(sb.toString());
16.82 - }
16.83 -
16.84 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> <span class="keyword-directive">double</span> getValue() {
16.85 - <span class="keyword-directive">try</span> {
16.86 - <span class="keyword-directive">return</span> Double.parseDouble(Calculator.DISPLAY.getValue());
16.87 - } <span class="keyword-directive">catch</span> (NumberFormatException ex) {
16.88 - Calculator.DISPLAY.setValue(<span class="string">"</span><span class="string">err</span><span class="string">"</span>);
16.89 - <span class="keyword-directive">return</span> <span class="number">0.0</span>;
16.90 - }
16.91 - }
16.92 - }
16.93 -
16.94 - </pre>
16.95 </body>
16.96 </html>
17.1 --- a/javaquery/demo-calculator/pom.xml Mon Mar 25 13:33:03 2013 +0100
17.2 +++ b/javaquery/demo-calculator/pom.xml Mon Mar 25 18:36:50 2013 +0100
17.3 @@ -12,6 +12,7 @@
17.4
17.5 <properties>
17.6 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17.7 + <bck2brwsr.obfuscationlevel>FULL</bck2brwsr.obfuscationlevel>
17.8 </properties>
17.9 <build>
17.10 <plugins>
17.11 @@ -31,7 +32,7 @@
17.12 <directory>${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/</directory>
17.13 <startpage>index.xhtml</startpage>
17.14 <javascript>${project.build.directory}/bck2brwsr.js</javascript>
17.15 - <obfuscation>FULL</obfuscation>
17.16 + <obfuscation>${bck2brwsr.obfuscationlevel}</obfuscation>
17.17 </configuration>
17.18 </plugin>
17.19 <plugin>
18.1 --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Mon Mar 25 13:33:03 2013 +0100
18.2 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Mon Mar 25 18:36:50 2013 +0100
18.3 @@ -17,9 +17,11 @@
18.4 */
18.5 package org.apidesign.bck2brwsr.demo.calc.staticcompilation;
18.6
18.7 +import java.util.List;
18.8 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
18.9 import org.apidesign.bck2brwsr.htmlpage.api.On;
18.10 import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
18.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
18.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
18.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
18.14
18.15 @@ -33,11 +35,12 @@
18.16 @Property(name = "memory", type = double.class),
18.17 @Property(name = "display", type = double.class),
18.18 @Property(name = "operation", type = String.class),
18.19 - @Property(name = "hover", type = boolean.class)
18.20 + @Property(name = "hover", type = boolean.class),
18.21 + @Property(name = "history", type = double.class, array = true)
18.22 })
18.23 public class Calc {
18.24 static {
18.25 - new Calculator().applyBindings();
18.26 + new Calculator().applyBindings().setOperation("plus");
18.27 }
18.28
18.29 @On(event = CLICK, id="clear")
18.30 @@ -65,14 +68,28 @@
18.31
18.32 @On(event = CLICK, id="result")
18.33 static void computeTheValue(Calculator c) {
18.34 - c.setDisplay(compute(
18.35 + final double newValue = compute(
18.36 c.getOperation(),
18.37 c.getMemory(),
18.38 c.getDisplay()
18.39 - ));
18.40 + );
18.41 + c.setDisplay(newValue);
18.42 + if (!c.getHistory().contains(newValue)) {
18.43 + c.getHistory().add(newValue);
18.44 + }
18.45 c.setMemory(0);
18.46 }
18.47
18.48 + @OnFunction
18.49 + static void recoverMemory(Calculator c, double data) {
18.50 + c.setDisplay(data);
18.51 + }
18.52 +
18.53 + @OnFunction
18.54 + static void removeMemory(Calculator c, double data) {
18.55 + c.getHistory().remove(data);
18.56 + }
18.57 +
18.58 private static double compute(String op, double memory, double display) {
18.59 switch (op) {
18.60 case "plus": return memory + display;
18.61 @@ -109,4 +126,9 @@
18.62 }
18.63 return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
18.64 }
18.65 +
18.66 + @ComputedProperty
18.67 + static boolean emptyHistory(List<?> history) {
18.68 + return history.isEmpty();
18.69 + }
18.70 }
19.1 --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Mon Mar 25 13:33:03 2013 +0100
19.2 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Mon Mar 25 18:36:50 2013 +0100
19.3 @@ -76,6 +76,18 @@
19.4 </tr>
19.5 </tbody>
19.6 </table>
19.7 +
19.8 + <h4>Previous Results</h4>
19.9 +
19.10 + <div data-bind="if: emptyHistory">No results yet.</div>
19.11 + <ul data-bind="foreach: history">
19.12 + <li>
19.13 + <span data-bind="text: $data"/> -
19.14 + <a href="#" data-bind="click: $root.recoverMemory">Use</a>
19.15 + <a href="#" data-bind="click: $root.removeMemory">Remove</a>
19.16 + </li>
19.17 + </ul>
19.18 +
19.19 <div data-bind="text: displayPreview"></div>
19.20 <script src="bck2brwsr.js"/>
19.21 <script>
20.1 --- a/rt/emul/mini/src/main/java/java/lang/Class.java Mon Mar 25 13:33:03 2013 +0100
20.2 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Mon Mar 25 18:36:50 2013 +0100
20.3 @@ -402,8 +402,15 @@
20.4 }
20.5 return cmpType != null && getComponentType().isAssignableFrom(cmpType);
20.6 }
20.7 - String prop = "$instOf_" + getName().replace('.', '_');
20.8 - return hasCnstrProperty(cls, prop);
20.9 + if (isPrimitive()) {
20.10 + return false;
20.11 + } else {
20.12 + if (cls.isPrimitive()) {
20.13 + return false;
20.14 + }
20.15 + String prop = "$instOf_" + getName().replace('.', '_');
20.16 + return hasCnstrProperty(cls, prop);
20.17 + }
20.18 }
20.19
20.20 @JavaScriptBody(args = { "who", "prop" }, body =
21.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Mon Mar 25 13:33:03 2013 +0100
21.2 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Mon Mar 25 18:36:50 2013 +0100
21.3 @@ -317,9 +317,13 @@
21.4 int cnt = is.read() - '0';
21.5 if (cnt == 'U' - '0') {
21.6 os.write(baseURL.getBytes("UTF-8"));
21.7 - }
21.8 - if (cnt >= 0 && cnt < params.length) {
21.9 - os.write(params[cnt].getBytes("UTF-8"));
21.10 + } else {
21.11 + if (cnt >= 0 && cnt < params.length) {
21.12 + os.write(params[cnt].getBytes("UTF-8"));
21.13 + } else {
21.14 + os.write('$');
21.15 + os.write(cnt + '0');
21.16 + }
21.17 }
21.18 } else {
21.19 os.write(ch);
22.1 --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Mon Mar 25 13:33:03 2013 +0100
22.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Mon Mar 25 18:36:50 2013 +0100
22.3 @@ -53,6 +53,22 @@
22.4 return Runnable.class.isInterface();
22.5 }
22.6
22.7 + @Compare public boolean isAssignableToPrimitiveType() {
22.8 + return boolean.class.isAssignableFrom(Runnable.class);
22.9 + }
22.10 +
22.11 + @Compare public boolean isAssignableFromPrimitiveType() {
22.12 + return Runnable.class.isAssignableFrom(boolean.class);
22.13 + }
22.14 +
22.15 + @Compare public boolean isAssignableLongFromInt() {
22.16 + return long.class.isAssignableFrom(int.class);
22.17 + }
22.18 +
22.19 + @Compare public boolean isAssignableIntFromLong() {
22.20 + return int.class.isAssignableFrom(long.class);
22.21 + }
22.22 +
22.23 @Compare public String isRunnableHasRunMethod() throws NoSuchMethodException {
22.24 return Runnable.class.getMethod("run").getName();
22.25 }