Merging with latest state - e.g. 0.7 version
1.1 --- a/.hgtags Tue Apr 02 15:40:51 2013 +0200
1.2 +++ b/.hgtags Thu May 02 09:18:22 2013 +0200
1.3 @@ -1,3 +1,10 @@
1.4 0a115f1c6f3c70458fc479ae82b4d7fcdeb7e95a jdk7-b147_base
1.5 7367a296a9ec4a88e0292a41244c96283818e563 bck2brwsr-0.3
1.6 caf1e66268fd4100d57922d973ae09a6bf3be847 release-${releaseVersion}
1.7 +30e9ac29654fba6f67d0921e7e3aa21133442592 release-0.5
1.8 +caf1e66268fd4100d57922d973ae09a6bf3be847 release-0.4
1.9 +caf1e66268fd4100d57922d973ae09a6bf3be847 release-${releaseVersion}
1.10 +0000000000000000000000000000000000000000 release-${releaseVersion}
1.11 +52a4a5f868bccc67d50ad17f793b9ebabdf75d88 release-0.6
1.12 +6792dc0bafb9c76a099e45bfc9e967d6a2823827 release-0.7
1.13 +623816269b75e53fffb4b19960df7040a3c20056 release-0.7
2.1 --- a/benchmarks/matrix-multiplication/pom.xml Tue Apr 02 15:40:51 2013 +0200
2.2 +++ b/benchmarks/matrix-multiplication/pom.xml Thu May 02 09:18:22 2013 +0200
2.3 @@ -4,12 +4,12 @@
2.4
2.5 <groupId>org.apidesign.bck2brwsr</groupId>
2.6 <artifactId>matrix.multiplication</artifactId>
2.7 - <version>0.5-SNAPSHOT</version>
2.8 + <version>0.8-SNAPSHOT</version>
2.9 <packaging>jar</packaging>
2.10 <parent>
2.11 <artifactId>benchmarks</artifactId>
2.12 <groupId>org.apidesign.bck2brwsr</groupId>
2.13 - <version>0.5-SNAPSHOT</version>
2.14 + <version>0.8-SNAPSHOT</version>
2.15 </parent>
2.16
2.17 <name>Matrix multiplication</name>
2.18 @@ -74,7 +74,7 @@
2.19 <dependency>
2.20 <groupId>org.apidesign.bck2brwsr</groupId>
2.21 <artifactId>emul.mini</artifactId>
2.22 - <version>0.5-SNAPSHOT</version>
2.23 + <version>0.8-SNAPSHOT</version>
2.24 </dependency>
2.25 <dependency>
2.26 <groupId>org.testng</groupId>
2.27 @@ -91,7 +91,13 @@
2.28 <dependency>
2.29 <groupId>org.apidesign.bck2brwsr</groupId>
2.30 <artifactId>vmtest</artifactId>
2.31 - <version>0.5-SNAPSHOT</version>
2.32 + <version>0.8-SNAPSHOT</version>
2.33 + <scope>test</scope>
2.34 + </dependency>
2.35 + <dependency>
2.36 + <groupId>org.apidesign.bck2brwsr</groupId>
2.37 + <artifactId>launcher.http</artifactId>
2.38 + <version>0.8-SNAPSHOT</version>
2.39 <scope>test</scope>
2.40 </dependency>
2.41 </dependencies>
3.1 --- a/benchmarks/pom.xml Tue Apr 02 15:40:51 2013 +0200
3.2 +++ b/benchmarks/pom.xml Thu May 02 09:18:22 2013 +0200
3.3 @@ -4,11 +4,11 @@
3.4 <parent>
3.5 <artifactId>bck2brwsr</artifactId>
3.6 <groupId>org.apidesign</groupId>
3.7 - <version>0.5-SNAPSHOT</version>
3.8 + <version>0.8-SNAPSHOT</version>
3.9 </parent>
3.10 <groupId>org.apidesign.bck2brwsr</groupId>
3.11 <artifactId>benchmarks</artifactId>
3.12 - <version>0.5-SNAPSHOT</version>
3.13 + <version>0.8-SNAPSHOT</version>
3.14 <packaging>pom</packaging>
3.15 <name>Performance benchmarks</name>
3.16 <modules>
4.1 --- a/dew/pom.xml Tue Apr 02 15:40:51 2013 +0200
4.2 +++ b/dew/pom.xml Thu May 02 09:18:22 2013 +0200
4.3 @@ -4,11 +4,11 @@
4.4 <parent>
4.5 <groupId>org.apidesign</groupId>
4.6 <artifactId>bck2brwsr</artifactId>
4.7 - <version>0.5-SNAPSHOT</version>
4.8 + <version>0.8-SNAPSHOT</version>
4.9 </parent>
4.10 <groupId>org.apidesign.bck2brwsr</groupId>
4.11 <artifactId>dew</artifactId>
4.12 - <version>0.5-SNAPSHOT</version>
4.13 + <version>0.8-SNAPSHOT</version>
4.14 <name>Development Environment for Web</name>
4.15 <url>http://maven.apache.org</url>
4.16 <build>
5.1 --- a/ide/editor/pom.xml Tue Apr 02 15:40:51 2013 +0200
5.2 +++ b/ide/editor/pom.xml Thu May 02 09:18:22 2013 +0200
5.3 @@ -4,19 +4,18 @@
5.4 <parent>
5.5 <artifactId>ide</artifactId>
5.6 <groupId>org.apidesign.bck2brwsr</groupId>
5.7 - <version>0.5-SNAPSHOT</version>
5.8 + <version>0.8-SNAPSHOT</version>
5.9 </parent>
5.10
5.11 - <groupId>org.apidesign.bck2brwsr.ide.editor</groupId>
5.12 + <groupId>org.apidesign.bck2brwsr.ide</groupId>
5.13 <artifactId>editor</artifactId>
5.14 - <version>0.5-SNAPSHOT</version>
5.15 + <version>0.8-SNAPSHOT</version>
5.16 <packaging>nbm</packaging>
5.17
5.18 <name>Editor Support for Bck2Brwsr</name>
5.19
5.20 <properties>
5.21 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
5.22 - <netbeans.version>RELEASE73</netbeans.version>
5.23 <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
5.24 </properties>
5.25
5.26 @@ -40,71 +39,59 @@
5.27 <dependency>
5.28 <groupId>org.netbeans.api</groupId>
5.29 <artifactId>org-netbeans-api-annotations-common</artifactId>
5.30 - <version>${netbeans.version}</version>
5.31 </dependency>
5.32 <dependency>
5.33 <groupId>org.netbeans.api</groupId>
5.34 <artifactId>org-netbeans-modules-java-source</artifactId>
5.35 - <version>${netbeans.version}</version>
5.36 </dependency>
5.37 <dependency>
5.38 <groupId>org.netbeans.api</groupId>
5.39 <artifactId>org-netbeans-libs-javacapi</artifactId>
5.40 - <version>${netbeans.version}</version>
5.41 </dependency>
5.42 <dependency>
5.43 <groupId>org.netbeans.api</groupId>
5.44 <artifactId>org-netbeans-spi-java-hints</artifactId>
5.45 - <version>${netbeans.version}</version>
5.46 </dependency>
5.47 <dependency>
5.48 <groupId>org.netbeans.api</groupId>
5.49 <artifactId>org-netbeans-modules-parsing-api</artifactId>
5.50 - <version>${netbeans.version}</version>
5.51 </dependency>
5.52 <dependency>
5.53 <groupId>org.netbeans.api</groupId>
5.54 <artifactId>org-netbeans-spi-editor-hints</artifactId>
5.55 - <version>${netbeans.version}</version>
5.56 </dependency>
5.57 <dependency>
5.58 <groupId>org.netbeans.api</groupId>
5.59 <artifactId>org-openide-util</artifactId>
5.60 - <version>${netbeans.version}</version>
5.61 </dependency>
5.62 <dependency>
5.63 <groupId>org.netbeans.api</groupId>
5.64 <artifactId>org-netbeans-modules-java-lexer</artifactId>
5.65 - <version>${netbeans.version}</version>
5.66 </dependency>
5.67 <dependency>
5.68 <groupId>org.netbeans.api</groupId>
5.69 <artifactId>org-netbeans-modules-lexer</artifactId>
5.70 - <version>${netbeans.version}</version>
5.71 </dependency>
5.72 <dependency>
5.73 <groupId>org.apidesign.bck2brwsr</groupId>
5.74 <artifactId>core</artifactId>
5.75 - <version>0.5-SNAPSHOT</version>
5.76 + <version>0.8-SNAPSHOT</version>
5.77 <type>jar</type>
5.78 <scope>test</scope>
5.79 </dependency>
5.80 <dependency>
5.81 <groupId>org.netbeans.api</groupId>
5.82 <artifactId>org-netbeans-modules-java-hints-test</artifactId>
5.83 - <version>${netbeans.version}</version>
5.84 <scope>test</scope>
5.85 </dependency>
5.86 <dependency>
5.87 <groupId>org.netbeans.api</groupId>
5.88 <artifactId>org-netbeans-libs-junit4</artifactId>
5.89 - <version>${netbeans.version}</version>
5.90 <scope>test</scope>
5.91 </dependency>
5.92 <dependency>
5.93 <groupId>org.netbeans.modules</groupId>
5.94 <artifactId>org-netbeans-lib-nbjavac</artifactId>
5.95 - <version>${netbeans.version}</version>
5.96 <scope>test</scope>
5.97 </dependency>
5.98 <dependency>
6.1 --- a/ide/pom.xml Tue Apr 02 15:40:51 2013 +0200
6.2 +++ b/ide/pom.xml Thu May 02 09:18:22 2013 +0200
6.3 @@ -4,14 +4,26 @@
6.4 <parent>
6.5 <artifactId>bck2brwsr</artifactId>
6.6 <groupId>org.apidesign</groupId>
6.7 - <version>0.5-SNAPSHOT</version>
6.8 + <version>0.8-SNAPSHOT</version>
6.9 </parent>
6.10 <groupId>org.apidesign.bck2brwsr</groupId>
6.11 <artifactId>ide</artifactId>
6.12 - <version>0.5-SNAPSHOT</version>
6.13 + <version>0.8-SNAPSHOT</version>
6.14 <packaging>pom</packaging>
6.15 <name>IDE Support</name>
6.16 <modules>
6.17 <module>editor</module>
6.18 </modules>
6.19 + <build>
6.20 + <plugins>
6.21 + <plugin>
6.22 + <groupId>org.apache.maven.plugins</groupId>
6.23 + <artifactId>maven-deploy-plugin</artifactId>
6.24 + <version>2.7</version>
6.25 + <configuration>
6.26 + <skip>true</skip>
6.27 + </configuration>
6.28 + </plugin>
6.29 + </plugins>
6.30 + </build>
6.31 </project>
7.1 --- a/javaquery/api/pom.xml Tue Apr 02 15:40:51 2013 +0200
7.2 +++ b/javaquery/api/pom.xml Thu May 02 09:18:22 2013 +0200
7.3 @@ -4,11 +4,11 @@
7.4 <parent>
7.5 <groupId>org.apidesign.bck2brwsr</groupId>
7.6 <artifactId>javaquery</artifactId>
7.7 - <version>0.5-SNAPSHOT</version>
7.8 + <version>0.8-SNAPSHOT</version>
7.9 </parent>
7.10 <groupId>org.apidesign.bck2brwsr</groupId>
7.11 <artifactId>javaquery.api</artifactId>
7.12 - <version>0.5-SNAPSHOT</version>
7.13 + <version>0.8-SNAPSHOT</version>
7.14 <name>JavaQuery API</name>
7.15 <url>http://maven.apache.org</url>
7.16 <build>
7.17 @@ -18,8 +18,8 @@
7.18 <artifactId>maven-compiler-plugin</artifactId>
7.19 <version>2.3.2</version>
7.20 <configuration>
7.21 - <source>1.6</source>
7.22 - <target>1.6</target>
7.23 + <source>1.7</source>
7.24 + <target>1.7</target>
7.25 </configuration>
7.26 </plugin>
7.27 <plugin>
7.28 @@ -74,6 +74,12 @@
7.29 <scope>test</scope>
7.30 </dependency>
7.31 <dependency>
7.32 + <groupId>${project.groupId}</groupId>
7.33 + <artifactId>launcher.http</artifactId>
7.34 + <version>${project.version}</version>
7.35 + <scope>test</scope>
7.36 + </dependency>
7.37 + <dependency>
7.38 <groupId>org.netbeans.modules</groupId>
7.39 <artifactId>org-netbeans-modules-html-parser</artifactId>
7.40 </dependency>
8.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Attributes.java Tue Apr 02 15:40:51 2013 +0200
8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
8.3 @@ -1,57 +0,0 @@
8.4 -/**
8.5 - * Back 2 Browser Bytecode Translator
8.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
8.7 - *
8.8 - * This program is free software: you can redistribute it and/or modify
8.9 - * it under the terms of the GNU General Public License as published by
8.10 - * the Free Software Foundation, version 2 of the License.
8.11 - *
8.12 - * This program is distributed in the hope that it will be useful,
8.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
8.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.15 - * GNU General Public License for more details.
8.16 - *
8.17 - * You should have received a copy of the GNU General Public License
8.18 - * along with this program. Look for COPYING file in the top folder.
8.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
8.20 - */
8.21 -package org.apidesign.bck2brwsr.htmlpage;
8.22 -
8.23 -import java.util.HashMap;
8.24 -import java.util.Map;
8.25 -
8.26 -/**
8.27 - * Temporary storing the type of attributes here. This should be implemented in HTML 5 model
8.28 - *
8.29 - * @author Jan Horvath <jhorvath@netbeans.org>
8.30 - */
8.31 -public class Attributes {
8.32 -
8.33 - static final Map<String, String> TYPES = new HashMap<String, String>() {
8.34 - {
8.35 - // HTML Global Attributes
8.36 - // id attribute is already defined in Element, don't add it again
8.37 - put("accesskey", "String");
8.38 - put("class", "String");
8.39 - put("contenteditable", "Boolean");
8.40 - put("contextmenu", "String");
8.41 - put("dir", "String");
8.42 - put("draggable", "Boolean");
8.43 - put("dropzone", "String");
8.44 - put("hidden", "Boolean");
8.45 - put("lang", "String");
8.46 - put("spellcheck", "Boolean");
8.47 - put("style", "String");
8.48 - put("tabindex", "String");
8.49 - put("title", "String");
8.50 - put("translate", "Boolean");
8.51 - put("width", "Integer");
8.52 - put("height", "Integer");
8.53 -
8.54 - put("value", "String");
8.55 - put("disabled", "Boolean");
8.56 -
8.57 -// put("text", "String"); 'text' field is used to set innerHTML of element
8.58 - }
8.59 - };
8.60 -}
9.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Tue Apr 02 15:40:51 2013 +0200
9.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Thu May 02 09:18:22 2013 +0200
9.3 @@ -41,11 +41,115 @@
9.4 Object ret = getProperty(object, property);
9.5 return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE;
9.6 }
9.7 +
9.8 + public static <T> T toModel(Class<T> modelClass, Object object, String property) {
9.9 + Object ret = getProperty(object, property);
9.10 + if (ret == null || modelClass.isInstance(ret)) {
9.11 + return modelClass.cast(ret);
9.12 + }
9.13 + throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
9.14 + }
9.15 +
9.16 + public static String toJSON(Object value) {
9.17 + if (value == null) {
9.18 + return "null";
9.19 + }
9.20 + if (value instanceof Enum) {
9.21 + value = value.toString();
9.22 + }
9.23 + if (value instanceof String) {
9.24 + return '"' +
9.25 + ((String)value).
9.26 + replace("\"", "\\\"").
9.27 + replace("\n", "\\n").
9.28 + replace("\r", "\\r").
9.29 + replace("\t", "\\t")
9.30 + + '"';
9.31 + }
9.32 + return value.toString();
9.33 + }
9.34
9.35 @JavaScriptBody(args = { "object", "property" },
9.36 - body = "var p = object[property]; return p ? p : null;"
9.37 + body = "if (property === null) return object;\n"
9.38 + + "var p = object[property]; return p ? p : null;"
9.39 )
9.40 private static Object getProperty(Object object, String property) {
9.41 return null;
9.42 }
9.43 +
9.44 + public static String createJSONP(Object[] jsonResult, Runnable whenDone) {
9.45 + int h = whenDone.hashCode();
9.46 + String name;
9.47 + for (;;) {
9.48 + name = "jsonp" + Integer.toHexString(h);
9.49 + if (defineIfUnused(name, jsonResult, whenDone)) {
9.50 + return name;
9.51 + }
9.52 + h++;
9.53 + }
9.54 + }
9.55 +
9.56 + @JavaScriptBody(args = { "name", "arr", "run" }, body =
9.57 + "if (window[name]) return false;\n "
9.58 + + "window[name] = function(data) {\n "
9.59 + + " delete window[name];\n"
9.60 + + " var el = window.document.getElementById(name);\n"
9.61 + + " el.parentNode.removeChild(el);\n"
9.62 + + " arr[0] = data;\n"
9.63 + + " run.run__V();\n"
9.64 + + "};\n"
9.65 + + "return true;\n"
9.66 + )
9.67 + private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
9.68 + return true;
9.69 + }
9.70 +
9.71 + @JavaScriptBody(args = { "url", "arr", "callback" }, body = ""
9.72 + + "var request = new XMLHttpRequest();\n"
9.73 + + "request.open('GET', url, true);\n"
9.74 + + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
9.75 + + "request.onreadystatechange = function() {\n"
9.76 + + " if (this.readyState!==4) return;\n"
9.77 + + " try {\n"
9.78 + + " arr[0] = eval('(' + this.response + ')');\n"
9.79 + + " } catch (error) {;\n"
9.80 + + " throw 'Cannot parse' + error + ':' + this.response;\n"
9.81 + + " };\n"
9.82 + + " callback.run__V();\n"
9.83 + + "};"
9.84 + + "request.send();"
9.85 + )
9.86 + private static void loadJSON(
9.87 + String url, Object[] jsonResult, Runnable whenDone
9.88 + ) {
9.89 + }
9.90 +
9.91 + public static void loadJSON(
9.92 + String url, Object[] jsonResult, Runnable whenDone, String jsonp
9.93 + ) {
9.94 + if (jsonp == null) {
9.95 + loadJSON(url, jsonResult, whenDone);
9.96 + } else {
9.97 + loadJSONP(url, jsonp);
9.98 + }
9.99 + }
9.100 +
9.101 + @JavaScriptBody(args = { "url", "jsonp" }, body =
9.102 + "var scrpt = window.document.createElement('script');\n "
9.103 + + "scrpt.setAttribute('src', url);\n "
9.104 + + "scrpt.setAttribute('id', jsonp);\n "
9.105 + + "scrpt.setAttribute('type', 'text/javascript');\n "
9.106 + + "var body = document.getElementsByTagName('body')[0];\n "
9.107 + + "body.appendChild(scrpt);\n"
9.108 + )
9.109 + private static void loadJSONP(String url, String jsonp) {
9.110 +
9.111 + }
9.112 +
9.113 + public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
9.114 + for (int i = 0; i < props.length; i++) {
9.115 + values[i] = getProperty(jsonObject, props[i]);
9.116 + }
9.117 + }
9.118 +
9.119 }
10.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ElementGenerator.java Tue Apr 02 15:40:51 2013 +0200
10.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
10.3 @@ -1,175 +0,0 @@
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.htmlpage;
10.22 -
10.23 -import java.io.IOException;
10.24 -import java.io.OutputStreamWriter;
10.25 -import java.io.Writer;
10.26 -import java.util.Arrays;
10.27 -import java.util.Collections;
10.28 -import java.util.HashMap;
10.29 -import java.util.Map;
10.30 -import java.util.Map.Entry;
10.31 -import java.util.ServiceLoader;
10.32 -import javax.annotation.processing.ProcessingEnvironment;
10.33 -import javax.lang.model.element.Element;
10.34 -import javax.tools.Diagnostic;
10.35 -import javax.tools.FileObject;
10.36 -import org.netbeans.modules.html.editor.lib.api.HtmlVersion;
10.37 -import org.netbeans.modules.html.editor.lib.api.model.HtmlModel;
10.38 -import org.netbeans.modules.html.editor.lib.api.model.HtmlModelProvider;
10.39 -import org.netbeans.modules.html.editor.lib.api.model.HtmlTag;
10.40 -import org.netbeans.modules.html.editor.lib.api.model.HtmlTagAttribute;
10.41 -
10.42 -/**
10.43 - *
10.44 - * @author Jan Horvath <jhorvath@netbeans.org>
10.45 - */
10.46 -public class ElementGenerator {
10.47 -
10.48 - static final Map<String, String> NAMING_EXCEPTIONS = new HashMap<String, String>() {
10.49 - {
10.50 - put("img", "Image");
10.51 - put("class", "Clazz");
10.52 - }
10.53 - };
10.54 -
10.55 - static final String javaKeywords[] = {
10.56 - "abstract", "assert", "boolean", "break", "byte", "case",
10.57 - "catch", "char", "class", "const", "continue", "default",
10.58 - "do", "double", "else", "extends", "false", "final", "finally",
10.59 - "float", "for", "goto", "if", "implements", "import",
10.60 - "instanceof", "int", "interface", "long", "native", "new",
10.61 - "null", "package", "private", "protected", "public",
10.62 - "return", "short", "static", "strictfp", "super",
10.63 - "switch", "synchronized", "this", "throw", "throws",
10.64 - "transient", "true", "try", "void", "volatile", "while"
10.65 - };
10.66 -
10.67 - private static Map<String, String> elements = new HashMap<String, String>();
10.68 - private final ProcessingEnvironment processingEnv;
10.69 - private HtmlModel model = null;
10.70 -
10.71 - ElementGenerator(ProcessingEnvironment processingEnv) {
10.72 - this.processingEnv = processingEnv;
10.73 - }
10.74 -
10.75 - String getType(String pkg, String tag, Element e) {
10.76 - String className = elements.get(tag);
10.77 - if (className == null) {
10.78 - className = createClass(pkg, tag, e);
10.79 - elements.put(tag, className);
10.80 - }
10.81 - return className;
10.82 - }
10.83 -
10.84 - private String createClass(String pkg, String tag, Element e) {
10.85 - String className = className(tag);
10.86 - Writer w;
10.87 - try {
10.88 - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
10.89 - w = new OutputStreamWriter(java.openOutputStream());
10.90 - try {
10.91 - w.append("package " + pkg + ";\n\n");
10.92 - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
10.93 - PredefinedFields.appendImports(w, tag);
10.94 - w.append("\n");
10.95 -
10.96 - w.append("class ").append(className).append(" extends Element {\n\n");
10.97 - w.append(" public ").append(className).append("(String id) {\n");
10.98 - w.append(" super(id);\n");
10.99 - w.append(" }\n\n");
10.100 - for (Entry<String, String> entry : getAttributes(tag).entrySet()) {
10.101 - String attrName = entry.getKey();
10.102 - String attrType = entry.getValue();
10.103 - // getter
10.104 - w.append(" public ").append(attrType).append(" ")
10.105 - .append("get").append(className(attrName)).append("() {\n");
10.106 - w.append(" return (").append(attrType).append(")getAttribute(\"")
10.107 - .append(attrName).append("\");\n");
10.108 - w.append(" }\n\n");
10.109 - // setter
10.110 - w.append(" public void ")
10.111 - .append("set").append(className(attrName)).append("(")
10.112 - .append(attrType).append(" ").append(attributeName(attrName)).append(") {\n");
10.113 - w.append(" setAttribute(\"").append(attrName).append("\", ").append(attributeName(attrName)).append(");\n");
10.114 - w.append(" }\n\n");
10.115 - }
10.116 - PredefinedFields.appendFields(w, tag);
10.117 - w.append("}\n");
10.118 - } finally {
10.119 - w.close();
10.120 - }
10.121 - } catch (IOException ex) {
10.122 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
10.123 - return null;
10.124 - }
10.125 - return className;
10.126 - }
10.127 -
10.128 - Map<String, String> getAttributes(String tagName) {
10.129 - Map<String, String> result = new HashMap<String, String>();
10.130 -
10.131 - if (model == null) {
10.132 - // HtmlModelProvider modelProvider = Lookup.getDefault().lookup(HtmlModelProvider.class);
10.133 - ServiceLoader<HtmlModelProvider> hmpLoader =
10.134 - ServiceLoader.load(HtmlModelProvider.class, this.getClass().getClassLoader());
10.135 - for (HtmlModelProvider htmlModelProvider : hmpLoader) {
10.136 - model = htmlModelProvider.getModel(HtmlVersion.HTML5);
10.137 - if (model != null) {
10.138 - break;
10.139 - }
10.140 - }
10.141 - }
10.142 -
10.143 - if (model == null) {
10.144 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
10.145 - "HTML 5 model provider was not found on classpath");
10.146 - return Collections.emptyMap();
10.147 - }
10.148 - HtmlTag tag = model.getTag(tagName);
10.149 - for (HtmlTagAttribute attr : tag.getAttributes()) {
10.150 - String name = attr.getName();
10.151 - String type = Attributes.TYPES.get(name);
10.152 - if (type != null) {
10.153 - result.put(name, type);
10.154 - }
10.155 - }
10.156 -
10.157 - return result;
10.158 - }
10.159 -
10.160 - private String className(String s) {
10.161 - if (s.length() == 0) {
10.162 - return s;
10.163 - }
10.164 - String name = NAMING_EXCEPTIONS.get(s.toLowerCase());
10.165 - if (name == null) {
10.166 - name = s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
10.167 - }
10.168 - return name;
10.169 - }
10.170 -
10.171 - private String attributeName(String s) {
10.172 - if (Arrays.binarySearch(javaKeywords, s) >= 0) {
10.173 - return String.format("%sAttr", s);
10.174 - }
10.175 - return s;
10.176 - }
10.177 -
10.178 -}
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/KOList.java Thu May 02 09:18:22 2013 +0200
11.3 @@ -0,0 +1,167 @@
11.4 +/**
11.5 + * Back 2 Browser Bytecode Translator
11.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
11.7 + *
11.8 + * This program is free software: you can redistribute it and/or modify
11.9 + * it under the terms of the GNU General Public License as published by
11.10 + * the Free Software Foundation, version 2 of the License.
11.11 + *
11.12 + * This program is distributed in the hope that it will be useful,
11.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
11.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11.15 + * GNU General Public License for more details.
11.16 + *
11.17 + * You should have received a copy of the GNU General Public License
11.18 + * along with this program. Look for COPYING file in the top folder.
11.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
11.20 + */
11.21 +package org.apidesign.bck2brwsr.htmlpage;
11.22 +
11.23 +import java.util.ArrayList;
11.24 +import java.util.Collection;
11.25 +import java.util.Iterator;
11.26 +import org.apidesign.bck2brwsr.core.JavaScriptOnly;
11.27 +
11.28 +/**
11.29 + *
11.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
11.31 + */
11.32 +public final class KOList<T> extends ArrayList<T> {
11.33 + private final String name;
11.34 + private final String[] deps;
11.35 + private Knockout model;
11.36 + private Runnable onchange;
11.37 +
11.38 + public KOList(String name, String... deps) {
11.39 + this.name = name;
11.40 + this.deps = deps;
11.41 + }
11.42 +
11.43 + public void assign(Knockout model) {
11.44 + if (this.model != model) {
11.45 + this.model = model;
11.46 + notifyChange();
11.47 + }
11.48 + }
11.49 +
11.50 + public KOList<T> onChange(Runnable r) {
11.51 + if (this.onchange != null) {
11.52 + throw new IllegalStateException();
11.53 + }
11.54 + this.onchange = r;
11.55 + return this;
11.56 + }
11.57 +
11.58 + @Override
11.59 + public boolean add(T e) {
11.60 + boolean ret = super.add(e);
11.61 + notifyChange();
11.62 + return ret;
11.63 + }
11.64 +
11.65 + @Override
11.66 + public boolean addAll(Collection<? extends T> c) {
11.67 + boolean ret = super.addAll(c);
11.68 + notifyChange();
11.69 + return ret;
11.70 + }
11.71 +
11.72 + @Override
11.73 + public boolean addAll(int index, Collection<? extends T> c) {
11.74 + boolean ret = super.addAll(index, c);
11.75 + notifyChange();
11.76 + return ret;
11.77 + }
11.78 +
11.79 + @Override
11.80 + public boolean remove(Object o) {
11.81 + boolean ret = super.remove(o);
11.82 + notifyChange();
11.83 + return ret;
11.84 + }
11.85 +
11.86 + @Override
11.87 + public void clear() {
11.88 + super.clear();
11.89 + notifyChange();
11.90 + }
11.91 +
11.92 + @Override
11.93 + public boolean removeAll(Collection<?> c) {
11.94 + boolean ret = super.removeAll(c);
11.95 + notifyChange();
11.96 + return ret;
11.97 + }
11.98 +
11.99 + @Override
11.100 + public boolean retainAll(Collection<?> c) {
11.101 + boolean ret = super.retainAll(c);
11.102 + notifyChange();
11.103 + return ret;
11.104 + }
11.105 +
11.106 + @Override
11.107 + public T set(int index, T element) {
11.108 + T ret = super.set(index, element);
11.109 + notifyChange();
11.110 + return ret;
11.111 + }
11.112 +
11.113 + @Override
11.114 + public void add(int index, T element) {
11.115 + super.add(index, element);
11.116 + notifyChange();
11.117 + }
11.118 +
11.119 + @Override
11.120 + public T remove(int index) {
11.121 + T ret = super.remove(index);
11.122 + notifyChange();
11.123 + return ret;
11.124 + }
11.125 +
11.126 + @Override
11.127 + public String toString() {
11.128 + Iterator<T> it = iterator();
11.129 + if (!it.hasNext()) {
11.130 + return "[]";
11.131 + }
11.132 + String sep = "";
11.133 + StringBuilder sb = new StringBuilder();
11.134 + sb.append('[');
11.135 + while (it.hasNext()) {
11.136 + T t = it.next();
11.137 + sb.append(sep);
11.138 + sb.append(ConvertTypes.toJSON(t));
11.139 + sep = ",";
11.140 + }
11.141 + sb.append(']');
11.142 + return sb.toString();
11.143 + }
11.144 +
11.145 +
11.146 + @JavaScriptOnly(name = "koArray", value = "function() { return this.toArray___3Ljava_lang_Object_2(); }")
11.147 + private static native int koArray();
11.148 +
11.149 + private void notifyChange() {
11.150 + Knockout m = model;
11.151 + if (m != null) {
11.152 + m.valueHasMutated(name);
11.153 + for (String dependant : deps) {
11.154 + m.valueHasMutated(dependant);
11.155 + }
11.156 + }
11.157 + Runnable r = onchange;
11.158 + if (r != null) {
11.159 + r.run();
11.160 + }
11.161 + }
11.162 +
11.163 + @Override
11.164 + public KOList clone() {
11.165 + KOList ko = (KOList)super.clone();
11.166 + ko.model = null;
11.167 + return ko;
11.168 + }
11.169 +
11.170 +}
12.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Tue Apr 02 15:40:51 2013 +0200
12.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Thu May 02 09:18:22 2013 +0200
12.3 @@ -18,6 +18,7 @@
12.4 package org.apidesign.bck2brwsr.htmlpage;
12.5
12.6 import java.lang.reflect.Method;
12.7 +import java.util.List;
12.8 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
12.9 import org.apidesign.bck2brwsr.core.JavaScriptBody;
12.10
12.11 @@ -29,38 +30,40 @@
12.12 public class Knockout {
12.13 /** used by tests */
12.14 static Knockout next;
12.15 -
12.16 - Knockout() {
12.17 + private final Object model;
12.18 +
12.19 + Knockout(Object model) {
12.20 + this.model = model == null ? this : model;
12.21 }
12.22
12.23 public static <M> Knockout applyBindings(
12.24 - Class<M> modelClass, M model, String[] propsGettersAndSetters
12.25 + Object model, String[] propsGettersAndSetters,
12.26 + String[] methodsAndSignatures
12.27 + ) {
12.28 + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
12.29 + return new Knockout(model);
12.30 + }
12.31 + public static <M> Knockout applyBindings(
12.32 + Class<M> modelClass, M model, String[] propsGettersAndSetters,
12.33 + String[] methodsAndSignatures
12.34 ) {
12.35 Knockout bindings = next;
12.36 next = null;
12.37 if (bindings == null) {
12.38 - bindings = new Knockout();
12.39 + bindings = new Knockout(null);
12.40 }
12.41 - for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
12.42 - try {
12.43 - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
12.44 - bind(bindings, model, propsGettersAndSetters[i],
12.45 - propsGettersAndSetters[i + 1],
12.46 - propsGettersAndSetters[i + 2],
12.47 - getter.getReturnType().isPrimitive()
12.48 - );
12.49 - } catch (NoSuchMethodException ex) {
12.50 - throw new IllegalStateException(ex.getMessage());
12.51 - }
12.52 - }
12.53 + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
12.54 applyBindings(bindings);
12.55 return bindings;
12.56 }
12.57
12.58 - @JavaScriptBody(args = { "prop" }, body =
12.59 - "this[prop].valueHasMutated();"
12.60 + public void valueHasMutated(String prop) {
12.61 + valueHasMutated(model, prop);
12.62 + }
12.63 + @JavaScriptBody(args = { "self", "prop" }, body =
12.64 + "self[prop].valueHasMutated();"
12.65 )
12.66 - public void valueHasMutated(String prop) {
12.67 + public void valueHasMutated(Object self, String prop) {
12.68 }
12.69
12.70
12.71 @@ -68,26 +71,60 @@
12.72 public static void triggerEvent(String id, String ev) {
12.73 }
12.74
12.75 - @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive" }, body =
12.76 + @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
12.77 "var bnd = {\n"
12.78 - + " read: function() {\n"
12.79 + + " 'read': function() {\n"
12.80 + " var v = model[getter]();\n"
12.81 + + " if (array) v = v.koArray();\n"
12.82 + " return v;\n"
12.83 + " },\n"
12.84 - + " owner: bindings\n"
12.85 + + " 'owner': bindings\n"
12.86 + "};\n"
12.87 + "if (setter != null) {\n"
12.88 - + " bnd.write = function(val) {\n"
12.89 + + " bnd['write'] = function(val) {\n"
12.90 + " model[setter](primitive ? new Number(val) : val);\n"
12.91 + " };\n"
12.92 + "}\n"
12.93 - + "bindings[prop] = ko.computed(bnd);"
12.94 + + "bindings[prop] = ko['computed'](bnd);"
12.95 )
12.96 private static void bind(
12.97 - Object bindings, Object model, String prop, String getter, String setter, boolean primitive
12.98 + Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
12.99 + ) {
12.100 + }
12.101 +
12.102 + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
12.103 + "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
12.104 + )
12.105 + private static void expose(
12.106 + Object bindings, Object model, String prop, String sig
12.107 ) {
12.108 }
12.109
12.110 @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
12.111 private static void applyBindings(Object bindings) {}
12.112 +
12.113 + private static void applyImpl(
12.114 + String[] propsGettersAndSetters,
12.115 + Class<?> modelClass,
12.116 + Object bindings,
12.117 + Object model,
12.118 + String[] methodsAndSignatures
12.119 + ) throws IllegalStateException, SecurityException {
12.120 + for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
12.121 + try {
12.122 + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
12.123 + bind(bindings, model, propsGettersAndSetters[i],
12.124 + propsGettersAndSetters[i + 1],
12.125 + propsGettersAndSetters[i + 2],
12.126 + getter.getReturnType().isPrimitive(),
12.127 + List.class.isAssignableFrom(getter.getReturnType()));
12.128 + } catch (NoSuchMethodException ex) {
12.129 + throw new IllegalStateException(ex.getMessage());
12.130 + }
12.131 + }
12.132 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
12.133 + expose(
12.134 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
12.135 + }
12.136 + }
12.137 }
13.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Apr 02 15:40:51 2013 +0200
13.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Thu May 02 09:18:22 2013 +0200
13.3 @@ -20,23 +20,30 @@
13.4 import java.io.IOException;
13.5 import java.io.InputStream;
13.6 import java.io.OutputStreamWriter;
13.7 +import java.io.StringWriter;
13.8 import java.io.Writer;
13.9 +import java.lang.annotation.AnnotationTypeMismatchException;
13.10 +import java.lang.annotation.IncompleteAnnotationException;
13.11 +import java.lang.reflect.Method;
13.12 import java.util.ArrayList;
13.13 import java.util.Collection;
13.14 import java.util.Collections;
13.15 import java.util.HashMap;
13.16 +import java.util.HashSet;
13.17 import java.util.LinkedHashSet;
13.18 import java.util.List;
13.19 -import java.util.Locale;
13.20 import java.util.Map;
13.21 import java.util.Set;
13.22 +import java.util.WeakHashMap;
13.23 import javax.annotation.processing.AbstractProcessor;
13.24 import javax.annotation.processing.Completion;
13.25 import javax.annotation.processing.Completions;
13.26 +import javax.annotation.processing.ProcessingEnvironment;
13.27 import javax.annotation.processing.Processor;
13.28 import javax.annotation.processing.RoundEnvironment;
13.29 import javax.annotation.processing.SupportedAnnotationTypes;
13.30 import javax.lang.model.element.AnnotationMirror;
13.31 +import javax.lang.model.element.AnnotationValue;
13.32 import javax.lang.model.element.Element;
13.33 import javax.lang.model.element.ElementKind;
13.34 import javax.lang.model.element.ExecutableElement;
13.35 @@ -44,14 +51,21 @@
13.36 import javax.lang.model.element.PackageElement;
13.37 import javax.lang.model.element.TypeElement;
13.38 import javax.lang.model.element.VariableElement;
13.39 +import javax.lang.model.type.ArrayType;
13.40 import javax.lang.model.type.MirroredTypeException;
13.41 import javax.lang.model.type.TypeKind;
13.42 import javax.lang.model.type.TypeMirror;
13.43 +import javax.lang.model.util.Elements;
13.44 +import javax.lang.model.util.Types;
13.45 import javax.tools.Diagnostic;
13.46 import javax.tools.FileObject;
13.47 import javax.tools.StandardLocation;
13.48 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
13.49 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
13.50 import org.apidesign.bck2brwsr.htmlpage.api.On;
13.51 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
13.52 +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange;
13.53 +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
13.54 import org.apidesign.bck2brwsr.htmlpage.api.Page;
13.55 import org.apidesign.bck2brwsr.htmlpage.api.Property;
13.56 import org.openide.util.lookup.ServiceProvider;
13.57 @@ -63,90 +77,70 @@
13.58 */
13.59 @ServiceProvider(service=Processor.class)
13.60 @SupportedAnnotationTypes({
13.61 + "org.apidesign.bck2brwsr.htmlpage.api.Model",
13.62 "org.apidesign.bck2brwsr.htmlpage.api.Page",
13.63 + "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
13.64 + "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
13.65 + "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange",
13.66 + "org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty",
13.67 "org.apidesign.bck2brwsr.htmlpage.api.On"
13.68 })
13.69 public final class PageProcessor extends AbstractProcessor {
13.70 + private final Map<Element,String> models = new WeakHashMap<>();
13.71 + private final Map<Element,Prprt[]> verify = new WeakHashMap<>();
13.72 @Override
13.73 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
13.74 - for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
13.75 - Page p = e.getAnnotation(Page.class);
13.76 - if (p == null) {
13.77 - continue;
13.78 - }
13.79 - PackageElement pe = (PackageElement)e.getEnclosingElement();
13.80 - String pkg = pe.getQualifiedName().toString();
13.81 -
13.82 - ProcessPage pp;
13.83 - ElementGenerator eGen = new ElementGenerator(processingEnv);
13.84 - try {
13.85 - InputStream is = openStream(pkg, p.xhtml());
13.86 - pp = ProcessPage.readPage(is);
13.87 - is.close();
13.88 - } catch (IOException iOException) {
13.89 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
13.90 - return false;
13.91 - }
13.92 - Writer w;
13.93 - String className = p.className();
13.94 - if (className.isEmpty()) {
13.95 - int indx = p.xhtml().indexOf('.');
13.96 - className = p.xhtml().substring(0, indx);
13.97 - }
13.98 - try {
13.99 - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
13.100 - w = new OutputStreamWriter(java.openOutputStream());
13.101 - try {
13.102 - w.append("package " + pkg + ";\n");
13.103 - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
13.104 - w.append("final class ").append(className).append(" {\n");
13.105 - w.append(" private boolean locked;\n");
13.106 - if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
13.107 - return false;
13.108 - }
13.109 - for (String id : pp.ids()) {
13.110 - String tag = pp.tagNameForId(id);
13.111 - String type = eGen.getType(pkg, tag, e);
13.112 - w.append(" ").append("public final ").
13.113 - append(type).append(' ').append(cnstnt(id)).append(" = new ").
13.114 - append(type).append("(\"").append(id).append("\");\n");
13.115 - }
13.116 - List<String> propsGetSet = new ArrayList<String>();
13.117 - Map<String,Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
13.118 - generateComputedProperties(w, e.getEnclosedElements(), propsGetSet, propsDeps);
13.119 - generateProperties(w, p.properties(), propsGetSet, propsDeps);
13.120 - w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
13.121 - if (!propsGetSet.isEmpty()) {
13.122 - w.write("public " + className + " applyBindings() {\n");
13.123 - w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
13.124 - w.write(className + ".class, this, ");
13.125 - w.write("new String[] {\n");
13.126 - String sep = "";
13.127 - for (String n : propsGetSet) {
13.128 - w.write(sep);
13.129 - if (n == null) {
13.130 - w.write(" null");
13.131 - } else {
13.132 - w.write(" \"" + n + "\"");
13.133 - }
13.134 - sep = ",\n";
13.135 - }
13.136 - w.write("\n });\n return this;\n}\n");
13.137 -
13.138 - w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
13.139 - w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
13.140 - w.write("}\n");
13.141 - }
13.142 - w.append("}\n");
13.143 - } finally {
13.144 - w.close();
13.145 - }
13.146 - } catch (IOException ex) {
13.147 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
13.148 - return false;
13.149 + boolean ok = true;
13.150 + for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
13.151 + if (!processModel(e)) {
13.152 + ok = false;
13.153 }
13.154 }
13.155 - return true;
13.156 + for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
13.157 + if (!processPage(e)) {
13.158 + ok = false;
13.159 + }
13.160 + }
13.161 + if (roundEnv.processingOver()) {
13.162 + models.clear();
13.163 + for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
13.164 + TypeElement te = (TypeElement)entry.getKey();
13.165 + String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
13.166 + Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
13.167 + if (finalElem == null) {
13.168 + continue;
13.169 + }
13.170 + Prprt[] props;
13.171 + Model m = finalElem.getAnnotation(Model.class);
13.172 + if (m != null) {
13.173 + props = Prprt.wrap(processingEnv, finalElem, m.properties());
13.174 + } else {
13.175 + Page p = finalElem.getAnnotation(Page.class);
13.176 + props = Prprt.wrap(processingEnv, finalElem, p.properties());
13.177 + }
13.178 + for (Prprt p : props) {
13.179 + boolean[] isModel = { false };
13.180 + boolean[] isEnum = { false };
13.181 + boolean[] isPrimitive = { false };
13.182 + String t = checkType(p, isModel, isEnum, isPrimitive);
13.183 + if (isEnum[0]) {
13.184 + continue;
13.185 + }
13.186 + if (isPrimitive[0]) {
13.187 + continue;
13.188 + }
13.189 + if (isModel[0]) {
13.190 + continue;
13.191 + }
13.192 + if ("java.lang.String".equals(t)) {
13.193 + continue;
13.194 + }
13.195 + error("The type " + t + " should be defined by @Model annotation", entry.getKey());
13.196 + }
13.197 + }
13.198 + verify.clear();
13.199 + }
13.200 + return ok;
13.201 }
13.202
13.203 private InputStream openStream(String pkg, String name) throws IOException {
13.204 @@ -159,13 +153,263 @@
13.205 }
13.206 }
13.207
13.208 + private void error(String msg, Element e) {
13.209 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
13.210 + }
13.211 +
13.212 + private boolean processModel(Element e) {
13.213 + boolean ok = true;
13.214 + Model m = e.getAnnotation(Model.class);
13.215 + if (m == null) {
13.216 + return true;
13.217 + }
13.218 + String pkg = findPkgName(e);
13.219 + Writer w;
13.220 + String className = m.className();
13.221 + models.put(e, className);
13.222 + try {
13.223 + StringWriter body = new StringWriter();
13.224 + List<String> propsGetSet = new ArrayList<>();
13.225 + List<String> functions = new ArrayList<>();
13.226 + Map<String, Collection<String>> propsDeps = new HashMap<>();
13.227 + Map<String, Collection<String>> functionDeps = new HashMap<>();
13.228 + Prprt[] props = createProps(e, m.properties());
13.229 +
13.230 + if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
13.231 + ok = false;
13.232 + }
13.233 + if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
13.234 + ok = false;
13.235 + }
13.236 + if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
13.237 + ok = false;
13.238 + }
13.239 + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
13.240 + ok = false;
13.241 + }
13.242 + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
13.243 + w = new OutputStreamWriter(java.openOutputStream());
13.244 + try {
13.245 + w.append("package " + pkg + ";\n");
13.246 + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
13.247 + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
13.248 + w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n");
13.249 + w.append("final class ").append(className).append(" implements Cloneable {\n");
13.250 + w.append(" private boolean locked;\n");
13.251 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
13.252 + w.append(body.toString());
13.253 + w.append(" private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
13.254 + w.append(" public ").append(className).append("() {\n");
13.255 + w.append(" intKnckt();\n");
13.256 + w.append(" };\n");
13.257 + w.append(" private void intKnckt() {\n");
13.258 + w.append(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
13.259 + writeStringArray(propsGetSet, w);
13.260 + w.append(", ");
13.261 + writeStringArray(functions, w);
13.262 + w.append(" );\n");
13.263 + w.append(" };\n");
13.264 + w.append(" ").append(className).append("(Object json) {\n");
13.265 + int values = 0;
13.266 + for (int i = 0; i < propsGetSet.size(); i += 4) {
13.267 + Prprt p = findPrprt(props, propsGetSet.get(i));
13.268 + if (p == null) {
13.269 + continue;
13.270 + }
13.271 + values++;
13.272 + }
13.273 + w.append(" Object[] ret = new Object[" + values + "];\n");
13.274 + w.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
13.275 + for (int i = 0; i < propsGetSet.size(); i += 4) {
13.276 + Prprt p = findPrprt(props, propsGetSet.get(i));
13.277 + if (p == null) {
13.278 + continue;
13.279 + }
13.280 + w.append(" \"").append(propsGetSet.get(i)).append("\",\n");
13.281 + }
13.282 + w.append(" }, ret);\n");
13.283 + for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
13.284 + final String pn = propsGetSet.get(i);
13.285 + Prprt p = findPrprt(props, pn);
13.286 + if (p == null) {
13.287 + continue;
13.288 + }
13.289 + boolean[] isModel = { false };
13.290 + boolean[] isEnum = { false };
13.291 + boolean isPrimitive[] = { false };
13.292 + String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
13.293 + if (p.array()) {
13.294 + w.append("if (ret[" + cnt + "] instanceof Object[]) {\n");
13.295 + w.append(" for (Object e : ((Object[])ret[" + cnt + "])) {\n");
13.296 + if (isModel[0]) {
13.297 + w.append(" this.prop_").append(pn).append(".add(new ");
13.298 + w.append(type).append("(e));\n");
13.299 + } else if (isEnum[0]) {
13.300 + w.append(" this.prop_").append(pn);
13.301 + w.append(".add(e == null ? null : ");
13.302 + w.append(type).append(".valueOf((String)e));\n");
13.303 + } else {
13.304 + if (isPrimitive(type)) {
13.305 + w.append(" this.prop_").append(pn).append(".add(((Number)e).");
13.306 + w.append(type).append("Value());\n");
13.307 + } else {
13.308 + w.append(" this.prop_").append(pn).append(".add((");
13.309 + w.append(type).append(")e);\n");
13.310 + }
13.311 + }
13.312 + w.append(" }\n");
13.313 + w.append("}\n");
13.314 + } else {
13.315 + if (isEnum[0]) {
13.316 + w.append(" this.prop_").append(pn);
13.317 + w.append(" = ret[" + cnt + "] == null ? null : ");
13.318 + w.append(type).append(".valueOf((String)ret[" + cnt + "]);\n");
13.319 + } else if (isPrimitive(type)) {
13.320 + w.append(" this.prop_").append(pn);
13.321 + w.append(" = ((Number)").append("ret[" + cnt + "]).");
13.322 + w.append(type).append("Value();\n");
13.323 + } else {
13.324 + w.append(" this.prop_").append(pn);
13.325 + w.append(" = (").append(type).append(')');
13.326 + w.append("ret[" + cnt + "];\n");
13.327 + }
13.328 + }
13.329 + cnt++;
13.330 + }
13.331 + w.append(" intKnckt();\n");
13.332 + w.append(" };\n");
13.333 + writeToString(props, w);
13.334 + writeClone(className, props, w);
13.335 + w.append("}\n");
13.336 + } finally {
13.337 + w.close();
13.338 + }
13.339 + } catch (IOException ex) {
13.340 + error("Can't create " + className + ".java", e);
13.341 + return false;
13.342 + }
13.343 + return ok;
13.344 + }
13.345 +
13.346 + private boolean processPage(Element e) {
13.347 + boolean ok = true;
13.348 + Page p = e.getAnnotation(Page.class);
13.349 + if (p == null) {
13.350 + return true;
13.351 + }
13.352 + String pkg = findPkgName(e);
13.353 +
13.354 + ProcessPage pp;
13.355 + try (InputStream is = openStream(pkg, p.xhtml())) {
13.356 + pp = ProcessPage.readPage(is);
13.357 + is.close();
13.358 + } catch (IOException iOException) {
13.359 + error("Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
13.360 + ok = false;
13.361 + pp = null;
13.362 + }
13.363 + Writer w;
13.364 + String className = p.className();
13.365 + if (className.isEmpty()) {
13.366 + int indx = p.xhtml().indexOf('.');
13.367 + className = p.xhtml().substring(0, indx);
13.368 + }
13.369 + try {
13.370 + StringWriter body = new StringWriter();
13.371 + List<String> propsGetSet = new ArrayList<>();
13.372 + List<String> functions = new ArrayList<>();
13.373 + Map<String, Collection<String>> propsDeps = new HashMap<>();
13.374 + Map<String, Collection<String>> functionDeps = new HashMap<>();
13.375 +
13.376 + Prprt[] props = createProps(e, p.properties());
13.377 + if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
13.378 + ok = false;
13.379 + }
13.380 + if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
13.381 + ok = false;
13.382 + }
13.383 + if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
13.384 + ok = false;
13.385 + }
13.386 + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
13.387 + ok = false;
13.388 + }
13.389 + if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) {
13.390 + ok = false;
13.391 + }
13.392 +
13.393 + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
13.394 + w = new OutputStreamWriter(java.openOutputStream());
13.395 + try {
13.396 + w.append("package " + pkg + ";\n");
13.397 + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
13.398 + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
13.399 + w.append("final class ").append(className).append(" {\n");
13.400 + w.append(" private boolean locked;\n");
13.401 + if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
13.402 + ok = false;
13.403 + } else {
13.404 + if (pp != null) for (String id : pp.ids()) {
13.405 + String tag = pp.tagNameForId(id);
13.406 + String type = type(tag);
13.407 + w.append(" ").append("public final ").
13.408 + append(type).append(' ').append(cnstnt(id)).append(" = new ").
13.409 + append(type).append("(\"").append(id).append("\");\n");
13.410 + }
13.411 + }
13.412 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
13.413 + w.append(body.toString());
13.414 + if (!propsGetSet.isEmpty()) {
13.415 + w.write("public " + className + " applyBindings() {\n");
13.416 + w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
13.417 + w.write(className + ".class, this, ");
13.418 + writeStringArray(propsGetSet, w);
13.419 + w.append(", ");
13.420 + writeStringArray(functions, w);
13.421 + w.write(");\n return this;\n}\n");
13.422 +
13.423 + w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
13.424 + w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
13.425 + w.write("}\n");
13.426 + }
13.427 + w.append("}\n");
13.428 + } finally {
13.429 + w.close();
13.430 + }
13.431 + } catch (IOException ex) {
13.432 + error("Can't create " + className + ".java", e);
13.433 + return false;
13.434 + }
13.435 + return ok;
13.436 + }
13.437 +
13.438 + private static String type(String tag) {
13.439 + if (tag.equals("title")) {
13.440 + return "Title";
13.441 + }
13.442 + if (tag.equals("button")) {
13.443 + return "Button";
13.444 + }
13.445 + if (tag.equals("input")) {
13.446 + return "Input";
13.447 + }
13.448 + if (tag.equals("canvas")) {
13.449 + return "Canvas";
13.450 + }
13.451 + if (tag.equals("img")) {
13.452 + return "Image";
13.453 + }
13.454 + return "Element";
13.455 + }
13.456 +
13.457 private static String cnstnt(String id) {
13.458 - return id.toUpperCase(Locale.ENGLISH).replace('.', '_').replace('-', '_');
13.459 + return id.replace('.', '_').replace('-', '_');
13.460 }
13.461
13.462 private boolean initializeOnClick(
13.463 String className, TypeElement type, Writer w, ProcessPage pp
13.464 ) throws IOException {
13.465 + boolean ok = true;
13.466 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
13.467 { //for (Element clazz : pe.getEnclosedElements()) {
13.468 // if (clazz.getKind() != ElementKind.CLASS) {
13.469 @@ -178,58 +422,27 @@
13.470 On oc = method.getAnnotation(On.class);
13.471 if (oc != null) {
13.472 for (String id : oc.id()) {
13.473 + if (pp == null) {
13.474 + error("id = " + id + " not found in HTML page.", method);
13.475 + ok = false;
13.476 + continue;
13.477 + }
13.478 if (pp.tagNameForId(id) == null) {
13.479 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
13.480 - return false;
13.481 + error("id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
13.482 + ok = false;
13.483 + continue;
13.484 }
13.485 ExecutableElement ee = (ExecutableElement)method;
13.486 - StringBuilder params = new StringBuilder();
13.487 - {
13.488 - boolean first = true;
13.489 - for (VariableElement ve : ee.getParameters()) {
13.490 - if (!first) {
13.491 - params.append(", ");
13.492 - }
13.493 - first = false;
13.494 - if (ve.asType() == stringType) {
13.495 - if (ve.getSimpleName().contentEquals("id")) {
13.496 - params.append('"').append(id).append('"');
13.497 - continue;
13.498 - }
13.499 - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(ev, \"");
13.500 - params.append(ve.getSimpleName().toString());
13.501 - params.append("\")");
13.502 - continue;
13.503 - }
13.504 - if (processingEnv.getTypeUtils().getPrimitiveType(TypeKind.DOUBLE) == ve.asType()) {
13.505 - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(ev, \"");
13.506 - params.append(ve.getSimpleName().toString());
13.507 - params.append("\")");
13.508 - continue;
13.509 - }
13.510 - String rn = ve.asType().toString();
13.511 - int last = rn.lastIndexOf('.');
13.512 - if (last >= 0) {
13.513 - rn = rn.substring(last + 1);
13.514 - }
13.515 - if (rn.equals(className)) {
13.516 - params.append(className).append(".this");
13.517 - continue;
13.518 - }
13.519 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
13.520 - "@On method can only accept String named 'id' or " + className + " arguments",
13.521 - ee
13.522 - );
13.523 - return false;
13.524 - }
13.525 - }
13.526 + CharSequence params = wrapParams(ee, id, className, "ev", null);
13.527 if (!ee.getModifiers().contains(Modifier.STATIC)) {
13.528 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
13.529 - return false;
13.530 + error("@On method has to be static", ee);
13.531 + ok = false;
13.532 + continue;
13.533 }
13.534 if (ee.getModifiers().contains(Modifier.PRIVATE)) {
13.535 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
13.536 - return false;
13.537 + error("@On method can't be private", ee);
13.538 + ok = false;
13.539 + continue;
13.540 }
13.541 w.append(" OnEvent." + oc.event()).append(".of(").append(cnstnt(id)).
13.542 append(").perform(new OnDispatch(" + dispatchCnt + "));\n");
13.543 @@ -260,7 +473,7 @@
13.544
13.545
13.546 }
13.547 - return true;
13.548 + return ok;
13.549 }
13.550
13.551 @Override
13.552 @@ -274,8 +487,7 @@
13.553
13.554 Element cls = findClass(element);
13.555 Page p = cls.getAnnotation(Page.class);
13.556 - PackageElement pe = (PackageElement) cls.getEnclosingElement();
13.557 - String pkg = pe.getQualifiedName().toString();
13.558 + String pkg = findPkgName(cls);
13.559 ProcessPage pp;
13.560 try {
13.561 InputStream is = openStream(pkg, p.xhtml());
13.562 @@ -285,7 +497,7 @@
13.563 return Collections.emptyList();
13.564 }
13.565
13.566 - List<Completion> cc = new ArrayList<Completion>();
13.567 + List<Completion> cc = new ArrayList<>();
13.568 userText = userText.substring(1);
13.569 for (String id : pp.ids()) {
13.570 if (id.startsWith(userText)) {
13.571 @@ -306,44 +518,89 @@
13.572 return e.getEnclosingElement();
13.573 }
13.574
13.575 - private static void generateProperties(
13.576 - Writer w, Property[] properties, Collection<String> props,
13.577 - Map<String,Collection<String>> deps
13.578 + private boolean generateProperties(
13.579 + Element where,
13.580 + Writer w, Prprt[] properties,
13.581 + Collection<String> props,
13.582 + Map<String,Collection<String>> deps,
13.583 + Map<String,Collection<String>> functionDeps
13.584 ) throws IOException {
13.585 - for (Property p : properties) {
13.586 - final String tn = typeName(p);
13.587 - String[] gs = toGetSet(p.name(), tn);
13.588 + boolean ok = true;
13.589 + for (Prprt p : properties) {
13.590 + final String tn;
13.591 + tn = typeName(where, p);
13.592 + String[] gs = toGetSet(p.name(), tn, p.array());
13.593
13.594 - w.write("private " + tn + " prop_" + p.name() + ";\n");
13.595 - w.write("public " + tn + " " + gs[0] + "() {\n");
13.596 - w.write(" if (locked) throw new IllegalStateException();\n");
13.597 - w.write(" return prop_" + p.name() + ";\n");
13.598 - w.write("}\n");
13.599 - w.write("public void " + gs[1] + "(" + tn + " v) {\n");
13.600 - w.write(" if (locked) throw new IllegalStateException();\n");
13.601 - w.write(" prop_" + p.name() + " = v;\n");
13.602 - w.write(" if (ko != null) {\n");
13.603 - w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n");
13.604 - final Collection<String> dependants = deps.get(p.name());
13.605 - if (dependants != null) {
13.606 - for (String depProp : dependants) {
13.607 - w.write(" ko.valueHasMutated(\"" + depProp + "\");\n");
13.608 + if (p.array()) {
13.609 + w.write("private KOList<" + tn + "> prop_" + p.name() + " = new KOList<" + tn + ">(\""
13.610 + + p.name() + "\"");
13.611 + Collection<String> dependants = deps.get(p.name());
13.612 + if (dependants != null) {
13.613 + for (String depProp : dependants) {
13.614 + w.write(", ");
13.615 + w.write('\"');
13.616 + w.write(depProp);
13.617 + w.write('\"');
13.618 + }
13.619 }
13.620 + w.write(")");
13.621 +
13.622 + dependants = functionDeps.get(p.name());
13.623 + if (dependants != null) {
13.624 + w.write(".onChange(new Runnable() { public void run() {\n");
13.625 + for (String call : dependants) {
13.626 + w.append(call);
13.627 + }
13.628 + w.write("}})");
13.629 + }
13.630 + w.write(";\n");
13.631 +
13.632 + w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n");
13.633 + w.write(" if (locked) throw new IllegalStateException();\n");
13.634 + w.write(" prop_" + p.name() + ".assign(ko);\n");
13.635 + w.write(" return prop_" + p.name() + ";\n");
13.636 + w.write("}\n");
13.637 + } else {
13.638 + w.write("private " + tn + " prop_" + p.name() + ";\n");
13.639 + w.write("public " + tn + " " + gs[0] + "() {\n");
13.640 + w.write(" if (locked) throw new IllegalStateException();\n");
13.641 + w.write(" return prop_" + p.name() + ";\n");
13.642 + w.write("}\n");
13.643 + w.write("public void " + gs[1] + "(" + tn + " v) {\n");
13.644 + w.write(" if (locked) throw new IllegalStateException();\n");
13.645 + w.write(" prop_" + p.name() + " = v;\n");
13.646 + w.write(" if (ko != null) {\n");
13.647 + w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n");
13.648 + Collection<String> dependants = deps.get(p.name());
13.649 + if (dependants != null) {
13.650 + for (String depProp : dependants) {
13.651 + w.write(" ko.valueHasMutated(\"" + depProp + "\");\n");
13.652 + }
13.653 + }
13.654 + w.write(" }\n");
13.655 + dependants = functionDeps.get(p.name());
13.656 + if (dependants != null) {
13.657 + for (String call : dependants) {
13.658 + w.append(call);
13.659 + }
13.660 + }
13.661 + w.write("}\n");
13.662 }
13.663 - w.write(" }\n");
13.664 - w.write("}\n");
13.665
13.666 props.add(p.name());
13.667 props.add(gs[2]);
13.668 props.add(gs[3]);
13.669 props.add(gs[0]);
13.670 }
13.671 + return ok;
13.672 }
13.673
13.674 private boolean generateComputedProperties(
13.675 - Writer w, Collection<? extends Element> arr, Collection<String> props,
13.676 + Writer w, Prprt[] fixedProps,
13.677 + Collection<? extends Element> arr, Collection<String> props,
13.678 Map<String,Collection<String>> deps
13.679 ) throws IOException {
13.680 + boolean ok = true;
13.681 for (Element e : arr) {
13.682 if (e.getKind() != ElementKind.METHOD) {
13.683 continue;
13.684 @@ -352,30 +609,43 @@
13.685 continue;
13.686 }
13.687 ExecutableElement ee = (ExecutableElement)e;
13.688 - final String tn = ee.getReturnType().toString();
13.689 + final TypeMirror rt = ee.getReturnType();
13.690 + final Types tu = processingEnv.getTypeUtils();
13.691 + TypeMirror ert = tu.erasure(rt);
13.692 + String tn = fqn(ert, ee);
13.693 + boolean array = false;
13.694 + if (tn.equals("java.util.List")) {
13.695 + array = true;
13.696 + }
13.697 +
13.698 final String sn = ee.getSimpleName().toString();
13.699 - String[] gs = toGetSet(sn, tn);
13.700 + String[] gs = toGetSet(sn, tn, array);
13.701
13.702 w.write("public " + tn + " " + gs[0] + "() {\n");
13.703 w.write(" if (locked) throw new IllegalStateException();\n");
13.704 int arg = 0;
13.705 for (VariableElement pe : ee.getParameters()) {
13.706 final String dn = pe.getSimpleName().toString();
13.707 - final String dt = pe.asType().toString();
13.708 - String[] call = toGetSet(dn, dt);
13.709 +
13.710 + if (!verifyPropName(pe, dn, fixedProps)) {
13.711 + ok = false;
13.712 + }
13.713 +
13.714 + final String dt = fqn(pe.asType(), ee);
13.715 + String[] call = toGetSet(dn, dt, false);
13.716 w.write(" " + dt + " arg" + (++arg) + " = ");
13.717 w.write(call[0] + "();\n");
13.718
13.719 Collection<String> depends = deps.get(dn);
13.720 if (depends == null) {
13.721 - depends = new LinkedHashSet<String>();
13.722 + depends = new LinkedHashSet<>();
13.723 deps.put(dn, depends);
13.724 }
13.725 depends.add(sn);
13.726 }
13.727 w.write(" try {\n");
13.728 w.write(" locked = true;\n");
13.729 - w.write(" return " + e.getEnclosingElement().getSimpleName() + '.' + e.getSimpleName() + "(");
13.730 + w.write(" return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
13.731 String sep = "";
13.732 for (int i = 1; i <= arg; i++) {
13.733 w.write(sep);
13.734 @@ -387,17 +657,17 @@
13.735 w.write(" locked = false;\n");
13.736 w.write(" }\n");
13.737 w.write("}\n");
13.738 -
13.739 +
13.740 props.add(e.getSimpleName().toString());
13.741 props.add(gs[2]);
13.742 props.add(null);
13.743 props.add(gs[0]);
13.744 }
13.745
13.746 - return true;
13.747 + return ok;
13.748 }
13.749
13.750 - private static String[] toGetSet(String name, String type) {
13.751 + private static String[] toGetSet(String name, String type, boolean array) {
13.752 String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
13.753 String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
13.754 if ("int".equals(type)) {
13.755 @@ -412,6 +682,14 @@
13.756 bck2brwsrType = "Z";
13.757 }
13.758 final String nu = n.replace('.', '_');
13.759 + if (array) {
13.760 + return new String[] {
13.761 + "get" + n,
13.762 + null,
13.763 + "get" + nu + "__Ljava_util_List_2",
13.764 + null
13.765 + };
13.766 + }
13.767 return new String[]{
13.768 pref + n,
13.769 "set" + n,
13.770 @@ -420,11 +698,702 @@
13.771 };
13.772 }
13.773
13.774 - private static String typeName(Property p) {
13.775 - try {
13.776 - return p.type().getName();
13.777 - } catch (MirroredTypeException ex) {
13.778 - return ex.getTypeMirror().toString();
13.779 + private String typeName(Element where, Prprt p) {
13.780 + String ret;
13.781 + boolean[] isModel = { false };
13.782 + boolean[] isEnum = { false };
13.783 + boolean isPrimitive[] = { false };
13.784 + ret = checkType(p, isModel, isEnum, isPrimitive);
13.785 + if (p.array()) {
13.786 + String bt = findBoxedType(ret);
13.787 + if (bt != null) {
13.788 + return bt;
13.789 + }
13.790 + }
13.791 + return ret;
13.792 + }
13.793 +
13.794 + private static String findBoxedType(String ret) {
13.795 + if (ret.equals("boolean")) {
13.796 + return Boolean.class.getName();
13.797 + }
13.798 + if (ret.equals("byte")) {
13.799 + return Byte.class.getName();
13.800 + }
13.801 + if (ret.equals("short")) {
13.802 + return Short.class.getName();
13.803 + }
13.804 + if (ret.equals("char")) {
13.805 + return Character.class.getName();
13.806 + }
13.807 + if (ret.equals("int")) {
13.808 + return Integer.class.getName();
13.809 + }
13.810 + if (ret.equals("long")) {
13.811 + return Long.class.getName();
13.812 + }
13.813 + if (ret.equals("float")) {
13.814 + return Float.class.getName();
13.815 + }
13.816 + if (ret.equals("double")) {
13.817 + return Double.class.getName();
13.818 + }
13.819 + return null;
13.820 + }
13.821 +
13.822 + private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
13.823 + StringBuilder sb = new StringBuilder();
13.824 + String sep = "";
13.825 + for (Prprt Prprt : existingProps) {
13.826 + if (Prprt.name().equals(propName)) {
13.827 + return true;
13.828 + }
13.829 + sb.append(sep);
13.830 + sb.append('"');
13.831 + sb.append(Prprt.name());
13.832 + sb.append('"');
13.833 + sep = ", ";
13.834 + }
13.835 + error(
13.836 + propName + " is not one of known properties: " + sb
13.837 + , e
13.838 + );
13.839 + return false;
13.840 + }
13.841 +
13.842 + private static String findPkgName(Element e) {
13.843 + for (;;) {
13.844 + if (e.getKind() == ElementKind.PACKAGE) {
13.845 + return ((PackageElement)e).getQualifiedName().toString();
13.846 + }
13.847 + e = e.getEnclosingElement();
13.848 }
13.849 }
13.850 +
13.851 + private boolean generateFunctions(
13.852 + Element clazz, StringWriter body, String className,
13.853 + List<? extends Element> enclosedElements, List<String> functions
13.854 + ) {
13.855 + for (Element m : enclosedElements) {
13.856 + if (m.getKind() != ElementKind.METHOD) {
13.857 + continue;
13.858 + }
13.859 + ExecutableElement e = (ExecutableElement)m;
13.860 + OnFunction onF = e.getAnnotation(OnFunction.class);
13.861 + if (onF == null) {
13.862 + continue;
13.863 + }
13.864 + if (!e.getModifiers().contains(Modifier.STATIC)) {
13.865 + error("@OnFunction method needs to be static", e);
13.866 + return false;
13.867 + }
13.868 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
13.869 + error("@OnFunction method cannot be private", e);
13.870 + return false;
13.871 + }
13.872 + if (e.getReturnType().getKind() != TypeKind.VOID) {
13.873 + error("@OnFunction method should return void", e);
13.874 + return false;
13.875 + }
13.876 + String n = e.getSimpleName().toString();
13.877 + body.append("private void ").append(n).append("(Object data, Object ev) {\n");
13.878 + body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
13.879 + body.append(wrapParams(e, null, className, "ev", "data"));
13.880 + body.append(");\n");
13.881 + body.append("}\n");
13.882 +
13.883 + functions.add(n);
13.884 + functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
13.885 + }
13.886 + return true;
13.887 + }
13.888 +
13.889 + private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
13.890 + Prprt[] properties, String className,
13.891 + Map<String, Collection<String>> functionDeps
13.892 + ) {
13.893 + for (Element m : clazz.getEnclosedElements()) {
13.894 + if (m.getKind() != ElementKind.METHOD) {
13.895 + continue;
13.896 + }
13.897 + ExecutableElement e = (ExecutableElement) m;
13.898 + OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
13.899 + if (onPC == null) {
13.900 + continue;
13.901 + }
13.902 + for (String pn : onPC.value()) {
13.903 + if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
13.904 + error("No Prprt named '" + pn + "' in the model", clazz);
13.905 + return false;
13.906 + }
13.907 + }
13.908 + if (!e.getModifiers().contains(Modifier.STATIC)) {
13.909 + error("@OnPrprtChange method needs to be static", e);
13.910 + return false;
13.911 + }
13.912 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
13.913 + error("@OnPrprtChange method cannot be private", e);
13.914 + return false;
13.915 + }
13.916 + if (e.getReturnType().getKind() != TypeKind.VOID) {
13.917 + error("@OnPrprtChange method should return void", e);
13.918 + return false;
13.919 + }
13.920 + String n = e.getSimpleName().toString();
13.921 +
13.922 +
13.923 + for (String pn : onPC.value()) {
13.924 + StringBuilder call = new StringBuilder();
13.925 + call.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
13.926 + call.append(wrapPropName(e, className, "name", pn));
13.927 + call.append(");\n");
13.928 +
13.929 + Collection<String> change = functionDeps.get(pn);
13.930 + if (change == null) {
13.931 + change = new ArrayList<>();
13.932 + functionDeps.put(pn, change);
13.933 + }
13.934 + change.add(call.toString());
13.935 + for (String dpn : findDerivedFrom(propDeps, pn)) {
13.936 + change = functionDeps.get(dpn);
13.937 + if (change == null) {
13.938 + change = new ArrayList<>();
13.939 + functionDeps.put(dpn, change);
13.940 + }
13.941 + change.add(call.toString());
13.942 + }
13.943 + }
13.944 + }
13.945 + return true;
13.946 + }
13.947 +
13.948 + private boolean generateReceive(
13.949 + Element clazz, StringWriter body, String className,
13.950 + List<? extends Element> enclosedElements, List<String> functions
13.951 + ) {
13.952 + for (Element m : enclosedElements) {
13.953 + if (m.getKind() != ElementKind.METHOD) {
13.954 + continue;
13.955 + }
13.956 + ExecutableElement e = (ExecutableElement)m;
13.957 + OnReceive onR = e.getAnnotation(OnReceive.class);
13.958 + if (onR == null) {
13.959 + continue;
13.960 + }
13.961 + if (!e.getModifiers().contains(Modifier.STATIC)) {
13.962 + error("@OnReceive method needs to be static", e);
13.963 + return false;
13.964 + }
13.965 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
13.966 + error("@OnReceive method cannot be private", e);
13.967 + return false;
13.968 + }
13.969 + if (e.getReturnType().getKind() != TypeKind.VOID) {
13.970 + error("@OnReceive method should return void", e);
13.971 + return false;
13.972 + }
13.973 + String modelClass = null;
13.974 + boolean expectsList = false;
13.975 + List<String> args = new ArrayList<>();
13.976 + {
13.977 + for (VariableElement ve : e.getParameters()) {
13.978 + TypeMirror modelType = null;
13.979 + if (ve.asType().toString().equals(className)) {
13.980 + args.add(className + ".this");
13.981 + } else if (isModel(ve.asType())) {
13.982 + modelType = ve.asType();
13.983 + } else if (ve.asType().getKind() == TypeKind.ARRAY) {
13.984 + modelType = ((ArrayType)ve.asType()).getComponentType();
13.985 + expectsList = true;
13.986 + }
13.987 + if (modelType != null) {
13.988 + if (modelClass != null) {
13.989 + error("There can be only one model class among arguments", e);
13.990 + } else {
13.991 + modelClass = modelType.toString();
13.992 + if (expectsList) {
13.993 + args.add("arr");
13.994 + } else {
13.995 + args.add("arr[0]");
13.996 + }
13.997 + }
13.998 + }
13.999 + }
13.1000 + }
13.1001 + if (modelClass == null) {
13.1002 + error("The method needs to have one @Model class as parameter", e);
13.1003 + }
13.1004 + String n = e.getSimpleName().toString();
13.1005 + body.append("public void ").append(n).append("(");
13.1006 + StringBuilder assembleURL = new StringBuilder();
13.1007 + String jsonpVarName = null;
13.1008 + {
13.1009 + String sep = "";
13.1010 + boolean skipJSONP = onR.jsonp().isEmpty();
13.1011 + for (String p : findParamNames(e, onR.url(), assembleURL)) {
13.1012 + if (!skipJSONP && p.equals(onR.jsonp())) {
13.1013 + skipJSONP = true;
13.1014 + jsonpVarName = p;
13.1015 + continue;
13.1016 + }
13.1017 + body.append(sep);
13.1018 + body.append("String ").append(p);
13.1019 + sep = ", ";
13.1020 + }
13.1021 + if (!skipJSONP) {
13.1022 + error(
13.1023 + "Name of jsonp attribute ('" + onR.jsonp() +
13.1024 + "') is not used in url attribute '" + onR.url() + "'", e
13.1025 + );
13.1026 + }
13.1027 + }
13.1028 + body.append(") {\n");
13.1029 + body.append(" final Object[] result = { null };\n");
13.1030 + body.append(
13.1031 + " class ProcessResult implements Runnable {\n" +
13.1032 + " @Override\n" +
13.1033 + " public void run() {\n" +
13.1034 + " Object value = result[0];\n");
13.1035 + body.append(
13.1036 + " " + modelClass + "[] arr;\n");
13.1037 + body.append(
13.1038 + " if (value instanceof Object[]) {\n" +
13.1039 + " Object[] data = ((Object[])value);\n" +
13.1040 + " arr = new " + modelClass + "[data.length];\n" +
13.1041 + " for (int i = 0; i < data.length; i++) {\n" +
13.1042 + " arr[i] = new " + modelClass + "(data[i]);\n" +
13.1043 + " }\n" +
13.1044 + " } else {\n" +
13.1045 + " arr = new " + modelClass + "[1];\n" +
13.1046 + " arr[0] = new " + modelClass + "(value);\n" +
13.1047 + " }\n"
13.1048 + );
13.1049 + {
13.1050 + body.append(clazz.getSimpleName()).append(".").append(n).append("(");
13.1051 + String sep = "";
13.1052 + for (String arg : args) {
13.1053 + body.append(sep);
13.1054 + body.append(arg);
13.1055 + sep = ", ";
13.1056 + }
13.1057 + body.append(");\n");
13.1058 + }
13.1059 + body.append(
13.1060 + " }\n" +
13.1061 + " }\n"
13.1062 + );
13.1063 + body.append(" ProcessResult pr = new ProcessResult();\n");
13.1064 + if (jsonpVarName != null) {
13.1065 + body.append(" String ").append(jsonpVarName).
13.1066 + append(" = org.apidesign.bck2brwsr.htmlpage.ConvertTypes.createJSONP(result, pr);\n");
13.1067 + }
13.1068 + body.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.loadJSON(\n ");
13.1069 + body.append(assembleURL);
13.1070 + body.append(", result, pr, ").append(jsonpVarName).append("\n );\n");
13.1071 +// body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
13.1072 +// body.append(wrapParams(e, null, className, "ev", "data"));
13.1073 +// body.append(");\n");
13.1074 + body.append("}\n");
13.1075 + }
13.1076 + return true;
13.1077 + }
13.1078 +
13.1079 + private CharSequence wrapParams(
13.1080 + ExecutableElement ee, String id, String className, String evName, String dataName
13.1081 + ) {
13.1082 + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
13.1083 + StringBuilder params = new StringBuilder();
13.1084 + boolean first = true;
13.1085 + for (VariableElement ve : ee.getParameters()) {
13.1086 + if (!first) {
13.1087 + params.append(", ");
13.1088 + }
13.1089 + first = false;
13.1090 + String toCall = null;
13.1091 + if (ve.asType() == stringType) {
13.1092 + if (ve.getSimpleName().contentEquals("id")) {
13.1093 + params.append('"').append(id).append('"');
13.1094 + continue;
13.1095 + }
13.1096 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(";
13.1097 + }
13.1098 + if (ve.asType().getKind() == TypeKind.DOUBLE) {
13.1099 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(";
13.1100 + }
13.1101 + if (ve.asType().getKind() == TypeKind.INT) {
13.1102 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt(";
13.1103 + }
13.1104 + if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
13.1105 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toModel(" + ve.asType() + ".class, ";
13.1106 + }
13.1107 +
13.1108 + if (toCall != null) {
13.1109 + params.append(toCall);
13.1110 + if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
13.1111 + params.append(dataName);
13.1112 + params.append(", null");
13.1113 + } else {
13.1114 + if (evName == null) {
13.1115 + final StringBuilder sb = new StringBuilder();
13.1116 + sb.append("Unexpected string parameter name.");
13.1117 + if (dataName != null) {
13.1118 + sb.append(" Try \"").append(dataName).append("\"");
13.1119 + }
13.1120 + error(sb.toString(), ee);
13.1121 + }
13.1122 + params.append(evName);
13.1123 + params.append(", \"");
13.1124 + params.append(ve.getSimpleName().toString());
13.1125 + params.append("\"");
13.1126 + }
13.1127 + params.append(")");
13.1128 + continue;
13.1129 + }
13.1130 + String rn = fqn(ve.asType(), ee);
13.1131 + int last = rn.lastIndexOf('.');
13.1132 + if (last >= 0) {
13.1133 + rn = rn.substring(last + 1);
13.1134 + }
13.1135 + if (rn.equals(className)) {
13.1136 + params.append(className).append(".this");
13.1137 + continue;
13.1138 + }
13.1139 + error(
13.1140 + "@On method can only accept String named 'id' or " + className + " arguments",
13.1141 + ee
13.1142 + );
13.1143 + }
13.1144 + return params;
13.1145 + }
13.1146 +
13.1147 +
13.1148 + private CharSequence wrapPropName(
13.1149 + ExecutableElement ee, String className, String propName, String propValue
13.1150 + ) {
13.1151 + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
13.1152 + StringBuilder params = new StringBuilder();
13.1153 + boolean first = true;
13.1154 + for (VariableElement ve : ee.getParameters()) {
13.1155 + if (!first) {
13.1156 + params.append(", ");
13.1157 + }
13.1158 + first = false;
13.1159 + if (ve.asType() == stringType) {
13.1160 + if (propName != null && ve.getSimpleName().contentEquals(propName)) {
13.1161 + params.append('"').append(propValue).append('"');
13.1162 + } else {
13.1163 + error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
13.1164 + }
13.1165 + continue;
13.1166 + }
13.1167 + String rn = fqn(ve.asType(), ee);
13.1168 + int last = rn.lastIndexOf('.');
13.1169 + if (last >= 0) {
13.1170 + rn = rn.substring(last + 1);
13.1171 + }
13.1172 + if (rn.equals(className)) {
13.1173 + params.append(className).append(".this");
13.1174 + continue;
13.1175 + }
13.1176 + error(
13.1177 + "@OnPrprtChange method can only accept String or " + className + " arguments",
13.1178 + ee);
13.1179 + }
13.1180 + return params;
13.1181 + }
13.1182 +
13.1183 + private boolean isModel(TypeMirror tm) {
13.1184 + final Element e = processingEnv.getTypeUtils().asElement(tm);
13.1185 + if (e == null) {
13.1186 + return false;
13.1187 + }
13.1188 + for (Element ch : e.getEnclosedElements()) {
13.1189 + if (ch.getKind() == ElementKind.METHOD) {
13.1190 + ExecutableElement ee = (ExecutableElement)ch;
13.1191 + if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
13.1192 + return true;
13.1193 + }
13.1194 + }
13.1195 + }
13.1196 + return models.values().contains(e.getSimpleName().toString());
13.1197 + }
13.1198 +
13.1199 + private void writeStringArray(List<String> strings, Writer w) throws IOException {
13.1200 + w.write("new String[] {\n");
13.1201 + String sep = "";
13.1202 + for (String n : strings) {
13.1203 + w.write(sep);
13.1204 + if (n == null) {
13.1205 + w.write(" null");
13.1206 + } else {
13.1207 + w.write(" \"" + n + "\"");
13.1208 + }
13.1209 + sep = ",\n";
13.1210 + }
13.1211 + w.write("\n }");
13.1212 + }
13.1213 +
13.1214 + private void writeToString(Prprt[] props, Writer w) throws IOException {
13.1215 + w.write(" public String toString() {\n");
13.1216 + w.write(" StringBuilder sb = new StringBuilder();\n");
13.1217 + w.write(" sb.append('{');\n");
13.1218 + String sep = "";
13.1219 + for (Prprt p : props) {
13.1220 + w.write(sep);
13.1221 + w.append(" sb.append('\"').append(\"" + p.name() + "\")");
13.1222 + w.append(".append('\"').append(\":\");\n");
13.1223 + w.append(" sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_");
13.1224 + w.append(p.name()).append("));\n");
13.1225 + sep = " sb.append(',');\n";
13.1226 + }
13.1227 + w.write(" sb.append('}');\n");
13.1228 + w.write(" return sb.toString();\n");
13.1229 + w.write(" }\n");
13.1230 + }
13.1231 + private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
13.1232 + w.write(" public " + className + " clone() {\n");
13.1233 + w.write(" " + className + " ret = new " + className + "();\n");
13.1234 + for (Prprt p : props) {
13.1235 + if (!p.array()) {
13.1236 + boolean isModel[] = { false };
13.1237 + boolean isEnum[] = { false };
13.1238 + boolean isPrimitive[] = { false };
13.1239 + checkType(p, isModel, isEnum, isPrimitive);
13.1240 + if (!isModel[0]) {
13.1241 + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
13.1242 + continue;
13.1243 + }
13.1244 + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
13.1245 + } else {
13.1246 + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
13.1247 + }
13.1248 + }
13.1249 +
13.1250 + w.write(" return ret;\n");
13.1251 + w.write(" }\n");
13.1252 + }
13.1253 +
13.1254 + private String inPckName(Element e) {
13.1255 + StringBuilder sb = new StringBuilder();
13.1256 + while (e.getKind() != ElementKind.PACKAGE) {
13.1257 + if (sb.length() == 0) {
13.1258 + sb.append(e.getSimpleName());
13.1259 + } else {
13.1260 + sb.insert(0, '.');
13.1261 + sb.insert(0, e.getSimpleName());
13.1262 + }
13.1263 + e = e.getEnclosingElement();
13.1264 + }
13.1265 + return sb.toString();
13.1266 + }
13.1267 +
13.1268 + private String fqn(TypeMirror pt, Element relative) {
13.1269 + if (pt.getKind() == TypeKind.ERROR) {
13.1270 + final Elements eu = processingEnv.getElementUtils();
13.1271 + PackageElement pckg = eu.getPackageOf(relative);
13.1272 + return pckg.getQualifiedName() + "." + pt.toString();
13.1273 + }
13.1274 + return pt.toString();
13.1275 + }
13.1276 +
13.1277 + private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
13.1278 + TypeMirror tm;
13.1279 + try {
13.1280 + String ret = p.typeName(processingEnv);
13.1281 + TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
13.1282 + if (e == null) {
13.1283 + isModel[0] = true;
13.1284 + isEnum[0] = false;
13.1285 + isPrimitive[0] = false;
13.1286 + return ret;
13.1287 + }
13.1288 + tm = e.asType();
13.1289 + } catch (MirroredTypeException ex) {
13.1290 + tm = ex.getTypeMirror();
13.1291 + }
13.1292 + tm = processingEnv.getTypeUtils().erasure(tm);
13.1293 + isPrimitive[0] = tm.getKind().isPrimitive();
13.1294 + final Element e = processingEnv.getTypeUtils().asElement(tm);
13.1295 + final Model m = e == null ? null : e.getAnnotation(Model.class);
13.1296 +
13.1297 + String ret;
13.1298 + if (m != null) {
13.1299 + ret = findPkgName(e) + '.' + m.className();
13.1300 + isModel[0] = true;
13.1301 + models.put(e, m.className());
13.1302 + } else if (findModelForMthd(e)) {
13.1303 + ret = ((TypeElement)e).getQualifiedName().toString();
13.1304 + isModel[0] = true;
13.1305 + } else {
13.1306 + ret = tm.toString();
13.1307 + }
13.1308 + TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
13.1309 + enm = processingEnv.getTypeUtils().erasure(enm);
13.1310 + isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
13.1311 + return ret;
13.1312 + }
13.1313 +
13.1314 + private static boolean findModelForMthd(Element clazz) {
13.1315 + if (clazz == null) {
13.1316 + return false;
13.1317 + }
13.1318 + for (Element e : clazz.getEnclosedElements()) {
13.1319 + if (e.getKind() == ElementKind.METHOD) {
13.1320 + ExecutableElement ee = (ExecutableElement)e;
13.1321 + if (
13.1322 + ee.getSimpleName().contentEquals("modelFor") &&
13.1323 + ee.getParameters().isEmpty()
13.1324 + ) {
13.1325 + return true;
13.1326 + }
13.1327 + }
13.1328 + }
13.1329 + return false;
13.1330 + }
13.1331 +
13.1332 + private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
13.1333 + List<String> params = new ArrayList<>();
13.1334 +
13.1335 + for (int pos = 0; ;) {
13.1336 + int next = url.indexOf('{', pos);
13.1337 + if (next == -1) {
13.1338 + assembleURL.append('"')
13.1339 + .append(url.substring(pos))
13.1340 + .append('"');
13.1341 + return params;
13.1342 + }
13.1343 + int close = url.indexOf('}', next);
13.1344 + if (close == -1) {
13.1345 + error("Unbalanced '{' and '}' in " + url, e);
13.1346 + return params;
13.1347 + }
13.1348 + final String paramName = url.substring(next + 1, close);
13.1349 + params.add(paramName);
13.1350 + assembleURL.append('"')
13.1351 + .append(url.substring(pos, next))
13.1352 + .append("\" + ").append(paramName).append(" + ");
13.1353 + pos = close + 1;
13.1354 + }
13.1355 + }
13.1356 +
13.1357 + private static Prprt findPrprt(Prprt[] properties, String propName) {
13.1358 + for (Prprt p : properties) {
13.1359 + if (propName.equals(p.name())) {
13.1360 + return p;
13.1361 + }
13.1362 + }
13.1363 + return null;
13.1364 + }
13.1365 +
13.1366 + private boolean isPrimitive(String type) {
13.1367 + return
13.1368 + "int".equals(type) ||
13.1369 + "double".equals(type) ||
13.1370 + "long".equals(type) ||
13.1371 + "short".equals(type) ||
13.1372 + "byte".equals(type) ||
13.1373 + "float".equals(type);
13.1374 + }
13.1375 +
13.1376 + private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
13.1377 + Set<String> names = new HashSet<>();
13.1378 + for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
13.1379 + if (e.getValue().contains(derivedProp)) {
13.1380 + names.add(e.getKey());
13.1381 + }
13.1382 + }
13.1383 + return names;
13.1384 + }
13.1385 +
13.1386 + private Prprt[] createProps(Element e, Property[] arr) {
13.1387 + Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
13.1388 + Prprt[] prev = verify.put(e, ret);
13.1389 + if (prev != null) {
13.1390 + error("Two sets of properties for ", e);
13.1391 + }
13.1392 + return ret;
13.1393 + }
13.1394 +
13.1395 + private static class Prprt {
13.1396 + private final Element e;
13.1397 + private final AnnotationMirror tm;
13.1398 + private final Property p;
13.1399 +
13.1400 + public Prprt(Element e, AnnotationMirror tm, Property p) {
13.1401 + this.e = e;
13.1402 + this.tm = tm;
13.1403 + this.p = p;
13.1404 + }
13.1405 +
13.1406 + String name() {
13.1407 + return p.name();
13.1408 + }
13.1409 +
13.1410 + boolean array() {
13.1411 + return p.array();
13.1412 + }
13.1413 +
13.1414 + String typeName(ProcessingEnvironment env) {
13.1415 + try {
13.1416 + return p.type().getName();
13.1417 + } catch (IncompleteAnnotationException | AnnotationTypeMismatchException ex) {
13.1418 + for (Object v : getAnnoValues(env)) {
13.1419 + String s = v.toString().replace(" ", "");
13.1420 + if (s.startsWith("type=") && s.endsWith(".class")) {
13.1421 + return s.substring(5, s.length() - 6);
13.1422 + }
13.1423 + }
13.1424 + throw ex;
13.1425 + }
13.1426 + }
13.1427 +
13.1428 +
13.1429 + static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
13.1430 + if (arr.length == 0) {
13.1431 + return new Prprt[0];
13.1432 + }
13.1433 +
13.1434 + if (e.getKind() != ElementKind.CLASS) {
13.1435 + throw new IllegalStateException("" + e.getKind());
13.1436 + }
13.1437 + TypeElement te = (TypeElement)e;
13.1438 + List<? extends AnnotationValue> val = null;
13.1439 + for (AnnotationMirror an : te.getAnnotationMirrors()) {
13.1440 + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
13.1441 + if (entry.getKey().getSimpleName().contentEquals("properties")) {
13.1442 + val = (List)entry.getValue().getValue();
13.1443 + break;
13.1444 + }
13.1445 + }
13.1446 + }
13.1447 + if (val == null || val.size() != arr.length) {
13.1448 + pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
13.1449 + return new Prprt[0];
13.1450 + }
13.1451 + Prprt[] ret = new Prprt[arr.length];
13.1452 + BIG: for (int i = 0; i < ret.length; i++) {
13.1453 + AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
13.1454 + ret[i] = new Prprt(e, am, arr[i]);
13.1455 +
13.1456 + }
13.1457 + return ret;
13.1458 + }
13.1459 +
13.1460 + private List<? extends Object> getAnnoValues(ProcessingEnvironment pe) {
13.1461 + try {
13.1462 + Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
13.1463 + Method m = trees.getMethod("instance", ProcessingEnvironment.class);
13.1464 + Object instance = m.invoke(null, pe);
13.1465 + m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
13.1466 + Object path = m.invoke(instance, e, tm);
13.1467 + m = path.getClass().getMethod("getLeaf");
13.1468 + Object leaf = m.invoke(path);
13.1469 + m = leaf.getClass().getMethod("getArguments");
13.1470 + return (List)m.invoke(leaf);
13.1471 + } catch (Exception ex) {
13.1472 + return Collections.emptyList();
13.1473 + }
13.1474 + }
13.1475 + }
13.1476 +
13.1477 }
14.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PredefinedFields.java Tue Apr 02 15:40:51 2013 +0200
14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
14.3 @@ -1,65 +0,0 @@
14.4 -/**
14.5 - * Back 2 Browser Bytecode Translator
14.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
14.7 - *
14.8 - * This program is free software: you can redistribute it and/or modify
14.9 - * it under the terms of the GNU General Public License as published by
14.10 - * the Free Software Foundation, version 2 of the License.
14.11 - *
14.12 - * This program is distributed in the hope that it will be useful,
14.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
14.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14.15 - * GNU General Public License for more details.
14.16 - *
14.17 - * You should have received a copy of the GNU General Public License
14.18 - * along with this program. Look for COPYING file in the top folder.
14.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
14.20 - */
14.21 -package org.apidesign.bck2brwsr.htmlpage;
14.22 -
14.23 -import java.io.IOException;
14.24 -import java.io.Writer;
14.25 -import java.util.HashMap;
14.26 -import java.util.Map;
14.27 -
14.28 -/**
14.29 - *
14.30 - * @author Jan Horvath <jhorvath@netbeans.org>
14.31 - */
14.32 -public class PredefinedFields {
14.33 -
14.34 - private static final Map<String, String> IMPORTS = new HashMap<String, String>() {
14.35 - {
14.36 - put("canvas", "import org.apidesign.bck2brwsr.core.JavaScriptBody;");
14.37 - }
14.38 - };
14.39 -
14.40 - private static final Map<String, String> FIELDS = new HashMap<String, String>() {
14.41 - {
14.42 - put("canvas",
14.43 - " @JavaScriptBody(\n" +
14.44 - " args = {\"el\"},\n" +
14.45 - " body = \"var e = window.document.getElementById(el._id());\\n\"\n" +
14.46 - " + \"return e.getContext('2d');\\n\")\n" +
14.47 - " private native static Object getContextImpl(Canvas el);\n" +
14.48 - " \n" +
14.49 - " public GraphicsContext getContext() {\n" +
14.50 - " return new GraphicsContext(getContextImpl(this));\n" +
14.51 - " }");
14.52 - }
14.53 - };
14.54 -
14.55 - static void appendImports(Writer w, String tag) throws IOException {
14.56 - String text = IMPORTS.get(tag.toLowerCase());
14.57 - if (text != null) {
14.58 - w.append(text).append("\n");
14.59 - }
14.60 - }
14.61 -
14.62 - static void appendFields(Writer w, String tag) throws IOException {
14.63 - String text = FIELDS.get(tag.toLowerCase());
14.64 - if (text != null) {
14.65 - w.append(text).append("\n");
14.66 - }
14.67 - }
14.68 -}
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java Thu May 02 09:18:22 2013 +0200
15.3 @@ -0,0 +1,36 @@
15.4 +/**
15.5 + * Back 2 Browser Bytecode Translator
15.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
15.7 + *
15.8 + * This program is free software: you can redistribute it and/or modify
15.9 + * it under the terms of the GNU General Public License as published by
15.10 + * the Free Software Foundation, version 2 of the License.
15.11 + *
15.12 + * This program is distributed in the hope that it will be useful,
15.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
15.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15.15 + * GNU General Public License for more details.
15.16 + *
15.17 + * You should have received a copy of the GNU General Public License
15.18 + * along with this program. Look for COPYING file in the top folder.
15.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
15.20 + */
15.21 +package org.apidesign.bck2brwsr.htmlpage.api;
15.22 +
15.23 +/**
15.24 + *
15.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
15.26 + */
15.27 +public final class Button extends Element {
15.28 + public Button(String id) {
15.29 + super(id);
15.30 + }
15.31 +
15.32 + @Override
15.33 + void dontSubclass() {
15.34 + }
15.35 +
15.36 + public void setDisabled(boolean state) {
15.37 + setAttribute(this, "disabled", state);
15.38 + }
15.39 +}
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Canvas.java Thu May 02 09:18:22 2013 +0200
16.3 @@ -0,0 +1,64 @@
16.4 +/**
16.5 + * Back 2 Browser Bytecode Translator
16.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
16.7 + *
16.8 + * This program is free software: you can redistribute it and/or modify
16.9 + * it under the terms of the GNU General Public License as published by
16.10 + * the Free Software Foundation, version 2 of the License.
16.11 + *
16.12 + * This program is distributed in the hope that it will be useful,
16.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
16.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16.15 + * GNU General Public License for more details.
16.16 + *
16.17 + * You should have received a copy of the GNU General Public License
16.18 + * along with this program. Look for COPYING file in the top folder.
16.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
16.20 + */
16.21 +package org.apidesign.bck2brwsr.htmlpage.api;
16.22 +
16.23 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
16.24 +import static org.apidesign.bck2brwsr.htmlpage.api.Element.getAttribute;
16.25 +
16.26 +/**
16.27 + *
16.28 + * @author Anton Epple <toni.epple@eppleton.de>
16.29 + */
16.30 +public class Canvas extends Element {
16.31 +
16.32 + public Canvas(String id) {
16.33 + super(id);
16.34 + }
16.35 +
16.36 + public void setHeight(int height) {
16.37 + setAttribute(this, "height", height);
16.38 + }
16.39 +
16.40 + public int getHeight() {
16.41 + Object ret = getAttribute(this, "height");
16.42 + return (ret instanceof Number) ? ((Number)ret).intValue(): Integer.MIN_VALUE;
16.43 + }
16.44 +
16.45 + public void setWidth(int width) {
16.46 + setAttribute(this, "width", width);
16.47 + }
16.48 +
16.49 + public int getWidth() {
16.50 + Object ret = getAttribute(this, "width");
16.51 + return (ret instanceof Number) ? ((Number)ret).intValue(): Integer.MIN_VALUE;
16.52 + }
16.53 +
16.54 + @JavaScriptBody(
16.55 + args = {"el"},
16.56 + body = "var e = window.document.getElementById(el._id());\n"
16.57 + + "return e.getContext('2d');\n")
16.58 + private native static Object getContextImpl(Canvas el);
16.59 +
16.60 + public GraphicsContext getContext() {
16.61 + return new GraphicsContext(getContextImpl(this));
16.62 + }
16.63 +
16.64 + @Override
16.65 + void dontSubclass() {
16.66 + }
16.67 +}
17.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/ComputedProperty.java Tue Apr 02 15:40:51 2013 +0200
17.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/ComputedProperty.java Thu May 02 09:18:22 2013 +0200
17.3 @@ -22,16 +22,11 @@
17.4 import java.lang.annotation.RetentionPolicy;
17.5 import java.lang.annotation.Target;
17.6
17.7 -/** Can be used in classes annotated with {@link Page} annotation to
17.8 - * define a derived property. Value of derived property is based on values
17.9 - * of {@link Property} as enumerated by {@link Page#properties()}.
17.10 - * <p>
17.11 - * The name of the derived property is the name of the method. The arguments
17.12 - * of the method define the property names (from {@link Page#properties()} list)
17.13 - * the value of property depends on.
17.14 - *
17.15 +/**
17.16 + * @deprecated Replaced by new {@link net.java.html.json.ComputedProperty net.java.html.json} API.
17.17 * @author Jaroslav Tulach <jtulach@netbeans.org>
17.18 */
17.19 +@Deprecated
17.20 @Retention(RetentionPolicy.SOURCE)
17.21 @Target(ElementType.METHOD)
17.22 public @interface ComputedProperty {
18.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Tue Apr 02 15:40:51 2013 +0200
18.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Thu May 02 09:18:22 2013 +0200
18.3 @@ -37,13 +37,7 @@
18.4 return id;
18.5 }
18.6
18.7 - public String getText() {
18.8 - return (String)getAttribute("innerHTML");
18.9 - }
18.10 -
18.11 - public void setText(String text) {
18.12 - setAttribute("innerHTML", text);
18.13 - }
18.14 + abstract void dontSubclass();
18.15
18.16 @JavaScriptBody(
18.17 args={"el", "property", "value"},
19.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java Tue Apr 02 15:40:51 2013 +0200
19.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java Thu May 02 09:18:22 2013 +0200
19.3 @@ -27,7 +27,7 @@
19.4
19.5 Object context;
19.6
19.7 - public GraphicsContext(Object contextImpl) {
19.8 + GraphicsContext(Object contextImpl) {
19.9 this.context = contextImpl;
19.10 }
19.11
19.12 @@ -113,15 +113,15 @@
19.13 @JavaScriptBody(args = {"x", "y"}, body = "this._context().scale(x,y);")
19.14 public native void scale(double x, double y);
19.15
19.16 - public void drawImage(Element image, double x, double y) {
19.17 + public void drawImage(Image image, double x, double y) {
19.18 drawImageImpl(context, Element.getElementById(image), x, y);
19.19 }
19.20
19.21 - public void drawImage(Element image, double x, double y, double width, double height) {
19.22 + public void drawImage(Image image, double x, double y, double width, double height) {
19.23 drawImageImpl(context, Element.getElementById(image), x, y, width, height);
19.24 }
19.25
19.26 - public void drawImage(Element image, double sx, double sy, double sWidth, double sHeight, double x, double y, double width, double height) {
19.27 + public void drawImage(Image image, double sx, double sy, double sWidth, double sHeight, double x, double y, double width, double height) {
19.28 drawImageImpl(context, Element.getElementById(image), sx, sy, sWidth, sHeight, x, y, width, height);
19.29 }
19.30
19.31 @@ -319,12 +319,12 @@
19.32 @JavaScriptBody(args = {"context", "x0", "y0", "x1", "y1"}, body = "return context.createLinearGradient(x0,y0,x1,y1);")
19.33 private native Object createLinearGradientImpl(Object context, double x0, double y0, double x1, double y1);
19.34
19.35 - public Pattern createPattern(Element image, String repeat) {
19.36 + public Pattern createPattern(Image image, String repeat) {
19.37 return new Pattern(createPatternImpl(context, image, repeat));
19.38 }
19.39
19.40 @JavaScriptBody(args = {"context", "image", "repeat"}, body = "return context.createPattern(image, repeat);")
19.41 - private static native Object createPatternImpl(Object context, Element image, String repeat);
19.42 + private static native Object createPatternImpl(Object context, Image image, String repeat);
19.43
19.44 public RadialGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1) {
19.45 return new RadialGradient(createRadialGradientImpl(context, x0, y0, r0, x1, y1, r1));
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Image.java Thu May 02 09:18:22 2013 +0200
20.3 @@ -0,0 +1,36 @@
20.4 +/**
20.5 + * Back 2 Browser Bytecode Translator
20.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
20.7 + *
20.8 + * This program is free software: you can redistribute it and/or modify
20.9 + * it under the terms of the GNU General Public License as published by
20.10 + * the Free Software Foundation, version 2 of the License.
20.11 + *
20.12 + * This program is distributed in the hope that it will be useful,
20.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
20.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20.15 + * GNU General Public License for more details.
20.16 + *
20.17 + * You should have received a copy of the GNU General Public License
20.18 + * along with this program. Look for COPYING file in the top folder.
20.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
20.20 + */
20.21 +package org.apidesign.bck2brwsr.htmlpage.api;
20.22 +
20.23 +/**
20.24 + *
20.25 + * @author Anton Epple <toni.epple@eppleton.de>
20.26 + */
20.27 +public class Image extends Element{
20.28 +
20.29 + public Image(String id) {
20.30 + super(id);
20.31 + }
20.32 +
20.33 +
20.34 +
20.35 + @Override
20.36 + void dontSubclass() {
20.37 + }
20.38 +
20.39 +}
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
21.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java Thu May 02 09:18:22 2013 +0200
21.3 @@ -0,0 +1,44 @@
21.4 +/**
21.5 + * Back 2 Browser Bytecode Translator
21.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
21.7 + *
21.8 + * This program is free software: you can redistribute it and/or modify
21.9 + * it under the terms of the GNU General Public License as published by
21.10 + * the Free Software Foundation, version 2 of the License.
21.11 + *
21.12 + * This program is distributed in the hope that it will be useful,
21.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
21.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21.15 + * GNU General Public License for more details.
21.16 + *
21.17 + * You should have received a copy of the GNU General Public License
21.18 + * along with this program. Look for COPYING file in the top folder.
21.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
21.20 + */
21.21 +package org.apidesign.bck2brwsr.htmlpage.api;
21.22 +
21.23 +/**
21.24 + *
21.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
21.26 + */
21.27 +public final class Input extends Element {
21.28 + public Input(String id) {
21.29 + super(id);
21.30 + }
21.31 +
21.32 + @Override
21.33 + void dontSubclass() {
21.34 + }
21.35 +
21.36 + public void setAutocomplete(boolean state) {
21.37 + setAttribute(this, "autocomplete", state);
21.38 + }
21.39 +
21.40 + public final String getValue() {
21.41 + return (String)getAttribute(this, "value");
21.42 + }
21.43 +
21.44 + public final void setValue(String txt) {
21.45 + setAttribute(this, "value", txt);
21.46 + }
21.47 +}
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
22.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Model.java Thu May 02 09:18:22 2013 +0200
22.3 @@ -0,0 +1,38 @@
22.4 +/**
22.5 + * Back 2 Browser Bytecode Translator
22.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
22.7 + *
22.8 + * This program is free software: you can redistribute it and/or modify
22.9 + * it under the terms of the GNU General Public License as published by
22.10 + * the Free Software Foundation, version 2 of the License.
22.11 + *
22.12 + * This program is distributed in the hope that it will be useful,
22.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
22.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22.15 + * GNU General Public License for more details.
22.16 + *
22.17 + * You should have received a copy of the GNU General Public License
22.18 + * along with this program. Look for COPYING file in the top folder.
22.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
22.20 + */
22.21 +package org.apidesign.bck2brwsr.htmlpage.api;
22.22 +
22.23 +import java.lang.annotation.ElementType;
22.24 +import java.lang.annotation.Retention;
22.25 +import java.lang.annotation.RetentionPolicy;
22.26 +import java.lang.annotation.Target;
22.27 +
22.28 +/**
22.29 + * @deprecated Replaced by new {@link net.java.html.json.Model net.java.html.json} API.
22.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
22.31 + */
22.32 +@Retention(RetentionPolicy.SOURCE)
22.33 +@Target(ElementType.TYPE)
22.34 +@Deprecated
22.35 +public @interface Model {
22.36 + /** Name of the model class */
22.37 + String className();
22.38 + /** List of properties in the model.
22.39 + */
22.40 + Property[] properties();
22.41 +}
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
23.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnFunction.java Thu May 02 09:18:22 2013 +0200
23.3 @@ -0,0 +1,33 @@
23.4 +/**
23.5 + * Back 2 Browser Bytecode Translator
23.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
23.7 + *
23.8 + * This program is free software: you can redistribute it and/or modify
23.9 + * it under the terms of the GNU General Public License as published by
23.10 + * the Free Software Foundation, version 2 of the License.
23.11 + *
23.12 + * This program is distributed in the hope that it will be useful,
23.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
23.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23.15 + * GNU General Public License for more details.
23.16 + *
23.17 + * You should have received a copy of the GNU General Public License
23.18 + * along with this program. Look for COPYING file in the top folder.
23.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
23.20 + */
23.21 +package org.apidesign.bck2brwsr.htmlpage.api;
23.22 +
23.23 +import java.lang.annotation.ElementType;
23.24 +import java.lang.annotation.Retention;
23.25 +import java.lang.annotation.RetentionPolicy;
23.26 +import java.lang.annotation.Target;
23.27 +
23.28 +/**
23.29 + * @deprecated Replaced by new {@link net.java.html.json.Function net.java.html.json} API.
23.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
23.31 + */
23.32 +@Target(ElementType.METHOD)
23.33 +@Retention(RetentionPolicy.SOURCE)
23.34 +@Deprecated
23.35 +public @interface OnFunction {
23.36 +}
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
24.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnPropertyChange.java Thu May 02 09:18:22 2013 +0200
24.3 @@ -0,0 +1,38 @@
24.4 +/**
24.5 + * Back 2 Browser Bytecode Translator
24.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
24.7 + *
24.8 + * This program is free software: you can redistribute it and/or modify
24.9 + * it under the terms of the GNU General Public License as published by
24.10 + * the Free Software Foundation, version 2 of the License.
24.11 + *
24.12 + * This program is distributed in the hope that it will be useful,
24.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
24.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24.15 + * GNU General Public License for more details.
24.16 + *
24.17 + * You should have received a copy of the GNU General Public License
24.18 + * along with this program. Look for COPYING file in the top folder.
24.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
24.20 + */
24.21 +package org.apidesign.bck2brwsr.htmlpage.api;
24.22 +
24.23 +import java.lang.annotation.ElementType;
24.24 +import java.lang.annotation.Retention;
24.25 +import java.lang.annotation.RetentionPolicy;
24.26 +import java.lang.annotation.Target;
24.27 +
24.28 +/**
24.29 + * @deprecated Replaced by new {@link net.java.html.json.OnPropertyChange net.java.html.json} API.
24.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
24.31 + */
24.32 +@Retention(RetentionPolicy.SOURCE)
24.33 +@Target(ElementType.METHOD)
24.34 +@Deprecated
24.35 +public @interface OnPropertyChange {
24.36 + /** Name(s) of the properties. One wishes to observe.
24.37 + *
24.38 + * @return valid java identifier
24.39 + */
24.40 + String[] value();
24.41 +}
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
25.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java Thu May 02 09:18:22 2013 +0200
25.3 @@ -0,0 +1,53 @@
25.4 +/**
25.5 + * Back 2 Browser Bytecode Translator
25.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
25.7 + *
25.8 + * This program is free software: you can redistribute it and/or modify
25.9 + * it under the terms of the GNU General Public License as published by
25.10 + * the Free Software Foundation, version 2 of the License.
25.11 + *
25.12 + * This program is distributed in the hope that it will be useful,
25.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
25.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25.15 + * GNU General Public License for more details.
25.16 + *
25.17 + * You should have received a copy of the GNU General Public License
25.18 + * along with this program. Look for COPYING file in the top folder.
25.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
25.20 + */
25.21 +package org.apidesign.bck2brwsr.htmlpage.api;
25.22 +
25.23 +import java.lang.annotation.ElementType;
25.24 +import java.lang.annotation.Retention;
25.25 +import java.lang.annotation.RetentionPolicy;
25.26 +import java.lang.annotation.Target;
25.27 +
25.28 +/**
25.29 + * @deprecated Replaced by new {@link net.java.html.json.OnReceive net.java.html.json} API.
25.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
25.31 + * @since 0.6
25.32 + */
25.33 +@Retention(RetentionPolicy.SOURCE)
25.34 +@Target(ElementType.METHOD)
25.35 +@Deprecated
25.36 +public @interface OnReceive {
25.37 + /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
25.38 + * Those parameters will then become variables of the associated method.
25.39 + *
25.40 + * @return the (possibly parametrized) url to connect to
25.41 + */
25.42 + String url();
25.43 +
25.44 + /** Support for <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> requires
25.45 + * a callback from the server generated page to a function defined in the
25.46 + * system. The name of such function is usually specified as a property
25.47 + * (of possibly different names). By defining the <code>jsonp</code> attribute
25.48 + * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
25.49 + * transmission and specifies the name of the property. The property should
25.50 + * also be used in the {@link #url()} attribute on appropriate place.
25.51 + *
25.52 + * @return name of a property to carry the name of <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
25.53 + * callback function.
25.54 + */
25.55 + String jsonp() default "";
25.56 +}
26.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java Tue Apr 02 15:40:51 2013 +0200
26.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java Thu May 02 09:18:22 2013 +0200
26.3 @@ -20,15 +20,36 @@
26.4 import java.lang.annotation.Retention;
26.5 import java.lang.annotation.RetentionPolicy;
26.6 import java.lang.annotation.Target;
26.7 +import java.util.List;
26.8
26.9 -/** Represents a property in a generated model of an HTML
26.10 - * {@link Page}.
26.11 - *
26.12 +/**
26.13 + * @deprecated Replaced by new {@link net.java.html.json.Property net.java.html.json} API.
26.14 * @author Jaroslav Tulach <jtulach@netbeans.org>
26.15 */
26.16 @Retention(RetentionPolicy.SOURCE)
26.17 @Target({})
26.18 +@Deprecated
26.19 public @interface Property {
26.20 + /** Name of the property. Will be used to define proper getter and setter
26.21 + * in the associated class.
26.22 + *
26.23 + * @return valid java identifier
26.24 + */
26.25 String name();
26.26 +
26.27 + /** Type of the property. Can either be primitive type (like <code>int.class</code>,
26.28 + * <code>double.class</code>, etc.), {@link String} or complex model
26.29 + * class (defined by {@link Model} property).
26.30 + *
26.31 + * @return the class of the property
26.32 + */
26.33 Class<?> type();
26.34 +
26.35 + /** Is this property an array of the {@link #type()} or a single value?
26.36 + * If the property is an array, only its getter (returning mutable {@link List} of
26.37 + * the boxed {@link #type()}).
26.38 + *
26.39 + * @return true, if this is supposed to be an array of values.
26.40 + */
26.41 + boolean array() default false;
26.42 }
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
27.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java Thu May 02 09:18:22 2013 +0200
27.3 @@ -0,0 +1,36 @@
27.4 +/**
27.5 + * Back 2 Browser Bytecode Translator
27.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
27.7 + *
27.8 + * This program is free software: you can redistribute it and/or modify
27.9 + * it under the terms of the GNU General Public License as published by
27.10 + * the Free Software Foundation, version 2 of the License.
27.11 + *
27.12 + * This program is distributed in the hope that it will be useful,
27.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
27.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27.15 + * GNU General Public License for more details.
27.16 + *
27.17 + * You should have received a copy of the GNU General Public License
27.18 + * along with this program. Look for COPYING file in the top folder.
27.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
27.20 + */
27.21 +package org.apidesign.bck2brwsr.htmlpage.api;
27.22 +
27.23 +/**
27.24 + *
27.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
27.26 + */
27.27 +public class Title extends Element {
27.28 + public Title(String id) {
27.29 + super(id);
27.30 + }
27.31 +
27.32 + @Override
27.33 + void dontSubclass() {
27.34 + }
27.35 +
27.36 + public final void setText(String text) {
27.37 + setAttribute(this, "innerHTML", text);
27.38 + }
27.39 +}
28.1 --- a/javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Tue Apr 02 15:40:51 2013 +0200
28.2 +++ b/javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Thu May 02 09:18:22 2013 +0200
28.3 @@ -2193,7 +2193,14 @@
28.4 else
28.5 element[attrName] = attrValue;
28.6 } else if (!toRemove) {
28.7 - element.setAttribute(attrName, attrValue.toString());
28.8 + try {
28.9 + element.setAttribute(attrName, attrValue.toString());
28.10 + } catch (err) {
28.11 + // ignore for now
28.12 + if (console) {
28.13 + console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
28.14 + }
28.15 + }
28.16 }
28.17
28.18 // Treat "name" specially - although you can think of it as an attribute, it also needs
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
29.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java Thu May 02 09:18:22 2013 +0200
29.3 @@ -0,0 +1,216 @@
29.4 +/**
29.5 + * Back 2 Browser Bytecode Translator
29.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
29.7 + *
29.8 + * This program is free software: you can redistribute it and/or modify
29.9 + * it under the terms of the GNU General Public License as published by
29.10 + * the Free Software Foundation, version 2 of the License.
29.11 + *
29.12 + * This program is distributed in the hope that it will be useful,
29.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
29.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29.15 + * GNU General Public License for more details.
29.16 + *
29.17 + * You should have received a copy of the GNU General Public License
29.18 + * along with this program. Look for COPYING file in the top folder.
29.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
29.20 + */
29.21 +package org.apidesign.bck2brwsr.htmlpage;
29.22 +
29.23 +import java.io.ByteArrayInputStream;
29.24 +import java.io.ByteArrayOutputStream;
29.25 +import java.io.IOException;
29.26 +import java.io.InputStream;
29.27 +import java.io.OutputStream;
29.28 +import java.net.URI;
29.29 +import java.net.URISyntaxException;
29.30 +import java.util.ArrayList;
29.31 +import java.util.Arrays;
29.32 +import java.util.HashMap;
29.33 +import java.util.List;
29.34 +import java.util.Map;
29.35 +import java.util.regex.Matcher;
29.36 +import java.util.regex.Pattern;
29.37 +import javax.tools.Diagnostic;
29.38 +import javax.tools.DiagnosticListener;
29.39 +import javax.tools.FileObject;
29.40 +import javax.tools.ForwardingJavaFileManager;
29.41 +import javax.tools.JavaFileManager;
29.42 +import javax.tools.JavaFileObject;
29.43 +import javax.tools.JavaFileObject.Kind;
29.44 +import javax.tools.SimpleJavaFileObject;
29.45 +import javax.tools.StandardJavaFileManager;
29.46 +import javax.tools.StandardLocation;
29.47 +import javax.tools.ToolProvider;
29.48 +
29.49 +/**
29.50 + *
29.51 + * @author Jaroslav Tulach <jtulach@netbeans.org>
29.52 + */
29.53 +final class Compile implements DiagnosticListener<JavaFileObject> {
29.54 + private final List<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<>();
29.55 + private final Map<String, byte[]> classes;
29.56 + private final String pkg;
29.57 + private final String cls;
29.58 + private final String html;
29.59 +
29.60 + private Compile(String html, String code) throws IOException {
29.61 + this.pkg = findPkg(code);
29.62 + this.cls = findCls(code);
29.63 + this.html = html;
29.64 + classes = compile(html, code);
29.65 + }
29.66 +
29.67 + /** Performs compilation of given HTML page and associated Java code
29.68 + */
29.69 + public static Compile create(String html, String code) throws IOException {
29.70 + return new Compile(html, code);
29.71 + }
29.72 +
29.73 + /** Checks for given class among compiled resources */
29.74 + public byte[] get(String res) {
29.75 + return classes.get(res);
29.76 + }
29.77 +
29.78 + /** Obtains errors created during compilation.
29.79 + */
29.80 + public List<Diagnostic<? extends JavaFileObject>> getErrors() {
29.81 + List<Diagnostic<? extends JavaFileObject>> err = new ArrayList<>();
29.82 + for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
29.83 + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
29.84 + err.add(diagnostic);
29.85 + }
29.86 + }
29.87 + return err;
29.88 + }
29.89 +
29.90 + private Map<String, byte[]> compile(final String html, final String code) throws IOException {
29.91 + StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
29.92 +
29.93 + final Map<String, ByteArrayOutputStream> class2BAOS = new HashMap<>();
29.94 +
29.95 + JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
29.96 + @Override
29.97 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
29.98 + return code;
29.99 + }
29.100 + };
29.101 + final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
29.102 + @Override
29.103 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
29.104 + return html;
29.105 + }
29.106 +
29.107 + @Override
29.108 + public InputStream openInputStream() throws IOException {
29.109 + return new ByteArrayInputStream(html.getBytes());
29.110 + }
29.111 + };
29.112 +
29.113 + final URI scratch;
29.114 + try {
29.115 + scratch = new URI("mem://mem3");
29.116 + } catch (URISyntaxException ex) {
29.117 + throw new IOException(ex);
29.118 + }
29.119 +
29.120 + JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
29.121 + @Override
29.122 + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
29.123 + if (kind == Kind.CLASS) {
29.124 + final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
29.125 +
29.126 + class2BAOS.put(className.replace('.', '/') + ".class", buffer);
29.127 + return new SimpleJavaFileObject(sibling.toUri(), kind) {
29.128 + @Override
29.129 + public OutputStream openOutputStream() throws IOException {
29.130 + return buffer;
29.131 + }
29.132 + };
29.133 + }
29.134 +
29.135 + if (kind == Kind.SOURCE) {
29.136 + return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) {
29.137 + private final ByteArrayOutputStream data = new ByteArrayOutputStream();
29.138 + @Override
29.139 + public OutputStream openOutputStream() throws IOException {
29.140 + return data;
29.141 + }
29.142 +
29.143 + @Override
29.144 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
29.145 + data.close();
29.146 + return new String(data.toByteArray());
29.147 + }
29.148 + };
29.149 + }
29.150 +
29.151 + throw new IllegalStateException();
29.152 + }
29.153 +
29.154 + @Override
29.155 + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
29.156 + if (location == StandardLocation.SOURCE_PATH) {
29.157 + if (packageName.equals(pkg)) {
29.158 + return htmlFile;
29.159 + }
29.160 + }
29.161 +
29.162 + return null;
29.163 + }
29.164 +
29.165 + @Override
29.166 + public boolean isSameFile(FileObject a, FileObject b) {
29.167 + if (a == null || b == null) {
29.168 + throw new NullPointerException();
29.169 + }
29.170 + if (!(a instanceof SimpleJavaFileObject)) {
29.171 + throw new IllegalArgumentException("Not supported: " + a);
29.172 + }
29.173 + if (!(b instanceof SimpleJavaFileObject)) {
29.174 + throw new IllegalArgumentException("Not supported: " + b);
29.175 + }
29.176 + return a.equals(b);
29.177 + }
29.178 + };
29.179 +
29.180 + ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call();
29.181 +
29.182 + Map<String, byte[]> result = new HashMap<>();
29.183 +
29.184 + for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
29.185 + result.put(e.getKey(), e.getValue().toByteArray());
29.186 + }
29.187 +
29.188 + return result;
29.189 + }
29.190 +
29.191 +
29.192 + @Override
29.193 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
29.194 + errors.add(diagnostic);
29.195 + }
29.196 + private static String findPkg(String java) throws IOException {
29.197 + Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
29.198 + Matcher m = p.matcher(java);
29.199 + if (!m.find()) {
29.200 + throw new IOException("Can't find package declaration in the java file");
29.201 + }
29.202 + String pkg = m.group(1);
29.203 + return pkg;
29.204 + }
29.205 + private static String findCls(String java) throws IOException {
29.206 + Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
29.207 + Matcher m = p.matcher(java);
29.208 + if (!m.find()) {
29.209 + throw new IOException("Can't find package declaration in the java file");
29.210 + }
29.211 + String cls = m.group(1);
29.212 + return cls;
29.213 + }
29.214 +
29.215 + String getHtml() {
29.216 + String fqn = "'" + pkg + '.' + cls + "'";
29.217 + return html.replace("'${fqn}'", fqn);
29.218 + }
29.219 +}
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
30.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java Thu May 02 09:18:22 2013 +0200
30.3 @@ -0,0 +1,63 @@
30.4 +/**
30.5 + * Back 2 Browser Bytecode Translator
30.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
30.7 + *
30.8 + * This program is free software: you can redistribute it and/or modify
30.9 + * it under the terms of the GNU General Public License as published by
30.10 + * the Free Software Foundation, version 2 of the License.
30.11 + *
30.12 + * This program is distributed in the hope that it will be useful,
30.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
30.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30.15 + * GNU General Public License for more details.
30.16 + *
30.17 + * You should have received a copy of the GNU General Public License
30.18 + * along with this program. Look for COPYING file in the top folder.
30.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
30.20 + */
30.21 +package org.apidesign.bck2brwsr.htmlpage;
30.22 +
30.23 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
30.24 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
30.25 +import org.apidesign.bck2brwsr.vmtest.VMTest;
30.26 +import org.testng.annotations.Factory;
30.27 +
30.28 +/**
30.29 + *
30.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
30.31 + */
30.32 +public class ConvertTypesTest {
30.33 + @JavaScriptBody(args = { "includeSex" }, body = "var json = new Object();"
30.34 + + "json.firstName = 'son';\n"
30.35 + + "json.lastName = 'dj';\n"
30.36 + + "if (includeSex) json.sex = 'MALE';\n"
30.37 + + "return json;"
30.38 + )
30.39 + private static native Object createJSON(boolean includeSex);
30.40 +
30.41 + @BrwsrTest
30.42 + public void testConvertToPeople() throws Exception {
30.43 + final Object o = createJSON(true);
30.44 +
30.45 + Person p = new Person(o);
30.46 +
30.47 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
30.48 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
30.49 + assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
30.50 + }
30.51 +
30.52 + @BrwsrTest
30.53 + public void testConvertToPeopleWithoutSex() throws Exception {
30.54 + final Object o = createJSON(false);
30.55 +
30.56 + Person p = new Person(o);
30.57 +
30.58 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
30.59 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
30.60 + assert p.getSex() == null : "No sex: " + p.getSex();
30.61 + }
30.62 +
30.63 + @Factory public static Object[] create() {
30.64 + return VMTest.create(ConvertTypesTest.class);
30.65 + }
30.66 +}
30.67 \ No newline at end of file
31.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ElementGeneratorTest.java Tue Apr 02 15:40:51 2013 +0200
31.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
31.3 @@ -1,35 +0,0 @@
31.4 -/**
31.5 - * Back 2 Browser Bytecode Translator
31.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
31.7 - *
31.8 - * This program is free software: you can redistribute it and/or modify
31.9 - * it under the terms of the GNU General Public License as published by
31.10 - * the Free Software Foundation, version 2 of the License.
31.11 - *
31.12 - * This program is distributed in the hope that it will be useful,
31.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
31.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31.15 - * GNU General Public License for more details.
31.16 - *
31.17 - * You should have received a copy of the GNU General Public License
31.18 - * along with this program. Look for COPYING file in the top folder.
31.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
31.20 - */
31.21 -package org.apidesign.bck2brwsr.htmlpage;
31.22 -
31.23 -import java.util.Map;
31.24 -import static org.testng.Assert.*;
31.25 -import org.testng.annotations.Test;
31.26 -
31.27 -/**
31.28 - *
31.29 - * @author Jan Horvath <jhorvath@netbeans.org>
31.30 - */
31.31 -public class ElementGeneratorTest {
31.32 -
31.33 - @Test public void testGetAttributes() {
31.34 - ElementGenerator gen = new ElementGenerator(null);
31.35 - Map<String, String> attrs = gen.getAttributes("input");
31.36 - assertEquals(attrs.get("width"), "Integer", "Expected type of width attribute is Integer");
31.37 - }
31.38 -}
32.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
32.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java Thu May 02 09:18:22 2013 +0200
32.3 @@ -0,0 +1,392 @@
32.4 +/**
32.5 + * Back 2 Browser Bytecode Translator
32.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
32.7 + *
32.8 + * This program is free software: you can redistribute it and/or modify
32.9 + * it under the terms of the GNU General Public License as published by
32.10 + * the Free Software Foundation, version 2 of the License.
32.11 + *
32.12 + * This program is distributed in the hope that it will be useful,
32.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
32.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32.15 + * GNU General Public License for more details.
32.16 + *
32.17 + * You should have received a copy of the GNU General Public License
32.18 + * along with this program. Look for COPYING file in the top folder.
32.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
32.20 + */
32.21 +package org.apidesign.bck2brwsr.htmlpage;
32.22 +
32.23 +import java.util.Arrays;
32.24 +import java.util.Iterator;
32.25 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
32.26 +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
32.27 +import org.apidesign.bck2brwsr.htmlpage.api.Page;
32.28 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
32.29 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
32.30 +import org.apidesign.bck2brwsr.vmtest.Http;
32.31 +import org.apidesign.bck2brwsr.vmtest.VMTest;
32.32 +import org.json.JSONException;
32.33 +import org.json.JSONObject;
32.34 +import org.json.JSONTokener;
32.35 +import org.testng.annotations.Test;
32.36 +import static org.testng.Assert.*;
32.37 +import org.testng.annotations.Factory;
32.38 +
32.39 +/** Need to verify that models produce reasonable JSON objects.
32.40 + *
32.41 + * @author Jaroslav Tulach <jtulach@netbeans.org>
32.42 + */
32.43 +@Page(xhtml = "Empty.html", className = "JSONik", properties = {
32.44 + @Property(name = "fetched", type = PersonImpl.class),
32.45 + @Property(name = "fetchedCount", type = int.class),
32.46 + @Property(name = "fetchedSex", type = Sex.class, array = true)
32.47 +})
32.48 +public class JSONTest {
32.49 + private JSONik js;
32.50 + private Integer orig;
32.51 +
32.52 + @Test public void personToString() throws JSONException {
32.53 + Person p = new Person();
32.54 + p.setSex(Sex.MALE);
32.55 + p.setFirstName("Jarda");
32.56 + p.setLastName("Tulach");
32.57 +
32.58 + JSONTokener t = new JSONTokener(p.toString());
32.59 + JSONObject o;
32.60 + try {
32.61 + o = new JSONObject(t);
32.62 + } catch (JSONException ex) {
32.63 + throw new AssertionError("Can't parse " + p.toString(), ex);
32.64 + }
32.65 +
32.66 + Iterator it = o.sortedKeys();
32.67 + assertEquals(it.next(), "firstName");
32.68 + assertEquals(it.next(), "lastName");
32.69 + assertEquals(it.next(), "sex");
32.70 +
32.71 + assertEquals(o.getString("firstName"), "Jarda");
32.72 + assertEquals(o.getString("lastName"), "Tulach");
32.73 + assertEquals(o.getString("sex"), "MALE");
32.74 + }
32.75 +
32.76 + @BrwsrTest public void toJSONInABrowser() throws Throwable {
32.77 + Person p = new Person();
32.78 + p.setSex(Sex.MALE);
32.79 + p.setFirstName("Jarda");
32.80 + p.setLastName("Tulach");
32.81 +
32.82 + Object json;
32.83 + try {
32.84 + json = parseJSON(p.toString());
32.85 + } catch (Throwable ex) {
32.86 + throw new IllegalStateException("Can't parse " + p).initCause(ex);
32.87 + }
32.88 +
32.89 + Person p2 = new Person(json);
32.90 +
32.91 + assert p2.getFirstName().equals(p.getFirstName()) :
32.92 + "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName();
32.93 + }
32.94 +
32.95 + @Test public void personWithWildCharactersAndNulls() throws JSONException {
32.96 + Person p = new Person();
32.97 + p.setFirstName("'\"\n");
32.98 + p.setLastName("\t\r\u0002");
32.99 +
32.100 + JSONTokener t = new JSONTokener(p.toString());
32.101 + JSONObject o;
32.102 + try {
32.103 + o = new JSONObject(t);
32.104 + } catch (JSONException ex) {
32.105 + throw new AssertionError("Can't parse " + p.toString(), ex);
32.106 + }
32.107 +
32.108 + Iterator it = o.sortedKeys();
32.109 + assertEquals(it.next(), "firstName");
32.110 + assertEquals(it.next(), "lastName");
32.111 + assertEquals(it.next(), "sex");
32.112 +
32.113 + assertEquals(o.getString("firstName"), p.getFirstName());
32.114 + assertEquals(o.getString("lastName"), p.getLastName());
32.115 + assertEquals(o.get("sex"), JSONObject.NULL);
32.116 + }
32.117 +
32.118 + @Test public void personsInArray() throws JSONException {
32.119 + Person p1 = new Person();
32.120 + p1.setFirstName("One");
32.121 +
32.122 + Person p2 = new Person();
32.123 + p2.setFirstName("Two");
32.124 +
32.125 + People arr = new People();
32.126 + arr.getInfo().add(p1);
32.127 + arr.getInfo().add(p2);
32.128 + arr.getNicknames().add("Prvn\u00ed k\u016f\u0148");
32.129 + final String n2 = "Druh\u00fd hlem\u00fd\u017e\u010f, star\u0161\u00ed";
32.130 + arr.getNicknames().add(n2);
32.131 + arr.getAge().add(33);
32.132 + arr.getAge().add(73);
32.133 +
32.134 +
32.135 + final String json = arr.toString();
32.136 +
32.137 + JSONTokener t = new JSONTokener(json);
32.138 + JSONObject o;
32.139 + try {
32.140 + o = new JSONObject(t);
32.141 + } catch (JSONException ex) {
32.142 + throw new AssertionError("Can't parse " + json, ex);
32.143 + }
32.144 +
32.145 + assertEquals(o.getJSONArray("info").getJSONObject(0).getString("firstName"), "One");
32.146 + assertEquals(o.getJSONArray("nicknames").getString(1), n2);
32.147 + assertEquals(o.getJSONArray("age").getInt(1), 73);
32.148 + }
32.149 +
32.150 +
32.151 + @OnReceive(url="/{url}")
32.152 + static void fetch(Person p, JSONik model) {
32.153 + model.setFetched(p);
32.154 + }
32.155 +
32.156 + @OnReceive(url="/{url}")
32.157 + static void fetchArray(Person[] p, JSONik model) {
32.158 + model.setFetchedCount(p.length);
32.159 + model.setFetched(p[0]);
32.160 + }
32.161 +
32.162 + @OnReceive(url="/{url}")
32.163 + static void fetchPeople(People p, JSONik model) {
32.164 + model.setFetchedCount(p.getInfo().size());
32.165 + model.setFetched(p.getInfo().get(0));
32.166 + }
32.167 +
32.168 + @OnReceive(url="/{url}")
32.169 + static void fetchPeopleAge(People p, JSONik model) {
32.170 + int sum = 0;
32.171 + for (int a : p.getAge()) {
32.172 + sum += a;
32.173 + }
32.174 + model.setFetchedCount(sum);
32.175 + }
32.176 +
32.177 + @Http(@Http.Resource(
32.178 + content = "{'firstName': 'Sitar', 'sex': 'MALE'}",
32.179 + path="/person.json",
32.180 + mimeType = "application/json"
32.181 + ))
32.182 + @BrwsrTest public void loadAndParseJSON() throws InterruptedException {
32.183 + if (js == null) {
32.184 + js = new JSONik();
32.185 + js.applyBindings();
32.186 +
32.187 + js.fetch("person.json");
32.188 + }
32.189 +
32.190 + Person p = js.getFetched();
32.191 + if (p == null) {
32.192 + throw new InterruptedException();
32.193 + }
32.194 +
32.195 + assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
32.196 + assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
32.197 + }
32.198 +
32.199 + @OnReceive(url="/{url}?callme={me}", jsonp = "me")
32.200 + static void fetchViaJSONP(Person p, JSONik model) {
32.201 + model.setFetched(p);
32.202 + }
32.203 +
32.204 + @Http(@Http.Resource(
32.205 + content = "$0({'firstName': 'Mitar', 'sex': 'MALE'})",
32.206 + path="/person.json",
32.207 + mimeType = "application/javascript",
32.208 + parameters = { "callme" }
32.209 + ))
32.210 + @BrwsrTest public void loadAndParseJSONP() throws InterruptedException {
32.211 +
32.212 + if (js == null) {
32.213 + orig = scriptElements();
32.214 + assert orig > 0 : "There should be some scripts on the page";
32.215 +
32.216 + js = new JSONik();
32.217 + js.applyBindings();
32.218 +
32.219 + js.fetchViaJSONP("person.json");
32.220 + }
32.221 +
32.222 + Person p = js.getFetched();
32.223 + if (p == null) {
32.224 + throw new InterruptedException();
32.225 + }
32.226 +
32.227 + assert "Mitar".equals(p.getFirstName()) : "Unexpected: " + p.getFirstName();
32.228 + assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
32.229 +
32.230 + int now = scriptElements();
32.231 +
32.232 + assert orig == now : "The set of elements is unchanged. Delta: " + (now - orig);
32.233 + }
32.234 +
32.235 + @JavaScriptBody(args = { }, body = "return window.document.getElementsByTagName('script').length;")
32.236 + private static native int scriptElements();
32.237 +
32.238 + @JavaScriptBody(args = { "s" }, body = "return window.JSON.parse(s);")
32.239 + private static native Object parseJSON(String s);
32.240 +
32.241 + @Http(@Http.Resource(
32.242 + content = "{'firstName': 'Sitar', 'sex': 'MALE'}",
32.243 + path="/person.json",
32.244 + mimeType = "application/json"
32.245 + ))
32.246 + @BrwsrTest public void loadAndParseJSONSentToArray() throws InterruptedException {
32.247 + if (js == null) {
32.248 + js = new JSONik();
32.249 + js.applyBindings();
32.250 +
32.251 + js.fetchArray("person.json");
32.252 + }
32.253 +
32.254 + Person p = js.getFetched();
32.255 + if (p == null) {
32.256 + throw new InterruptedException();
32.257 + }
32.258 +
32.259 + assert p != null : "We should get our person back: " + p;
32.260 + assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
32.261 + assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
32.262 + }
32.263 +
32.264 + @Http(@Http.Resource(
32.265 + content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]",
32.266 + path="/person.json",
32.267 + mimeType = "application/json"
32.268 + ))
32.269 + @BrwsrTest public void loadAndParseJSONArraySingle() throws InterruptedException {
32.270 + if (js == null) {
32.271 + js = new JSONik();
32.272 + js.applyBindings();
32.273 +
32.274 + js.fetch("person.json");
32.275 + }
32.276 +
32.277 + Person p = js.getFetched();
32.278 + if (p == null) {
32.279 + throw new InterruptedException();
32.280 + }
32.281 +
32.282 + assert p != null : "We should get our person back: " + p;
32.283 + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
32.284 + assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
32.285 + }
32.286 +
32.287 + @Http(@Http.Resource(
32.288 + content = "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
32.289 + path="/people.json",
32.290 + mimeType = "application/json"
32.291 + ))
32.292 + @BrwsrTest public void loadAndParseArrayInPeople() throws InterruptedException {
32.293 + if (js == null) {
32.294 + js = new JSONik();
32.295 + js.applyBindings();
32.296 +
32.297 + js.fetchPeople("people.json");
32.298 + }
32.299 +
32.300 + if (0 == js.getFetchedCount()) {
32.301 + throw new InterruptedException();
32.302 + }
32.303 +
32.304 + assert js.getFetchedCount() == 1 : "One person loaded: " + js.getFetchedCount();
32.305 +
32.306 + Person p = js.getFetched();
32.307 +
32.308 + assert p != null : "We should get our person back: " + p;
32.309 + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
32.310 + assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
32.311 + }
32.312 +
32.313 + @Http(@Http.Resource(
32.314 + content = "{'age':[1, 2, 3]}",
32.315 + path="/people.json",
32.316 + mimeType = "application/json"
32.317 + ))
32.318 + @BrwsrTest public void loadAndParseArrayOfIntegers() throws InterruptedException {
32.319 + if (js == null) {
32.320 + js = new JSONik();
32.321 + js.applyBindings();
32.322 +
32.323 + js.fetchPeopleAge("people.json");
32.324 + }
32.325 +
32.326 + if (0 == js.getFetchedCount()) {
32.327 + throw new InterruptedException();
32.328 + }
32.329 +
32.330 + assert js.getFetchedCount() == 6 : "1 + 2 + 3 is " + js.getFetchedCount();
32.331 + }
32.332 +
32.333 + @OnReceive(url="/{url}")
32.334 + static void fetchPeopleSex(People p, JSONik model) {
32.335 + model.setFetchedCount(1);
32.336 + model.getFetchedSex().addAll(p.getSex());
32.337 + }
32.338 +
32.339 +
32.340 + @Http(@Http.Resource(
32.341 + content = "{'sex':['FEMALE', 'MALE', 'MALE']}",
32.342 + path="/people.json",
32.343 + mimeType = "application/json"
32.344 + ))
32.345 + @BrwsrTest public void loadAndParseArrayOfEnums() throws InterruptedException {
32.346 + if (js == null) {
32.347 + js = new JSONik();
32.348 + js.applyBindings();
32.349 +
32.350 + js.fetchPeopleSex("people.json");
32.351 + }
32.352 +
32.353 + if (0 == js.getFetchedCount()) {
32.354 + throw new InterruptedException();
32.355 + }
32.356 +
32.357 + assert js.getFetchedCount() == 1 : "Loaded";
32.358 +
32.359 + assert js.getFetchedSex().size() == 3 : "Three values " + js.getFetchedSex();
32.360 + assert js.getFetchedSex().get(0) == Sex.FEMALE : "Female first " + js.getFetchedSex();
32.361 + assert js.getFetchedSex().get(1) == Sex.MALE : "male 2nd " + js.getFetchedSex();
32.362 + assert js.getFetchedSex().get(2) == Sex.MALE : "male 3rd " + js.getFetchedSex();
32.363 + }
32.364 +
32.365 + @Http(@Http.Resource(
32.366 + content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'},"
32.367 + + "{'firstName': 'Peter', 'sex': 'MALE'}"
32.368 + + "]",
32.369 + path="/person.json",
32.370 + mimeType = "application/json"
32.371 + ))
32.372 + @BrwsrTest public void loadAndParseJSONArray() throws InterruptedException {
32.373 + if (js == null) {
32.374 + js = new JSONik();
32.375 + js.applyBindings();
32.376 + js.fetchArray("person.json");
32.377 + }
32.378 +
32.379 +
32.380 + Person p = js.getFetched();
32.381 + if (p == null) {
32.382 + throw new InterruptedException();
32.383 + }
32.384 +
32.385 + assert js.getFetchedCount() == 2 : "We got two values: " + js.getFetchedCount();
32.386 + assert p != null : "We should get our person back: " + p;
32.387 + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
32.388 + assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
32.389 + }
32.390 +
32.391 + @Factory public static Object[] create() {
32.392 + return VMTest.create(JSONTest.class);
32.393 + }
32.394 +
32.395 +}
33.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Tue Apr 02 15:40:51 2013 +0200
33.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Thu May 02 09:18:22 2013 +0200
33.3 @@ -17,21 +17,29 @@
33.4 */
33.5 package org.apidesign.bck2brwsr.htmlpage;
33.6
33.7 +import java.util.List;
33.8 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
33.9 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
33.10 import org.apidesign.bck2brwsr.htmlpage.api.OnEvent;
33.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
33.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
33.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
33.14 import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
33.15 import org.apidesign.bck2brwsr.vmtest.HtmlFragment;
33.16 import org.apidesign.bck2brwsr.vmtest.VMTest;
33.17 +import static org.testng.Assert.assertEquals;
33.18 import org.testng.annotations.Factory;
33.19 +import org.testng.annotations.Test;
33.20
33.21 /**
33.22 *
33.23 * @author Jaroslav Tulach <jtulach@netbeans.org>
33.24 */
33.25 @Page(xhtml="Knockout.xhtml", className="KnockoutModel", properties={
33.26 - @Property(name="name", type=String.class)
33.27 + @Property(name="name", type=String.class),
33.28 + @Property(name="results", type=String.class, array = true),
33.29 + @Property(name="callbackCount", type=int.class),
33.30 + @Property(name="people", type=PersonImpl.class, array = true)
33.31 })
33.32 public class KnockoutTest {
33.33
33.34 @@ -44,19 +52,207 @@
33.35 KnockoutModel m = new KnockoutModel();
33.36 m.setName("Kukuc");
33.37 m.applyBindings();
33.38 - assert "Kukuc".equals(m.INPUT.getValue()) : "Value is really kukuc: " + m.INPUT.getValue();
33.39 - m.INPUT.setValue("Jardo");
33.40 - m.triggerEvent(m.INPUT, OnEvent.CHANGE);
33.41 + assert "Kukuc".equals(m.input.getValue()) : "Value is really kukuc: " + m.input.getValue();
33.42 + m.input.setValue("Jardo");
33.43 + m.triggerEvent(m.input, OnEvent.CHANGE);
33.44 assert "Jardo".equals(m.getName()) : "Name property updated: " + m.getName();
33.45 }
33.46
33.47 + @HtmlFragment(
33.48 + "<ul id='ul' data-bind='foreach: results'>\n"
33.49 + + " <li data-bind='text: $data, click: $root.call'/>\n"
33.50 + + "</ul>\n"
33.51 + )
33.52 + @BrwsrTest public void displayContentOfArray() {
33.53 + KnockoutModel m = new KnockoutModel();
33.54 + m.getResults().add("Ahoj");
33.55 + m.applyBindings();
33.56 +
33.57 + int cnt = countChildren("ul");
33.58 + assert cnt == 1 : "One child, but was " + cnt;
33.59 +
33.60 + m.getResults().add("Hi");
33.61 +
33.62 + cnt = countChildren("ul");
33.63 + assert cnt == 2 : "Two children now, but was " + cnt;
33.64 +
33.65 + triggerChildClick("ul", 1);
33.66 +
33.67 + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
33.68 + assert "Hi".equals(m.getName()) : "We got callback from 2nd child " + m.getName();
33.69 + }
33.70 +
33.71 + @HtmlFragment(
33.72 + "<ul id='ul' data-bind='foreach: cmpResults'>\n"
33.73 + + " <li><b data-bind='text: $data'></b></li>\n"
33.74 + + "</ul>\n"
33.75 + )
33.76 + @BrwsrTest public void displayContentOfDerivedArray() {
33.77 + KnockoutModel m = new KnockoutModel();
33.78 + m.getResults().add("Ahoj");
33.79 + m.applyBindings();
33.80 +
33.81 + int cnt = countChildren("ul");
33.82 + assert cnt == 1 : "One child, but was " + cnt;
33.83 +
33.84 + m.getResults().add("hello");
33.85 +
33.86 + cnt = countChildren("ul");
33.87 + assert cnt == 2 : "Two children now, but was " + cnt;
33.88 + }
33.89 +
33.90 + @HtmlFragment(
33.91 + "<ul id='ul' data-bind='foreach: people'>\n"
33.92 + + " <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
33.93 + + "</ul>\n"
33.94 + )
33.95 + @BrwsrTest public void displayContentOfArrayOfPeople() {
33.96 + KnockoutModel m = new KnockoutModel();
33.97 +
33.98 + final Person first = new Person();
33.99 + first.setFirstName("first");
33.100 + m.getPeople().add(first);
33.101 +
33.102 + m.applyBindings();
33.103 +
33.104 + int cnt = countChildren("ul");
33.105 + assert cnt == 1 : "One child, but was " + cnt;
33.106 +
33.107 + final Person second = new Person();
33.108 + second.setFirstName("second");
33.109 + m.getPeople().add(second);
33.110 +
33.111 + cnt = countChildren("ul");
33.112 + assert cnt == 2 : "Two children now, but was " + cnt;
33.113 +
33.114 + triggerChildClick("ul", 1);
33.115 +
33.116 + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
33.117 +
33.118 + cnt = countChildren("ul");
33.119 + assert cnt == 1 : "Again one child, but was " + cnt;
33.120 +
33.121 + String txt = childText("ul", 0);
33.122 + assert "first".equals(txt) : "Expecting 'first': " + txt;
33.123 +
33.124 + first.setFirstName("changed");
33.125 +
33.126 + txt = childText("ul", 0);
33.127 + assert "changed".equals(txt) : "Expecting 'changed': " + txt;
33.128 + }
33.129 +
33.130 + @ComputedProperty
33.131 + static Person firstPerson(List<Person> people) {
33.132 + return people.isEmpty() ? null : people.get(0);
33.133 + }
33.134 +
33.135 + @HtmlFragment(
33.136 + "<p id='ul' data-bind='with: firstPerson'>\n"
33.137 + + " <span data-bind='text: firstName, click: changeSex'></span>\n"
33.138 + + "</p>\n"
33.139 + )
33.140 + @BrwsrTest public void accessFirstPersonWithOnFunction() {
33.141 + trasfertToFemale();
33.142 + }
33.143 +
33.144 + @HtmlFragment(
33.145 + "<ul id='ul' data-bind='foreach: people'>\n"
33.146 + + " <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
33.147 + + "</ul>\n"
33.148 + )
33.149 + @BrwsrTest public void onPersonFunction() {
33.150 + trasfertToFemale();
33.151 + }
33.152 +
33.153 + private void trasfertToFemale() {
33.154 + KnockoutModel m = new KnockoutModel();
33.155 +
33.156 + final Person first = new Person();
33.157 + first.setFirstName("first");
33.158 + first.setSex(Sex.MALE);
33.159 + m.getPeople().add(first);
33.160 +
33.161 +
33.162 + m.applyBindings();
33.163 +
33.164 + int cnt = countChildren("ul");
33.165 + assert cnt == 1 : "One child, but was " + cnt;
33.166 +
33.167 +
33.168 + triggerChildClick("ul", 0);
33.169 +
33.170 + assert first.getSex() == Sex.FEMALE : "Transverted to female: " + first.getSex();
33.171 + }
33.172 +
33.173 + @Test public void cloneModel() {
33.174 + Person model = new Person();
33.175 +
33.176 + model.setFirstName("first");
33.177 + Person snd = model.clone();
33.178 + snd.setFirstName("clone");
33.179 + assertEquals("first", model.getFirstName(), "Value has not changed");
33.180 + assertEquals("clone", snd.getFirstName(), "Value has changed in clone");
33.181 + }
33.182 +
33.183 +
33.184 + @Test public void deepCopyOnClone() {
33.185 + People model = new People();
33.186 + model.getNicknames().add("Jarda");
33.187 + assertEquals(model.getNicknames().size(), 1, "One element");
33.188 + People snd = model.clone();
33.189 + snd.getNicknames().clear();
33.190 + assertEquals(snd.getNicknames().size(), 0, "Clone is empty");
33.191 + assertEquals(model.getNicknames().size(), 1, "Still one element");
33.192 + }
33.193 +
33.194 +
33.195 + @OnFunction
33.196 + static void call(KnockoutModel m, String data) {
33.197 + m.setName(data);
33.198 + m.setCallbackCount(m.getCallbackCount() + 1);
33.199 + }
33.200 +
33.201 + @OnFunction
33.202 + static void removePerson(KnockoutModel model, Person data) {
33.203 + model.setCallbackCount(model.getCallbackCount() + 1);
33.204 + model.getPeople().remove(data);
33.205 + }
33.206 +
33.207 +
33.208 @ComputedProperty
33.209 static String helloMessage(String name) {
33.210 return "Hello " + name + "!";
33.211 }
33.212
33.213 + @ComputedProperty
33.214 + static List<String> cmpResults(List<String> results) {
33.215 + return results;
33.216 + }
33.217 +
33.218 @Factory
33.219 public static Object[] create() {
33.220 return VMTest.create(KnockoutTest.class);
33.221 }
33.222 +
33.223 + @JavaScriptBody(args = { "id" }, body =
33.224 + "var e = window.document.getElementById(id);\n "
33.225 + + "if (typeof e === 'undefined') return -2;\n "
33.226 + + "return e.children.length;\n "
33.227 + )
33.228 + private static native int countChildren(String id);
33.229 +
33.230 + @JavaScriptBody(args = { "id", "pos" }, body =
33.231 + "var e = window.document.getElementById(id);\n "
33.232 + + "var ev = window.document.createEvent('MouseEvents');\n "
33.233 + + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
33.234 + + "e.children[pos].dispatchEvent(ev);\n "
33.235 + )
33.236 + private static native void triggerChildClick(String id, int pos);
33.237 +
33.238 + @JavaScriptBody(args = { "id", "pos" }, body =
33.239 + "var e = window.document.getElementById(id);\n "
33.240 + + "var t = e.children[pos].innerHTML;\n "
33.241 + + "return t ? t : null;"
33.242 + )
33.243 + private static native String childText(String id, int pos);
33.244 }
34.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Tue Apr 02 15:40:51 2013 +0200
34.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Thu May 02 09:18:22 2013 +0200
34.3 @@ -18,8 +18,13 @@
34.4 package org.apidesign.bck2brwsr.htmlpage;
34.5
34.6 import java.util.ArrayList;
34.7 +import java.util.Collections;
34.8 +import java.util.Iterator;
34.9 import java.util.List;
34.10 +import java.util.ListIterator;
34.11 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
34.12 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
34.13 +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange;
34.14 import org.apidesign.bck2brwsr.htmlpage.api.Page;
34.15 import org.apidesign.bck2brwsr.htmlpage.api.Property;
34.16 import static org.testng.Assert.*;
34.17 @@ -30,17 +35,22 @@
34.18 *
34.19 * @author Jaroslav Tulach <jtulach@netbeans.org>
34.20 */
34.21 -@Page(xhtml = "Empty.html", className = "Model", properties = {
34.22 +@Page(xhtml = "Empty.html", className = "Modelik", properties = {
34.23 @Property(name = "value", type = int.class),
34.24 - @Property(name = "unrelated", type = long.class)
34.25 + @Property(name = "count", type = int.class),
34.26 + @Property(name = "unrelated", type = long.class),
34.27 + @Property(name = "names", type = String.class, array = true),
34.28 + @Property(name = "values", type = int.class, array = true),
34.29 + @Property(name = "people", type = PersonImpl.class, array = true),
34.30 + @Property(name = "changedProperty", type=String.class)
34.31 })
34.32 public class ModelTest {
34.33 - private Model model;
34.34 - private static Model leakedModel;
34.35 + private Modelik model;
34.36 + private static Modelik leakedModel;
34.37
34.38 @BeforeMethod
34.39 public void createModel() {
34.40 - model = new Model();
34.41 + model = new Modelik();
34.42 }
34.43
34.44 @Test public void classGeneratedWithSetterGetter() {
34.45 @@ -53,6 +63,75 @@
34.46 assertEquals(16, model.getPowerValue());
34.47 }
34.48
34.49 + @Test public void arrayIsMutable() {
34.50 + assertEquals(model.getNames().size(), 0, "Is empty");
34.51 + model.getNames().add("Jarda");
34.52 + assertEquals(model.getNames().size(), 1, "One element");
34.53 + }
34.54 +
34.55 + @Test public void arrayChangesNotified() {
34.56 + MockKnockout my = new MockKnockout();
34.57 + MockKnockout.next = my;
34.58 +
34.59 + model.applyBindings();
34.60 +
34.61 + model.getNames().add("Hello");
34.62 +
34.63 + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
34.64 + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
34.65 +
34.66 + my.mutated.clear();
34.67 +
34.68 + Iterator<String> it = model.getNames().iterator();
34.69 + assertEquals(it.next(), "Hello");
34.70 + it.remove();
34.71 +
34.72 + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
34.73 + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
34.74 +
34.75 + my.mutated.clear();
34.76 +
34.77 + ListIterator<String> lit = model.getNames().listIterator();
34.78 + lit.add("Jarda");
34.79 +
34.80 + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
34.81 + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
34.82 + }
34.83 +
34.84 + @Test public void autoboxedArray() {
34.85 + MockKnockout my = new MockKnockout();
34.86 + MockKnockout.next = my;
34.87 +
34.88 + model.applyBindings();
34.89 +
34.90 + model.getValues().add(10);
34.91 +
34.92 + assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
34.93 + }
34.94 +
34.95 + @Test public void derivedArrayProp() {
34.96 + MockKnockout my = new MockKnockout();
34.97 + MockKnockout.next = my;
34.98 +
34.99 + model.applyBindings();
34.100 +
34.101 + model.setCount(10);
34.102 +
34.103 + List<String> arr = model.getRepeat();
34.104 + assertEquals(arr.size(), 10, "Ten items: " + arr);
34.105 +
34.106 + my.mutated.clear();
34.107 +
34.108 + model.setCount(5);
34.109 +
34.110 + arr = model.getRepeat();
34.111 + assertEquals(arr.size(), 5, "Five items: " + arr);
34.112 +
34.113 + assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
34.114 + assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
34.115 + assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
34.116 + }
34.117 +
34.118 @Test public void derivedPropertiesAreNotified() {
34.119 MockKnockout my = new MockKnockout();
34.120 MockKnockout.next = my;
34.121 @@ -61,6 +140,9 @@
34.122
34.123 model.setValue(33);
34.124
34.125 + // not interested in change of this property
34.126 + my.mutated.remove("changedProperty");
34.127 +
34.128 assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
34.129 assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
34.130 assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
34.131 @@ -68,7 +150,11 @@
34.132 my.mutated.clear();
34.133
34.134 model.setUnrelated(44);
34.135 - assertEquals(my.mutated.size(), 1, "One property changed");
34.136 +
34.137 +
34.138 + // not interested in change of this property
34.139 + my.mutated.remove("changedProperty");
34.140 + assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
34.141 assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
34.142 }
34.143
34.144 @@ -92,11 +178,43 @@
34.145 }
34.146 }
34.147
34.148 + @OnFunction
34.149 + static void doSomething() {
34.150 + }
34.151 +
34.152 @ComputedProperty
34.153 static int powerValue(int value) {
34.154 return value * value;
34.155 }
34.156
34.157 + @OnPropertyChange({ "powerValue", "unrelated" })
34.158 + static void aPropertyChanged(Modelik m, String name) {
34.159 + m.setChangedProperty(name);
34.160 + }
34.161 +
34.162 + @OnPropertyChange({ "values" })
34.163 + static void anArrayPropertyChanged(String name, Modelik m) {
34.164 + m.setChangedProperty(name);
34.165 + }
34.166 +
34.167 + @Test public void changeAnything() {
34.168 + model.setCount(44);
34.169 + assertNull(model.getChangedProperty(), "No observed value change");
34.170 + }
34.171 + @Test public void changeValue() {
34.172 + model.setValue(33);
34.173 + assertEquals(model.getChangedProperty(), "powerValue", "power property changed");
34.174 + }
34.175 + @Test public void changeUnrelated() {
34.176 + model.setUnrelated(333);
34.177 + assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed");
34.178 + }
34.179 +
34.180 + @Test public void changeInArray() {
34.181 + model.getValues().add(10);
34.182 + assertEquals(model.getChangedProperty(), "values", "Something added into the array");
34.183 + }
34.184 +
34.185 @ComputedProperty
34.186 static String notAllowedRead() {
34.187 return "Not allowed callback: " + leakedModel.getUnrelated();
34.188 @@ -108,12 +226,31 @@
34.189 return "Not allowed callback!";
34.190 }
34.191
34.192 + @ComputedProperty
34.193 + static List<String> repeat(int count) {
34.194 + return Collections.nCopies(count, "Hello");
34.195 + }
34.196 +
34.197 static class MockKnockout extends Knockout {
34.198 - List<String> mutated = new ArrayList<String>();
34.199 + List<String> mutated = new ArrayList<>();
34.200 +
34.201 + MockKnockout() {
34.202 + super(null);
34.203 + }
34.204
34.205 @Override
34.206 public void valueHasMutated(String prop) {
34.207 mutated.add(prop);
34.208 }
34.209 }
34.210 +
34.211 + public @Test void hasPersonPropertyAndComputedFullName() {
34.212 + List<Person> arr = model.getPeople();
34.213 + assertEquals(arr.size(), 0, "By default empty");
34.214 + Person p = null;
34.215 + if (p != null) {
34.216 + String fullNameGenerated = p.getFullName();
34.217 + assertNotNull(fullNameGenerated);
34.218 + }
34.219 + }
34.220 }
35.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Tue Apr 02 15:40:51 2013 +0200
35.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Thu May 02 09:18:22 2013 +0200
35.3 @@ -50,7 +50,7 @@
35.4 if (PAGE != ref) {
35.5 throw new IllegalStateException("Both references should be the same. " + ref + " != " + PAGE);
35.6 }
35.7 - ref.PG_TITLE.setText("You want this window to be named " + ref.PG_TEXT.getValue());
35.8 + ref.pg_title.setText("You want this window to be named " + ref.pg_text.getValue());
35.9 }
35.10
35.11 @On(event = CLICK, id={ "pg.title", "pg.text" })
35.12 @@ -58,11 +58,11 @@
35.13 if (!id.equals("pg.title")) {
35.14 throw new IllegalStateException();
35.15 }
35.16 - PAGE.PG_TITLE.setText(id);
35.17 + PAGE.pg_title.setText(id);
35.18 }
35.19
35.20 @On(event = CLICK, id={ "pg.canvas" })
35.21 static void clickCanvas(String id, double layerX) {
35.22 - PAGE.PG_CANVAS.setWidth((int) layerX);
35.23 + PAGE.pg_canvas.setWidth((int) layerX);
35.24 }
35.25 }
36.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
36.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Thu May 02 09:18:22 2013 +0200
36.3 @@ -0,0 +1,53 @@
36.4 +/**
36.5 + * Back 2 Browser Bytecode Translator
36.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
36.7 + *
36.8 + * This program is free software: you can redistribute it and/or modify
36.9 + * it under the terms of the GNU General Public License as published by
36.10 + * the Free Software Foundation, version 2 of the License.
36.11 + *
36.12 + * This program is distributed in the hope that it will be useful,
36.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
36.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36.15 + * GNU General Public License for more details.
36.16 + *
36.17 + * You should have received a copy of the GNU General Public License
36.18 + * along with this program. Look for COPYING file in the top folder.
36.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
36.20 + */
36.21 +package org.apidesign.bck2brwsr.htmlpage;
36.22 +
36.23 +import java.io.IOException;
36.24 +import java.util.Locale;
36.25 +import javax.tools.Diagnostic;
36.26 +import javax.tools.JavaFileObject;
36.27 +import static org.testng.Assert.*;
36.28 +import org.testng.annotations.Test;
36.29 +
36.30 +/** Verify errors emitted by the processor.
36.31 + *
36.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
36.33 + */
36.34 +public class PageTest {
36.35 + @Test public void verifyWrongType() throws IOException {
36.36 + String html = "<html><body>"
36.37 + + "</body></html>";
36.38 + String code = "package x.y.z;\n"
36.39 + + "import org.apidesign.bck2brwsr.htmlpage.api.*;\n"
36.40 + + "@Page(xhtml=\"index.xhtml\", className=\"Model\", properties={\n"
36.41 + + " @Property(name=\"prop\", type=Runnable.class)\n"
36.42 + + "})\n"
36.43 + + "class X {\n"
36.44 + + "}\n";
36.45 +
36.46 + Compile c = Compile.create(html, code);
36.47 + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
36.48 + for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
36.49 + String msg = e.getMessage(Locale.ENGLISH);
36.50 + if (!msg.contains("Runnable")) {
36.51 + fail("Should contain warning about Runnable: " + msg);
36.52 + }
36.53 + }
36.54 + }
36.55 +
36.56 +}
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
37.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Thu May 02 09:18:22 2013 +0200
37.3 @@ -0,0 +1,62 @@
37.4 +/**
37.5 + * Back 2 Browser Bytecode Translator
37.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
37.7 + *
37.8 + * This program is free software: you can redistribute it and/or modify
37.9 + * it under the terms of the GNU General Public License as published by
37.10 + * the Free Software Foundation, version 2 of the License.
37.11 + *
37.12 + * This program is distributed in the hope that it will be useful,
37.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
37.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37.15 + * GNU General Public License for more details.
37.16 + *
37.17 + * You should have received a copy of the GNU General Public License
37.18 + * along with this program. Look for COPYING file in the top folder.
37.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
37.20 + */
37.21 +package org.apidesign.bck2brwsr.htmlpage;
37.22 +
37.23 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
37.24 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
37.25 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
37.26 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
37.27 +
37.28 +/**
37.29 + *
37.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
37.31 + */
37.32 +@Model(className = "Person", properties = {
37.33 + @Property(name = "firstName", type = String.class),
37.34 + @Property(name = "lastName", type = String.class),
37.35 + @Property(name = "sex", type = Sex.class)
37.36 +})
37.37 +final class PersonImpl {
37.38 + @ComputedProperty
37.39 + public static String fullName(String firstName, String lastName) {
37.40 + return firstName + " " + lastName;
37.41 + }
37.42 +
37.43 + @ComputedProperty
37.44 + public static String sexType(Sex sex) {
37.45 + return sex == null ? "unknown" : sex.toString();
37.46 + }
37.47 +
37.48 + @OnFunction
37.49 + static void changeSex(Person p) {
37.50 + if (p.getSex() == Sex.MALE) {
37.51 + p.setSex(Sex.FEMALE);
37.52 + } else {
37.53 + p.setSex(Sex.MALE);
37.54 + }
37.55 + }
37.56 +
37.57 + @Model(className = "People", properties = {
37.58 + @Property(array = true, name = "info", type = Person.class),
37.59 + @Property(array = true, name = "nicknames", type = String.class),
37.60 + @Property(array = true, name = "age", type = int.class),
37.61 + @Property(array = true, name = "sex", type = Sex.class)
37.62 + })
37.63 + public class PeopleImpl {
37.64 + }
37.65 +}
38.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
38.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Sex.java Thu May 02 09:18:22 2013 +0200
38.3 @@ -0,0 +1,26 @@
38.4 +/**
38.5 + * Back 2 Browser Bytecode Translator
38.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
38.7 + *
38.8 + * This program is free software: you can redistribute it and/or modify
38.9 + * it under the terms of the GNU General Public License as published by
38.10 + * the Free Software Foundation, version 2 of the License.
38.11 + *
38.12 + * This program is distributed in the hope that it will be useful,
38.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
38.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38.15 + * GNU General Public License for more details.
38.16 + *
38.17 + * You should have received a copy of the GNU General Public License
38.18 + * along with this program. Look for COPYING file in the top folder.
38.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
38.20 + */
38.21 +package org.apidesign.bck2brwsr.htmlpage;
38.22 +
38.23 +/**
38.24 + *
38.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
38.26 + */
38.27 +public enum Sex {
38.28 + MALE, FEMALE;
38.29 +}
39.1 --- a/javaquery/demo-calculator-dynamic/nbactions.xml Tue Apr 02 15:40:51 2013 +0200
39.2 +++ b/javaquery/demo-calculator-dynamic/nbactions.xml Thu May 02 09:18:22 2013 +0200
39.3 @@ -23,7 +23,7 @@
39.4 <actionName>run</actionName>
39.5 <goals>
39.6 <goal>process-classes</goal>
39.7 - <goal>org.apidesign.bck2brwsr:mojo:0.5-SNAPSHOT:brwsr</goal>
39.8 + <goal>bck2brwsr:brwsr</goal>
39.9 </goals>
39.10 </action>
39.11 </actions>
40.1 --- a/javaquery/demo-calculator-dynamic/pom.xml Tue Apr 02 15:40:51 2013 +0200
40.2 +++ b/javaquery/demo-calculator-dynamic/pom.xml Thu May 02 09:18:22 2013 +0200
40.3 @@ -4,7 +4,7 @@
40.4
40.5 <groupId>org.apidesign.bck2brwsr</groupId>
40.6 <artifactId>demo.calculator</artifactId>
40.7 - <version>0.5-SNAPSHOT</version>
40.8 + <version>0.8-SNAPSHOT</version>
40.9 <packaging>jar</packaging>
40.10
40.11 <name>JavaQuery Demo - Calculator</name>
40.12 @@ -17,8 +17,8 @@
40.13 <plugins>
40.14 <plugin>
40.15 <groupId>org.apidesign.bck2brwsr</groupId>
40.16 - <artifactId>mojo</artifactId>
40.17 - <version>0.5-SNAPSHOT</version>
40.18 + <artifactId>bck2brwsr-maven-plugin</artifactId>
40.19 + <version>${project.version}</version>
40.20 <executions>
40.21 <execution>
40.22 <goals>
40.23 @@ -93,13 +93,13 @@
40.24 <dependency>
40.25 <groupId>org.apidesign.bck2brwsr</groupId>
40.26 <artifactId>emul</artifactId>
40.27 - <version>0.5-SNAPSHOT</version>
40.28 + <version>${project.version}</version>
40.29 <classifier>rt</classifier>
40.30 </dependency>
40.31 <dependency>
40.32 <groupId>org.apidesign.bck2brwsr</groupId>
40.33 <artifactId>javaquery.api</artifactId>
40.34 - <version>0.5-SNAPSHOT</version>
40.35 + <version>${project.version}</version>
40.36 </dependency>
40.37 <dependency>
40.38 <groupId>org.testng</groupId>
40.39 @@ -112,7 +112,7 @@
40.40 <artifactId>vm4brwsr</artifactId>
40.41 <classifier>js</classifier>
40.42 <type>zip</type>
40.43 - <version>0.5-SNAPSHOT</version>
40.44 + <version>${project.version}</version>
40.45 <scope>provided</scope>
40.46 </dependency>
40.47 </dependencies>
41.1 --- a/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Tue Apr 02 15:40:51 2013 +0200
41.2 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Thu May 02 09:18:22 2013 +0200
41.3 @@ -17,9 +17,11 @@
41.4 */
41.5 package org.apidesign.bck2brwsr.demo.calc;
41.6
41.7 +import java.util.List;
41.8 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
41.9 import org.apidesign.bck2brwsr.htmlpage.api.On;
41.10 import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
41.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
41.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
41.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
41.14
41.15 @@ -33,11 +35,12 @@
41.16 @Property(name = "memory", type = double.class),
41.17 @Property(name = "display", type = double.class),
41.18 @Property(name = "operation", type = String.class),
41.19 - @Property(name = "hover", type = boolean.class)
41.20 + @Property(name = "hover", type = boolean.class),
41.21 + @Property(name = "history", type = HistoryImpl.class, array = true)
41.22 })
41.23 public class Calc {
41.24 static {
41.25 - new Calculator().applyBindings();
41.26 + new Calculator().applyBindings().setOperation("plus");
41.27 }
41.28
41.29 @On(event = CLICK, id="clear")
41.30 @@ -65,14 +68,31 @@
41.31
41.32 @On(event = CLICK, id="result")
41.33 static void computeTheValue(Calculator c) {
41.34 - c.setDisplay(compute(
41.35 + final double newValue = compute(
41.36 c.getOperation(),
41.37 c.getMemory(),
41.38 c.getDisplay()
41.39 - ));
41.40 + );
41.41 + c.setDisplay(newValue);
41.42 + if (!containsValue(c.getHistory(), newValue)) {
41.43 + History h = new History();
41.44 + h.setValue(newValue);
41.45 + h.setOperation(c.getOperation());
41.46 + c.getHistory().add(h);
41.47 + }
41.48 c.setMemory(0);
41.49 }
41.50
41.51 + @OnFunction
41.52 + static void recoverMemory(Calculator c, History data) {
41.53 + c.setDisplay(data.getValue());
41.54 + }
41.55 +
41.56 + @OnFunction
41.57 + static void removeMemory(Calculator c, History data) {
41.58 + c.getHistory().remove(data);
41.59 + }
41.60 +
41.61 private static double compute(String op, double memory, double display) {
41.62 switch (op) {
41.63 case "plus": return memory + display;
41.64 @@ -109,4 +129,18 @@
41.65 }
41.66 return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
41.67 }
41.68 +
41.69 + @ComputedProperty
41.70 + static boolean emptyHistory(List<?> history) {
41.71 + return history.isEmpty();
41.72 + }
41.73 +
41.74 + private static boolean containsValue(List<History> arr, final double newValue) {
41.75 + for (History history : arr) {
41.76 + if (history.getValue() == newValue) {
41.77 + return true;
41.78 + }
41.79 + }
41.80 + return false;
41.81 + }
41.82 }
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
42.2 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/HistoryImpl.java Thu May 02 09:18:22 2013 +0200
42.3 @@ -0,0 +1,43 @@
42.4 +/**
42.5 + * Back 2 Browser Bytecode Translator
42.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
42.7 + *
42.8 + * This program is free software: you can redistribute it and/or modify
42.9 + * it under the terms of the GNU General Public License as published by
42.10 + * the Free Software Foundation, version 2 of the License.
42.11 + *
42.12 + * This program is distributed in the hope that it will be useful,
42.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
42.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42.15 + * GNU General Public License for more details.
42.16 + *
42.17 + * You should have received a copy of the GNU General Public License
42.18 + * along with this program. Look for COPYING file in the top folder.
42.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
42.20 + */
42.21 +package org.apidesign.bck2brwsr.demo.calc;
42.22 +
42.23 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
42.24 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
42.25 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
42.26 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
42.27 +
42.28 +/**
42.29 + *
42.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
42.31 + */
42.32 +@Model(className = "History", properties = {
42.33 + @Property(name = "value", type = double.class),
42.34 + @Property(name = "operation", type = String.class)
42.35 +})
42.36 +public class HistoryImpl {
42.37 + @ComputedProperty
42.38 + static String resultOf(String operation) {
42.39 + return "result of " + operation;
42.40 + }
42.41 +
42.42 + @OnFunction
42.43 + static void twice(History data) {
42.44 + data.setValue(2.0 * data.getValue());
42.45 + }
42.46 +}
43.1 --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Tue Apr 02 15:40:51 2013 +0200
43.2 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Thu May 02 09:18:22 2013 +0200
43.3 @@ -78,82 +78,25 @@
43.4 </table>
43.5 <div data-bind="text: displayPreview"></div>
43.6
43.7 + <h4>Previous Results</h4>
43.8 +
43.9 + <div data-bind="if: emptyHistory">No results yet.</div>
43.10 + <ul data-bind="foreach: history">
43.11 + <li>
43.12 + <span data-bind="text: $data.value"></span> -
43.13 + <a href="#" data-bind="click: $root.recoverMemory">Use</a>
43.14 + <a href="#" data-bind="click: $root.removeMemory">Remove</a>
43.15 + <a href="#" data-bind="click: $data.twice">Double</a> -
43.16 + <span data-bind="text: $data.resultOf"></span>
43.17 + </li>
43.18 + </ul>
43.19 +
43.20 <script src="bck2brwsr.js"></script>
43.21 <script type="text/javascript">
43.22 - var vm = bck2brwsr('demo.calculator-0.3-SNAPSHOT.jar');
43.23 + var vm = bck2brwsr('demo.calculator-0.6.jar');
43.24 vm.loadClass('org.apidesign.bck2brwsr.demo.calc.Calc');
43.25 </script>
43.26
43.27 <hr/>
43.28 - <pre>
43.29 - <span class="keyword-directive">package</span> org.apidesign.bck2brwsr.mavenhtml;
43.30 -
43.31 - <span class="keyword-directive">import</span> org.apidesign.bck2brwsr.htmlpage.api.OnClick;
43.32 - <span class="keyword-directive">import</span> org.apidesign.bck2brwsr.htmlpage.api.Page;
43.33 -
43.34 - <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>
43.35 - <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>
43.36 - <span class="comment"> * </span><span class="comment">an XHTML page and Java.</span>
43.37 - <span class="comment"> * </span>
43.38 - <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>
43.39 - <span class="comment">*/</span>
43.40 - @Page(xhtml=<span class="string">"</span><span class="string">Calculator.xhtml</span><span class="string">"</span>)
43.41 - <span class="keyword-directive">public</span> <span class="keyword-directive">class</span> App {
43.42 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> <span class="keyword-directive">double</span> memory;
43.43 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> String operation;
43.44 -
43.45 - @OnClick(id=<span class="string">"</span><span class="string">clear</span><span class="string">"</span>)
43.46 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> clear() {
43.47 - memory = <span class="number">0</span>;
43.48 - operation = <span class="keyword-directive">null</span>;
43.49 - Calculator.DISPLAY.setValue(<span class="string">"</span><span class="string">0</span><span class="string">"</span>);
43.50 - }
43.51 -
43.52 - @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> })
43.53 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> applyOp(String op) {
43.54 - memory = getValue();
43.55 - operation = op;
43.56 - Calculator.DISPLAY.setValue(<span class="string">"</span><span class="string">0</span><span class="string">"</span>);
43.57 - }
43.58 -
43.59 - @OnClick(id=<span class="string">"</span><span class="string">result</span><span class="string">"</span>)
43.60 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> computeTheValue() {
43.61 - <span class="keyword-directive">switch</span> (operation) {
43.62 - <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>;
43.63 - <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>;
43.64 - <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>;
43.65 - <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>;
43.66 - <span class="keyword-directive">default</span>: <span class="keyword-directive">throw</span> <span class="keyword-directive">new</span> IllegalStateException(operation);
43.67 - }
43.68 - }
43.69 -
43.70 - @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>})
43.71 - <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> addDigit(String digit) {
43.72 - digit = digit.substring(<span class="number">1</span>);
43.73 - String v = Calculator.DISPLAY.getValue();
43.74 - <span class="keyword-directive">if</span> (getValue() == <span class="number">0.0</span>) {
43.75 - Calculator.DISPLAY.setValue(digit);
43.76 - } <span class="keyword-directive">else</span> {
43.77 - Calculator.DISPLAY.setValue(v + digit);
43.78 - }
43.79 - }
43.80 -
43.81 - <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) {
43.82 - StringBuilder sb = <span class="keyword-directive">new</span> StringBuilder();
43.83 - sb.append(v);
43.84 - Calculator.DISPLAY.setValue(sb.toString());
43.85 - }
43.86 -
43.87 - <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> <span class="keyword-directive">double</span> getValue() {
43.88 - <span class="keyword-directive">try</span> {
43.89 - <span class="keyword-directive">return</span> Double.parseDouble(Calculator.DISPLAY.getValue());
43.90 - } <span class="keyword-directive">catch</span> (NumberFormatException ex) {
43.91 - Calculator.DISPLAY.setValue(<span class="string">"</span><span class="string">err</span><span class="string">"</span>);
43.92 - <span class="keyword-directive">return</span> <span class="number">0.0</span>;
43.93 - }
43.94 - }
43.95 - }
43.96 -
43.97 - </pre>
43.98 </body>
43.99 </html>
44.1 --- a/javaquery/demo-calculator/nbactions.xml Tue Apr 02 15:40:51 2013 +0200
44.2 +++ b/javaquery/demo-calculator/nbactions.xml Thu May 02 09:18:22 2013 +0200
44.3 @@ -23,7 +23,7 @@
44.4 <actionName>run</actionName>
44.5 <goals>
44.6 <goal>package</goal>
44.7 - <goal>org.apidesign.bck2brwsr:mojo:0.5-SNAPSHOT:brwsr</goal>
44.8 + <goal>bck2brwsr:brwsr</goal>
44.9 </goals>
44.10 <properties>
44.11 <skipTests>true</skipTests>
45.1 --- a/javaquery/demo-calculator/pom.xml Tue Apr 02 15:40:51 2013 +0200
45.2 +++ b/javaquery/demo-calculator/pom.xml Thu May 02 09:18:22 2013 +0200
45.3 @@ -4,7 +4,7 @@
45.4
45.5 <groupId>org.apidesign.bck2brwsr</groupId>
45.6 <artifactId>demo.static.calculator</artifactId>
45.7 - <version>0.5-SNAPSHOT</version>
45.8 + <version>0.8-SNAPSHOT</version>
45.9 <packaging>jar</packaging>
45.10
45.11 <name>JavaQuery Demo - Calculator - Static Compilation</name>
45.12 @@ -12,13 +12,14 @@
45.13
45.14 <properties>
45.15 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
45.16 + <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
45.17 </properties>
45.18 <build>
45.19 <plugins>
45.20 <plugin>
45.21 <groupId>org.apidesign.bck2brwsr</groupId>
45.22 - <artifactId>mojo</artifactId>
45.23 - <version>0.5-SNAPSHOT</version>
45.24 + <artifactId>bck2brwsr-maven-plugin</artifactId>
45.25 + <version>${project.version}</version>
45.26 <executions>
45.27 <execution>
45.28 <goals>
45.29 @@ -31,6 +32,7 @@
45.30 <directory>${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/</directory>
45.31 <startpage>index.xhtml</startpage>
45.32 <javascript>${project.build.directory}/bck2brwsr.js</javascript>
45.33 + <obfuscation>${bck2brwsr.obfuscationlevel}</obfuscation>
45.34 </configuration>
45.35 </plugin>
45.36 <plugin>
45.37 @@ -96,13 +98,13 @@
45.38 <dependency>
45.39 <groupId>org.apidesign.bck2brwsr</groupId>
45.40 <artifactId>emul</artifactId>
45.41 - <version>0.5-SNAPSHOT</version>
45.42 + <version>${project.version}</version>
45.43 <classifier>rt</classifier>
45.44 </dependency>
45.45 <dependency>
45.46 <groupId>org.apidesign.bck2brwsr</groupId>
45.47 <artifactId>javaquery.api</artifactId>
45.48 - <version>0.5-SNAPSHOT</version>
45.49 + <version>${project.version}</version>
45.50 </dependency>
45.51 </dependencies>
45.52 </project>
46.1 --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Tue Apr 02 15:40:51 2013 +0200
46.2 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Thu May 02 09:18:22 2013 +0200
46.3 @@ -17,9 +17,11 @@
46.4 */
46.5 package org.apidesign.bck2brwsr.demo.calc.staticcompilation;
46.6
46.7 +import java.util.List;
46.8 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
46.9 import org.apidesign.bck2brwsr.htmlpage.api.On;
46.10 import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
46.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
46.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
46.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
46.14
46.15 @@ -33,11 +35,12 @@
46.16 @Property(name = "memory", type = double.class),
46.17 @Property(name = "display", type = double.class),
46.18 @Property(name = "operation", type = String.class),
46.19 - @Property(name = "hover", type = boolean.class)
46.20 + @Property(name = "hover", type = boolean.class),
46.21 + @Property(name = "history", type = double.class, array = true)
46.22 })
46.23 public class Calc {
46.24 static {
46.25 - new Calculator().applyBindings();
46.26 + new Calculator().applyBindings().setOperation("plus");
46.27 }
46.28
46.29 @On(event = CLICK, id="clear")
46.30 @@ -65,14 +68,28 @@
46.31
46.32 @On(event = CLICK, id="result")
46.33 static void computeTheValue(Calculator c) {
46.34 - c.setDisplay(compute(
46.35 + final double newValue = compute(
46.36 c.getOperation(),
46.37 c.getMemory(),
46.38 c.getDisplay()
46.39 - ));
46.40 + );
46.41 + c.setDisplay(newValue);
46.42 + if (!c.getHistory().contains(newValue)) {
46.43 + c.getHistory().add(newValue);
46.44 + }
46.45 c.setMemory(0);
46.46 }
46.47
46.48 + @OnFunction
46.49 + static void recoverMemory(Calculator c, double data) {
46.50 + c.setDisplay(data);
46.51 + }
46.52 +
46.53 + @OnFunction
46.54 + static void removeMemory(Calculator c, double data) {
46.55 + c.getHistory().remove(data);
46.56 + }
46.57 +
46.58 private static double compute(String op, double memory, double display) {
46.59 switch (op) {
46.60 case "plus": return memory + display;
46.61 @@ -109,4 +126,9 @@
46.62 }
46.63 return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
46.64 }
46.65 +
46.66 + @ComputedProperty
46.67 + static boolean emptyHistory(List<?> history) {
46.68 + return history.isEmpty();
46.69 + }
46.70 }
47.1 --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Tue Apr 02 15:40:51 2013 +0200
47.2 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Thu May 02 09:18:22 2013 +0200
47.3 @@ -76,10 +76,22 @@
47.4 </tr>
47.5 </tbody>
47.6 </table>
47.7 +
47.8 + <h4>Previous Results</h4>
47.9 +
47.10 + <div data-bind="if: emptyHistory">No results yet.</div>
47.11 + <ul data-bind="foreach: history">
47.12 + <li>
47.13 + <span data-bind="text: $data"></span> -
47.14 + <a href="#" data-bind="click: $root.recoverMemory">Use</a>
47.15 + <a href="#" data-bind="click: $root.removeMemory">Remove</a>
47.16 + </li>
47.17 + </ul>
47.18 +
47.19 <div data-bind="text: displayPreview"></div>
47.20 - <script src="bck2brwsr.js"/>
47.21 + <script src="bck2brwsr.js"></script>
47.22 <script>
47.23 - var vm = bck2brwsr('demo.static.calculator-0.5-SNAPSHOT.jar');
47.24 + var vm = bck2brwsr('demo.static.calculator-0.6.jar');
47.25 vm.loadClass('org.apidesign.bck2brwsr.demo.calc.staticcompilation.Calc');
47.26 </script>
47.27 </body>
48.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
48.2 +++ b/javaquery/demo-twitter/bck2brwsr-assembly.xml Thu May 02 09:18:22 2013 +0200
48.3 @@ -0,0 +1,62 @@
48.4 +<?xml version="1.0"?>
48.5 +<!--
48.6 +
48.7 + Back 2 Browser Bytecode Translator
48.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
48.9 +
48.10 + This program is free software: you can redistribute it and/or modify
48.11 + it under the terms of the GNU General Public License as published by
48.12 + the Free Software Foundation, version 2 of the License.
48.13 +
48.14 + This program is distributed in the hope that it will be useful,
48.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
48.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48.17 + GNU General Public License for more details.
48.18 +
48.19 + You should have received a copy of the GNU General Public License
48.20 + along with this program. Look for COPYING file in the top folder.
48.21 + If not, see http://opensource.org/licenses/GPL-2.0.
48.22 +
48.23 +-->
48.24 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
48.25 + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
48.26 +
48.27 + <id>bck2brwsr</id>
48.28 + <formats>
48.29 + <format>zip</format>
48.30 + </formats>
48.31 + <baseDirectory>public_html</baseDirectory>
48.32 + <dependencySets>
48.33 + <dependencySet>
48.34 + <useProjectArtifact>false</useProjectArtifact>
48.35 + <scope>runtime</scope>
48.36 + <outputDirectory>lib</outputDirectory>
48.37 + <includes>
48.38 + <include>*:jar</include>
48.39 + <include>*:rt</include>
48.40 + </includes>
48.41 + </dependencySet>
48.42 + </dependencySets>
48.43 + <fileSets>
48.44 + <fileSet>
48.45 + <directory>${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/twitter/</directory>
48.46 + <includes>
48.47 + <include>**/*</include>
48.48 + </includes>
48.49 + <excludes>
48.50 + <exclude>**/*.class</exclude>
48.51 + </excludes>
48.52 + <outputDirectory>/</outputDirectory>
48.53 + </fileSet>
48.54 + </fileSets>
48.55 + <files>
48.56 + <file>
48.57 + <source>${project.build.directory}/${project.build.finalName}.jar</source>
48.58 + <outputDirectory>/</outputDirectory>
48.59 + </file>
48.60 + <file>
48.61 + <source>${project.build.directory}/bck2brwsr.js</source>
48.62 + <outputDirectory>/</outputDirectory>
48.63 + </file>
48.64 + </files>
48.65 +</assembly>
48.66 \ No newline at end of file
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
49.2 +++ b/javaquery/demo-twitter/nb-configuration.xml Thu May 02 09:18:22 2013 +0200
49.3 @@ -0,0 +1,37 @@
49.4 +<?xml version="1.0" encoding="UTF-8"?>
49.5 +<!--
49.6 +
49.7 + Back 2 Browser Bytecode Translator
49.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
49.9 +
49.10 + This program is free software: you can redistribute it and/or modify
49.11 + it under the terms of the GNU General Public License as published by
49.12 + the Free Software Foundation, version 2 of the License.
49.13 +
49.14 + This program is distributed in the hope that it will be useful,
49.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
49.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49.17 + GNU General Public License for more details.
49.18 +
49.19 + You should have received a copy of the GNU General Public License
49.20 + along with this program. Look for COPYING file in the top folder.
49.21 + If not, see http://opensource.org/licenses/GPL-2.0.
49.22 +
49.23 +-->
49.24 +<project-shared-configuration>
49.25 + <!--
49.26 +This file contains additional configuration written by modules in the NetBeans IDE.
49.27 +The configuration is intended to be shared among all the users of project and
49.28 +therefore it is assumed to be part of version control checkout.
49.29 +Without this configuration present, some functionality in the IDE may be limited or fail altogether.
49.30 +-->
49.31 + <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
49.32 + <!--
49.33 +Properties that influence various parts of the IDE, especially code formatting and the like.
49.34 +You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
49.35 +That way multiple projects can share the same settings (useful for formatting rules for example).
49.36 +Any value defined here will override the pom.xml file value but is only applicable to the current project.
49.37 +-->
49.38 + <netbeans.compile.on.save>none</netbeans.compile.on.save>
49.39 + </properties>
49.40 +</project-shared-configuration>
50.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
50.2 +++ b/javaquery/demo-twitter/nbactions.xml Thu May 02 09:18:22 2013 +0200
50.3 @@ -0,0 +1,29 @@
50.4 +<?xml version="1.0" encoding="UTF-8"?>
50.5 +<!--
50.6 +
50.7 + Back 2 Browser Bytecode Translator
50.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
50.9 +
50.10 + This program is free software: you can redistribute it and/or modify
50.11 + it under the terms of the GNU General Public License as published by
50.12 + the Free Software Foundation, version 2 of the License.
50.13 +
50.14 + This program is distributed in the hope that it will be useful,
50.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
50.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50.17 + GNU General Public License for more details.
50.18 +
50.19 + You should have received a copy of the GNU General Public License
50.20 + along with this program. Look for COPYING file in the top folder.
50.21 + If not, see http://opensource.org/licenses/GPL-2.0.
50.22 +
50.23 +-->
50.24 +<actions>
50.25 + <action>
50.26 + <actionName>run</actionName>
50.27 + <goals>
50.28 + <goal>process-classes</goal>
50.29 + <goal>bck2brwsr:brwsr</goal>
50.30 + </goals>
50.31 + </action>
50.32 +</actions>
51.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
51.2 +++ b/javaquery/demo-twitter/pom.xml Thu May 02 09:18:22 2013 +0200
51.3 @@ -0,0 +1,151 @@
51.4 +<?xml version="1.0" encoding="UTF-8"?>
51.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
51.6 + <modelVersion>4.0.0</modelVersion>
51.7 + <parent>
51.8 + <artifactId>javaquery</artifactId>
51.9 + <groupId>org.apidesign.bck2brwsr</groupId>
51.10 + <version>0.8-SNAPSHOT</version>
51.11 + </parent>
51.12 +
51.13 + <groupId>org.apidesign.bck2brwsr</groupId>
51.14 + <artifactId>demo-twitter</artifactId>
51.15 + <version>0.8-SNAPSHOT</version>
51.16 + <packaging>jar</packaging>
51.17 +
51.18 + <name>Bck2Brwsr's Twttr</name>
51.19 + <description>
51.20 + Rewrite of knockoutjs example to use model written in Java and
51.21 + execute using Bck2Brwsr virtual machine.
51.22 + </description>
51.23 +
51.24 + <repositories>
51.25 + <repository>
51.26 + <id>java.net</id>
51.27 + <name>Java.net</name>
51.28 + <url>https://maven.java.net/content/repositories/releases/</url>
51.29 + <snapshots>
51.30 + </snapshots>
51.31 + </repository>
51.32 + <repository>
51.33 + <id>netbeans</id>
51.34 + <name>NetBeans</name>
51.35 + <url>http://bits.netbeans.org/maven2/</url>
51.36 + </repository>
51.37 + </repositories>
51.38 + <pluginRepositories>
51.39 + <pluginRepository>
51.40 + <id>java.net</id>
51.41 + <name>Java.net</name>
51.42 + <url>https://maven.java.net/content/repositories/releases/</url>
51.43 + <snapshots>
51.44 + </snapshots>
51.45 + </pluginRepository>
51.46 + </pluginRepositories>
51.47 +
51.48 + <properties>
51.49 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
51.50 + <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
51.51 + </properties>
51.52 + <build>
51.53 + <plugins>
51.54 + <plugin>
51.55 + <groupId>org.apidesign.bck2brwsr</groupId>
51.56 + <artifactId>bck2brwsr-maven-plugin</artifactId>
51.57 + <version>${project.version}</version>
51.58 + <executions>
51.59 + <execution>
51.60 + <goals>
51.61 + <goal>brwsr</goal>
51.62 + <goal>j2js</goal>
51.63 + </goals>
51.64 + </execution>
51.65 + </executions>
51.66 + <configuration>
51.67 + <startpage>org/apidesign/bck2brwsr/demo/twitter/index.html</startpage>
51.68 + <javascript>${project.build.directory}/bck2brwsr.js</javascript>
51.69 + <obfuscation>${bck2brwsr.obfuscationlevel}</obfuscation>
51.70 + </configuration>
51.71 + </plugin>
51.72 + <plugin>
51.73 + <groupId>org.apache.maven.plugins</groupId>
51.74 + <artifactId>maven-compiler-plugin</artifactId>
51.75 + <version>2.3.2</version>
51.76 + <configuration>
51.77 + <source>1.7</source>
51.78 + <target>1.7</target>
51.79 + </configuration>
51.80 + </plugin>
51.81 + <plugin>
51.82 + <groupId>org.apache.maven.plugins</groupId>
51.83 + <artifactId>maven-jar-plugin</artifactId>
51.84 + <version>2.4</version>
51.85 + <configuration>
51.86 + <archive>
51.87 + <manifest>
51.88 + <addClasspath>true</addClasspath>
51.89 + <classpathPrefix>lib/</classpathPrefix>
51.90 + </manifest>
51.91 + </archive>
51.92 + </configuration>
51.93 + </plugin>
51.94 + <plugin>
51.95 + <groupId>org.apache.maven.plugins</groupId>
51.96 + <artifactId>maven-deploy-plugin</artifactId>
51.97 + <version>2.7</version>
51.98 + <configuration>
51.99 + <skip>true</skip>
51.100 + </configuration>
51.101 + </plugin>
51.102 + <plugin>
51.103 + <artifactId>maven-assembly-plugin</artifactId>
51.104 + <version>2.4</version>
51.105 + <executions>
51.106 + <execution>
51.107 + <id>distro-assembly</id>
51.108 + <phase>package</phase>
51.109 + <goals>
51.110 + <goal>single</goal>
51.111 + </goals>
51.112 + <configuration>
51.113 + <descriptors>
51.114 + <descriptor>bck2brwsr-assembly.xml</descriptor>
51.115 + </descriptors>
51.116 + </configuration>
51.117 + </execution>
51.118 + </executions>
51.119 + </plugin>
51.120 + </plugins>
51.121 + </build>
51.122 +
51.123 + <dependencies>
51.124 + <dependency>
51.125 + <groupId>org.apidesign.bck2brwsr</groupId>
51.126 + <artifactId>emul</artifactId>
51.127 + <version>${project.version}</version>
51.128 + <classifier>rt</classifier>
51.129 + </dependency>
51.130 + <dependency>
51.131 + <groupId>org.apidesign.bck2brwsr</groupId>
51.132 + <artifactId>javaquery.api</artifactId>
51.133 + <version>${project.version}</version>
51.134 + </dependency>
51.135 + <dependency>
51.136 + <groupId>org.testng</groupId>
51.137 + <artifactId>testng</artifactId>
51.138 + <version>6.5.2</version>
51.139 + <scope>test</scope>
51.140 + </dependency>
51.141 + <dependency>
51.142 + <groupId>org.apidesign.bck2brwsr</groupId>
51.143 + <artifactId>vmtest</artifactId>
51.144 + <version>${project.version}</version>
51.145 + <scope>test</scope>
51.146 + </dependency>
51.147 + <dependency>
51.148 + <groupId>org.apidesign.bck2brwsr</groupId>
51.149 + <artifactId>launcher.http</artifactId>
51.150 + <version>${project.version}</version>
51.151 + <scope>runtime</scope>
51.152 + </dependency>
51.153 + </dependencies>
51.154 +</project>
52.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
52.2 +++ b/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java Thu May 02 09:18:22 2013 +0200
52.3 @@ -0,0 +1,194 @@
52.4 +/**
52.5 + * Back 2 Browser Bytecode Translator
52.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
52.7 + *
52.8 + * This program is free software: you can redistribute it and/or modify
52.9 + * it under the terms of the GNU General Public License as published by
52.10 + * the Free Software Foundation, version 2 of the License.
52.11 + *
52.12 + * This program is distributed in the hope that it will be useful,
52.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
52.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52.15 + * GNU General Public License for more details.
52.16 + *
52.17 + * You should have received a copy of the GNU General Public License
52.18 + * along with this program. Look for COPYING file in the top folder.
52.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
52.20 + */
52.21 +package org.apidesign.bck2brwsr.demo.twitter;
52.22 +
52.23 +import java.util.Arrays;
52.24 +import java.util.List;
52.25 +import org.apidesign.bck2brwsr.htmlpage.api.*;
52.26 +import org.apidesign.bck2brwsr.htmlpage.api.Page;
52.27 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
52.28 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
52.29 +
52.30 +/** Controller class for access to Twitter.
52.31 + *
52.32 + * @author Jaroslav Tulach
52.33 + */
52.34 +@Page(xhtml="index.html", className="TwitterModel", properties={
52.35 + @Property(name="savedLists", type=Tweeters.class, array = true),
52.36 + @Property(name="activeTweetersName", type=String.class),
52.37 + @Property(name="activeTweeters", type=String.class, array = true),
52.38 + @Property(name="userNameToAdd", type=String.class),
52.39 + @Property(name="currentTweets", type=Tweet.class, array = true)
52.40 +})
52.41 +public class TwitterClient {
52.42 + @Model(className = "Tweeters", properties = {
52.43 + @Property(name="name", type = String.class),
52.44 + @Property(name="userNames", type = String.class, array = true)
52.45 + })
52.46 + static class Twttrs {
52.47 + }
52.48 + @Model(className = "Tweet", properties = {
52.49 + @Property(name = "from_user", type = String.class),
52.50 + @Property(name = "from_user_id", type = int.class),
52.51 + @Property(name = "profile_image_url", type = String.class),
52.52 + @Property(name = "text", type = String.class),
52.53 + @Property(name = "created_at", type = String.class),
52.54 + })
52.55 + static final class Twt {
52.56 + @ComputedProperty static String html(String text) {
52.57 + StringBuilder sb = new StringBuilder(320);
52.58 + for (int pos = 0;;) {
52.59 + int http = text.indexOf("http", pos);
52.60 + if (http == -1) {
52.61 + sb.append(text.substring(pos));
52.62 + return sb.toString();
52.63 + }
52.64 + int spc = text.indexOf(' ', http);
52.65 + if (spc == -1) {
52.66 + spc = text.length();
52.67 + }
52.68 + sb.append(text.substring(pos, http));
52.69 + String url = text.substring(http, spc);
52.70 + sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
52.71 + pos = spc;
52.72 + }
52.73 + }
52.74 +
52.75 + @ComputedProperty static String userUrl(String from_user) {
52.76 + return "http://twitter.com/" + from_user;
52.77 + }
52.78 + }
52.79 + @Model(className = "TwitterQuery", properties = {
52.80 + @Property(array = true, name = "results", type = Twt.class)
52.81 + })
52.82 + public static final class TwttrQr {
52.83 + }
52.84 +
52.85 + @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
52.86 + static void queryTweets(TwitterModel page, TwitterQuery q) {
52.87 + page.getCurrentTweets().clear();
52.88 + page.getCurrentTweets().addAll(q.getResults());
52.89 + }
52.90 +
52.91 + @OnPropertyChange("activeTweetersName")
52.92 + static void changeTweetersList(TwitterModel model) {
52.93 + Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());
52.94 + model.getActiveTweeters().clear();
52.95 + model.getActiveTweeters().addAll(people.getUserNames());
52.96 + }
52.97 +
52.98 + @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
52.99 + static void refreshTweets(TwitterModel model) {
52.100 + StringBuilder sb = new StringBuilder();
52.101 + sb.append("rpp=25&q=");
52.102 + String sep = "";
52.103 + for (String p : model.getActiveTweeters()) {
52.104 + sb.append(sep);
52.105 + sb.append("from:");
52.106 + sb.append(p);
52.107 + sep = " OR ";
52.108 + }
52.109 + model.queryTweets("http://search.twitter.com", sb.toString());
52.110 + }
52.111 +
52.112 + static {
52.113 + final TwitterModel model = new TwitterModel();
52.114 + final List<Tweeters> svdLst = model.getSavedLists();
52.115 + svdLst.add(newTweeters("API Design", "JaroslavTulach"));
52.116 + svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
52.117 + svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
52.118 + svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
52.119 + svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
52.120 +
52.121 + model.setActiveTweetersName("NetBeans");
52.122 +
52.123 + model.applyBindings();
52.124 + }
52.125 +
52.126 + @ComputedProperty
52.127 + static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
52.128 + Tweeters tw = findByName(savedLists, activeTweetersName);
52.129 + if (activeTweeters == null) {
52.130 + return false;
52.131 + }
52.132 + return !tw.getUserNames().equals(activeTweeters);
52.133 + }
52.134 +
52.135 + @ComputedProperty
52.136 + static int activeTweetersCount(List<String> activeTweeters) {
52.137 + return activeTweeters.size();
52.138 + }
52.139 +
52.140 + @ComputedProperty
52.141 + static boolean userNameToAddIsValid(
52.142 + String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
52.143 + ) {
52.144 + return userNameToAdd != null &&
52.145 + userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
52.146 + !activeTweeters.contains(userNameToAdd);
52.147 + }
52.148 +
52.149 + @OnFunction
52.150 + static void deleteList(TwitterModel model) {
52.151 + final List<Tweeters> sl = model.getSavedLists();
52.152 + sl.remove(findByName(sl, model.getActiveTweetersName()));
52.153 + if (sl.isEmpty()) {
52.154 + final Tweeters t = new Tweeters();
52.155 + t.setName("New");
52.156 + sl.add(t);
52.157 + }
52.158 + model.setActiveTweetersName(sl.get(0).getName());
52.159 + }
52.160 +
52.161 + @OnFunction
52.162 + static void saveChanges(TwitterModel model) {
52.163 + Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
52.164 + int indx = model.getSavedLists().indexOf(t);
52.165 + if (indx != -1) {
52.166 + t.setName(model.getActiveTweetersName());
52.167 + t.getUserNames().clear();
52.168 + t.getUserNames().addAll(model.getActiveTweeters());
52.169 + }
52.170 + }
52.171 +
52.172 + @OnFunction
52.173 + static void addUser(TwitterModel model) {
52.174 + String n = model.getUserNameToAdd();
52.175 + model.getActiveTweeters().add(n);
52.176 + }
52.177 + @OnFunction
52.178 + static void removeUser(String data, TwitterModel model) {
52.179 + model.getActiveTweeters().remove(data);
52.180 + }
52.181 +
52.182 + private static Tweeters findByName(List<Tweeters> list, String name) {
52.183 + for (Tweeters l : list) {
52.184 + if (l.getName() != null && l.getName().equals(name)) {
52.185 + return l;
52.186 + }
52.187 + }
52.188 + return list.isEmpty() ? new Tweeters() : list.get(0);
52.189 + }
52.190 +
52.191 + private static Tweeters newTweeters(String listName, String... userNames) {
52.192 + Tweeters t = new Tweeters();
52.193 + t.setName(listName);
52.194 + t.getUserNames().addAll(Arrays.asList(userNames));
52.195 + return t;
52.196 + }
52.197 +}
53.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
53.2 +++ b/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/index.html Thu May 02 09:18:22 2013 +0200
53.3 @@ -0,0 +1,99 @@
53.4 +<?xml version="1.0" encoding="UTF-8"?>
53.5 +<!--
53.6 +
53.7 + Back 2 Browser Bytecode Translator
53.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
53.9 +
53.10 + This program is free software: you can redistribute it and/or modify
53.11 + it under the terms of the GNU General Public License as published by
53.12 + the Free Software Foundation, version 2 of the License.
53.13 +
53.14 + This program is distributed in the hope that it will be useful,
53.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
53.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53.17 + GNU General Public License for more details.
53.18 +
53.19 + You should have received a copy of the GNU General Public License
53.20 + along with this program. Look for COPYING file in the top folder.
53.21 + If not, see http://opensource.org/licenses/GPL-2.0.
53.22 +
53.23 +-->
53.24 +
53.25 +<!--
53.26 + Copied from knockout.js Twitter example:
53.27 + http://knockoutjs.com/examples/twitter.html
53.28 +-->
53.29 +
53.30 +<!DOCTYPE html>
53.31 +<html xmlns="http://www.w3.org/1999/xhtml">
53.32 + <head>
53.33 + <title>Bck2Brwsr's Twitter</title>
53.34 + </head>
53.35 + <body>
53.36 + <link href='twitterExample.css' rel='Stylesheet' ></link>
53.37 +
53.38 + <style type='text/css'>
53.39 + .liveExample select { height: 1.7em; }
53.40 + .liveExample button { height: 2em; }
53.41 + </style>
53.42 +
53.43 +
53.44 + <h2>Bck2Brwsr's Twitter</h2>
53.45 +
53.46 + <p>
53.47 + This code based on original <a href="http://knockoutjs.com/examples/twitter.html">knockout.js Twitter example</a> and
53.48 + uses almost unmodified HTML code. It just changes the model. It
53.49 + is written in Java language and it is executed using <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a>
53.50 + virtual machine. The Java source code has about 190 lines and is available
53.51 + <a href="http://source.apidesign.org/hg/bck2brwsr/file/7fc6b7e9c982/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java">here</a>
53.52 + - in fact it may even be more dense than the original JavaScript model.
53.53 + </p>
53.54 +
53.55 + <div class='liveExample'>
53.56 + <div class='configuration'>
53.57 + <div class='listChooser'>
53.58 + <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
53.59 + <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button>
53.60 + <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
53.61 + </div>
53.62 +
53.63 + <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
53.64 + <div class='currentUsers' >
53.65 + <ul data-bind='foreach: activeTweeters'>
53.66 + <li>
53.67 + <button data-bind='click: $root.removeUser'>Remove</button>
53.68 + <div data-bind='text: $data'> </div>
53.69 + </li>
53.70 + </ul>
53.71 + </div>
53.72 +
53.73 + <form data-bind='submit: addUser'>
53.74 + <label>Add user:</label>
53.75 + <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
53.76 + <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
53.77 + </form>
53.78 + </div>
53.79 + <div class='tweets'>
53.80 + <div class='loadingIndicator'>Loading...</div>
53.81 + <table data-bind='foreach: currentTweets' width='100%'>
53.82 + <tr>
53.83 + <td><img data-bind='attr: { src: profile_image_url }' /></td>
53.84 + <td>
53.85 + <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
53.86 + <span data-bind='html: html'> </span>
53.87 + <div class='tweetInfo' data-bind='text: created_at'> </div>
53.88 + </td>
53.89 + </tr>
53.90 + </table>
53.91 + </div>
53.92 + </div>
53.93 +
53.94 + <script src="bck2brwsr.js"></script>
53.95 + <script type="text/javascript">
53.96 + var vm = bck2brwsr('demo-twitter-0.8-SNAPSHOT.jar');
53.97 + vm.loadClass('org.apidesign.bck2brwsr.demo.twitter.TwitterClient');
53.98 + </script>
53.99 +
53.100 +
53.101 + </body>
53.102 +</html>
54.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
54.2 +++ b/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/twitterExample.css Thu May 02 09:18:22 2013 +0200
54.3 @@ -0,0 +1,50 @@
54.4 +/**
54.5 + * Back 2 Browser Bytecode Translator
54.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
54.7 + *
54.8 + * This program is free software: you can redistribute it and/or modify
54.9 + * it under the terms of the GNU General Public License as published by
54.10 + * the Free Software Foundation, version 2 of the License.
54.11 + *
54.12 + * This program is distributed in the hope that it will be useful,
54.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
54.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54.15 + * GNU General Public License for more details.
54.16 + *
54.17 + * You should have received a copy of the GNU General Public License
54.18 + * along with this program. Look for COPYING file in the top folder.
54.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
54.20 + */
54.21 +
54.22 +/*
54.23 + Copied from knockout.js Twitter example:
54.24 + http://knockoutjs.com/examples/twitter.html
54.25 +*/
54.26 +
54.27 +.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
54.28 +.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
54.29 +.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; }
54.30 +.tweets table { border-width: 0;}
54.31 +.tweets tr { vertical-align: top; }
54.32 +.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
54.33 +.tweets img { width: 4em; }
54.34 +.tweetInfo { color: Gray; font-size: 0.9em; }
54.35 +.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
54.36 +input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
54.37 +
54.38 +.listChooser select, .listChooser button { vertical-align:top; }
54.39 +.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
54.40 +.listChooser button { width: 19%; height:1.68em; float:right; }
54.41 +
54.42 +.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
54.43 +.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
54.44 +.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
54.45 +.currentUsers li { height: 2.4em; font-size: 1.2em; background-color: #A7D0E3; border: 1px solid gray; margin-bottom: 0.3em; -webkit-border-radius: 5px; -moz-border-radius: 5px; -webkit-box-shadow: 0 0.2em 0.5em gray; -moz-box-shadow: 0 0.2em 0.5em gray; }
54.46 +.currentUsers li div { padding: 0.6em; }
54.47 +.currentUsers li:hover { background-color: #EEC; }
54.48 +
54.49 +.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
54.50 +.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
54.51 +.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
54.52 +
54.53 +.loadingIndicator { position: absolute; top: 0.1em; left: 0.1em; font: 0.8em Arial; background-color: #229; color: White; padding: 0.2em 0.5em 0.2em 0.5em; display: none; }
55.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
55.2 +++ b/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClientTest.java Thu May 02 09:18:22 2013 +0200
55.3 @@ -0,0 +1,67 @@
55.4 +/**
55.5 + * Back 2 Browser Bytecode Translator
55.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
55.7 + *
55.8 + * This program is free software: you can redistribute it and/or modify
55.9 + * it under the terms of the GNU General Public License as published by
55.10 + * the Free Software Foundation, version 2 of the License.
55.11 + *
55.12 + * This program is distributed in the hope that it will be useful,
55.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
55.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
55.15 + * GNU General Public License for more details.
55.16 + *
55.17 + * You should have received a copy of the GNU General Public License
55.18 + * along with this program. Look for COPYING file in the top folder.
55.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
55.20 + */
55.21 +package org.apidesign.bck2brwsr.demo.twitter;
55.22 +
55.23 +import java.util.List;
55.24 +import static org.testng.Assert.*;
55.25 +import org.testng.annotations.BeforeMethod;
55.26 +import org.testng.annotations.Test;
55.27 +
55.28 +/** We can unit test the TwitterModel smoothly.
55.29 + *
55.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
55.31 + */
55.32 +public class TwitterClientTest {
55.33 + private TwitterModel model;
55.34 +
55.35 +
55.36 + @BeforeMethod
55.37 + public void initModel() {
55.38 + model = new TwitterModel().applyBindings();
55.39 + }
55.40 +
55.41 + @Test public void testIsValidToAdd() {
55.42 + model.setUserNameToAdd("Joe");
55.43 + Tweeters t = new Tweeters();
55.44 + t.setName("test");
55.45 + model.getSavedLists().add(t);
55.46 + model.setActiveTweetersName("test");
55.47 +
55.48 + assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
55.49 + TwitterClient.addUser(model);
55.50 + assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
55.51 + assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
55.52 +
55.53 + List<String> mod = model.getActiveTweeters();
55.54 + assertTrue(model.isHasUnsavedChanges(), "We have modifications");
55.55 + assertEquals(mod.size(), 1, "One element in the list");
55.56 + assertEquals(mod.get(0), "Joe", "Its name is Joe");
55.57 +
55.58 + assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
55.59 +
55.60 + TwitterClient.saveChanges(model);
55.61 + assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
55.62 +
55.63 + assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
55.64 + }
55.65 +
55.66 + @Test public void httpAtTheEnd() {
55.67 + String res = TwitterClient.Twt.html("Ahoj http://kuk");
55.68 + assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
55.69 + }
55.70 +}
56.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
56.2 +++ b/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterProtocolTest.java Thu May 02 09:18:22 2013 +0200
56.3 @@ -0,0 +1,94 @@
56.4 +/**
56.5 + * Back 2 Browser Bytecode Translator
56.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
56.7 + *
56.8 + * This program is free software: you can redistribute it and/or modify
56.9 + * it under the terms of the GNU General Public License as published by
56.10 + * the Free Software Foundation, version 2 of the License.
56.11 + *
56.12 + * This program is distributed in the hope that it will be useful,
56.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
56.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56.15 + * GNU General Public License for more details.
56.16 + *
56.17 + * You should have received a copy of the GNU General Public License
56.18 + * along with this program. Look for COPYING file in the top folder.
56.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
56.20 + */
56.21 +package org.apidesign.bck2brwsr.demo.twitter;
56.22 +
56.23 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
56.24 +import org.apidesign.bck2brwsr.vmtest.Http;
56.25 +import org.apidesign.bck2brwsr.vmtest.VMTest;
56.26 +import org.testng.annotations.Factory;
56.27 +
56.28 +/**
56.29 + *
56.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
56.31 + */
56.32 +public class TwitterProtocolTest {
56.33 + private TwitterModel page;
56.34 + @Http(@Http.Resource(
56.35 + path = "/search.json",
56.36 + mimeType = "application/json",
56.37 + parameters = {"callback"},
56.38 + content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
56.39 + + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
56.40 + + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
56.41 + + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
56.42 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
56.43 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
56.44 + + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
56.45 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.46 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.47 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
56.48 + + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
56.49 + + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
56.50 + + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
56.51 + + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
56.52 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
56.53 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
56.54 + + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
56.55 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.56 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.57 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
56.58 + + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
56.59 + + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
56.60 + + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
56.61 + + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
56.62 + + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
56.63 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.64 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.65 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
56.66 + + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
56.67 + + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
56.68 + + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
56.69 + + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
56.70 + + "{\"result_type\":\"recent\"},\"profile_image_url\":"
56.71 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.72 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
56.73 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
56.74 + + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
56.75 + + "\"since_id\":0,\"since_id_str\":\"0\"})"
56.76 + ))
56.77 + @BrwsrTest public void readFromTwttr() throws InterruptedException {
56.78 + if (page == null) {
56.79 + page = new TwitterModel();
56.80 + page.applyBindings();
56.81 + page.queryTweets("", "q=xyz");
56.82 + }
56.83 +
56.84 + if (page.getCurrentTweets().isEmpty()) {
56.85 + throw new InterruptedException();
56.86 + }
56.87 +
56.88 + assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
56.89 +
56.90 + String firstDate = page.getCurrentTweets().get(0).getCreated_at();
56.91 + assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
56.92 + }
56.93 +
56.94 + @Factory public static Object[] create() {
56.95 + return VMTest.create(TwitterProtocolTest.class);
56.96 + }
56.97 +}
57.1 --- a/javaquery/pom.xml Tue Apr 02 15:40:51 2013 +0200
57.2 +++ b/javaquery/pom.xml Thu May 02 09:18:22 2013 +0200
57.3 @@ -4,16 +4,17 @@
57.4 <parent>
57.5 <artifactId>bck2brwsr</artifactId>
57.6 <groupId>org.apidesign</groupId>
57.7 - <version>0.5-SNAPSHOT</version>
57.8 + <version>0.8-SNAPSHOT</version>
57.9 </parent>
57.10 <groupId>org.apidesign.bck2brwsr</groupId>
57.11 <artifactId>javaquery</artifactId>
57.12 - <version>0.5-SNAPSHOT</version>
57.13 + <version>0.8-SNAPSHOT</version>
57.14 <packaging>pom</packaging>
57.15 <name>JavaQuery API and Demo</name>
57.16 <modules>
57.17 <module>api</module>
57.18 <module>demo-calculator</module>
57.19 <module>demo-calculator-dynamic</module>
57.20 - </modules>
57.21 -</project>
57.22 + <module>demo-twitter</module>
57.23 + </modules>
57.24 +</project>
57.25 \ No newline at end of file
58.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
58.2 +++ b/launcher/api/pom.xml Thu May 02 09:18:22 2013 +0200
58.3 @@ -0,0 +1,31 @@
58.4 +<?xml version="1.0"?>
58.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
58.6 + <modelVersion>4.0.0</modelVersion>
58.7 + <parent>
58.8 + <groupId>org.apidesign.bck2brwsr</groupId>
58.9 + <artifactId>launcher-pom</artifactId>
58.10 + <version>0.8-SNAPSHOT</version>
58.11 + </parent>
58.12 + <groupId>org.apidesign.bck2brwsr</groupId>
58.13 + <artifactId>launcher</artifactId>
58.14 + <version>0.8-SNAPSHOT</version>
58.15 + <name>Launcher API</name>
58.16 + <url>http://maven.apache.org</url>
58.17 + <properties>
58.18 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
58.19 + </properties>
58.20 + <build>
58.21 + <plugins>
58.22 + <plugin>
58.23 + <groupId>org.apache.maven.plugins</groupId>
58.24 + <artifactId>maven-javadoc-plugin</artifactId>
58.25 + <configuration>
58.26 + <subpackages>org.apidesign.bck2brwsr.launcher</subpackages>
58.27 + <skip>false</skip>
58.28 + </configuration>
58.29 + </plugin>
58.30 + </plugins>
58.31 + </build>
58.32 + <dependencies>
58.33 + </dependencies>
58.34 +</project>
59.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
59.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Thu May 02 09:18:22 2013 +0200
59.3 @@ -0,0 +1,115 @@
59.4 +/**
59.5 + * Back 2 Browser Bytecode Translator
59.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
59.7 + *
59.8 + * This program is free software: you can redistribute it and/or modify
59.9 + * it under the terms of the GNU General Public License as published by
59.10 + * the Free Software Foundation, version 2 of the License.
59.11 + *
59.12 + * This program is distributed in the hope that it will be useful,
59.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
59.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59.15 + * GNU General Public License for more details.
59.16 + *
59.17 + * You should have received a copy of the GNU General Public License
59.18 + * along with this program. Look for COPYING file in the top folder.
59.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
59.20 + */
59.21 +package org.apidesign.bck2brwsr.launcher;
59.22 +
59.23 +import java.io.IOException;
59.24 +import java.io.InputStream;
59.25 +import java.util.ArrayList;
59.26 +import java.util.List;
59.27 +import java.util.concurrent.CountDownLatch;
59.28 +import java.util.concurrent.TimeUnit;
59.29 +
59.30 +/** Represents individual method invocation, its context and its result.
59.31 + *
59.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
59.33 + */
59.34 +public final class InvocationContext {
59.35 + final CountDownLatch wait = new CountDownLatch(1);
59.36 + final Class<?> clazz;
59.37 + final String methodName;
59.38 + private final Launcher launcher;
59.39 + private String result;
59.40 + private Throwable exception;
59.41 + String html;
59.42 + final List<Resource> resources = new ArrayList<>();
59.43 +
59.44 + InvocationContext(Launcher launcher, Class<?> clazz, String methodName) {
59.45 + this.launcher = launcher;
59.46 + this.clazz = clazz;
59.47 + this.methodName = methodName;
59.48 + }
59.49 +
59.50 + /** An HTML fragment to be available for the execution. Useful primarily when
59.51 + * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}.
59.52 + * @param html the html fragment
59.53 + */
59.54 + public void setHtmlFragment(String html) {
59.55 + this.html = html;
59.56 + }
59.57 +
59.58 + /** HTTP resource to be available during execution. An invocation may
59.59 + * perform an HTTP query and obtain a resource relative to the page.
59.60 + */
59.61 + public void addHttpResource(String relativePath, String mimeType, String[] parameters, InputStream content) {
59.62 + if (relativePath == null || mimeType == null || content == null || parameters == null) {
59.63 + throw new NullPointerException();
59.64 + }
59.65 + resources.add(new Resource(content, mimeType, relativePath, parameters));
59.66 + }
59.67 +
59.68 + /** Invokes the associated method.
59.69 + * @return the textual result of the invocation
59.70 + */
59.71 + public String invoke() throws IOException {
59.72 + launcher.runMethod(this);
59.73 + return toString();
59.74 + }
59.75 +
59.76 + /** Obtains textual result of the invocation.
59.77 + * @return text representing the exception or result value
59.78 + */
59.79 + @Override
59.80 + public String toString() {
59.81 + if (exception != null) {
59.82 + return exception.toString();
59.83 + }
59.84 + return result;
59.85 + }
59.86 +
59.87 + /**
59.88 + * @param timeOut
59.89 + * @throws InterruptedException
59.90 + */
59.91 + void await(long timeOut) throws InterruptedException {
59.92 + wait.await(timeOut, TimeUnit.MILLISECONDS);
59.93 + }
59.94 +
59.95 + void result(String r, Throwable e) {
59.96 + this.result = r;
59.97 + this.exception = e;
59.98 + wait.countDown();
59.99 + }
59.100 +
59.101 +
59.102 + static final class Resource {
59.103 + final InputStream httpContent;
59.104 + final String httpType;
59.105 + final String httpPath;
59.106 + final String[] parameters;
59.107 +
59.108 + Resource(InputStream httpContent, String httpType, String httpPath,
59.109 + String[] parameters
59.110 + ) {
59.111 + httpContent.mark(Integer.MAX_VALUE);
59.112 + this.httpContent = httpContent;
59.113 + this.httpType = httpType;
59.114 + this.httpPath = httpPath;
59.115 + this.parameters = parameters;
59.116 + }
59.117 + }
59.118 +}
60.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
60.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Thu May 02 09:18:22 2013 +0200
60.3 @@ -0,0 +1,166 @@
60.4 +/**
60.5 + * Back 2 Browser Bytecode Translator
60.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
60.7 + *
60.8 + * This program is free software: you can redistribute it and/or modify
60.9 + * it under the terms of the GNU General Public License as published by
60.10 + * the Free Software Foundation, version 2 of the License.
60.11 + *
60.12 + * This program is distributed in the hope that it will be useful,
60.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
60.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60.15 + * GNU General Public License for more details.
60.16 + *
60.17 + * You should have received a copy of the GNU General Public License
60.18 + * along with this program. Look for COPYING file in the top folder.
60.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
60.20 + */
60.21 +package org.apidesign.bck2brwsr.launcher;
60.22 +
60.23 +import java.io.Closeable;
60.24 +import java.io.File;
60.25 +import java.io.IOException;
60.26 +import java.lang.reflect.Constructor;
60.27 +
60.28 +/** An abstraction for executing tests in a Bck2Brwsr virtual machine.
60.29 + * Either in {@link Launcher#createJavaScript JavaScript engine},
60.30 + * or in {@link Launcher#createBrowser external browser}.
60.31 + * <p>
60.32 + * There also are methods to {@link #showDir(java.io.File, java.lang.String) display pages}
60.33 + * in an external browser served by internal HTTP server.
60.34 + *
60.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
60.36 + */
60.37 +public abstract class Launcher {
60.38 +
60.39 + Launcher() {
60.40 + }
60.41 +
60.42 + /** Initializes the launcher. This may mean starting a web browser or
60.43 + * initializing execution engine.
60.44 + * @throws IOException if something goes wrong
60.45 + */
60.46 + public abstract void initialize() throws IOException;
60.47 +
60.48 + /** Shuts down the launcher.
60.49 + * @throws IOException if something goes wrong
60.50 + */
60.51 + public abstract void shutdown() throws IOException;
60.52 +
60.53 +
60.54 + /** Builds an invocation context. The context can later be customized
60.55 + * and {@link InvocationContext#invoke() invoked}.
60.56 + *
60.57 + * @param clazz the class to execute method from
60.58 + * @param method the method to execute
60.59 + * @return the context pointing to the selected method
60.60 + */
60.61 + public InvocationContext createInvocation(Class<?> clazz, String method) {
60.62 + return new InvocationContext(this, clazz, method);
60.63 + }
60.64 +
60.65 +
60.66 + /** Creates launcher that uses internal JavaScript engine (Rhino).
60.67 + * @return the launcher
60.68 + */
60.69 + public static Launcher createJavaScript() {
60.70 + try {
60.71 + Class<?> c = loadClass("org.apidesign.bck2brwsr.launcher.JSLauncher");
60.72 + return (Launcher) c.newInstance();
60.73 + } catch (Exception ex) {
60.74 + throw new IllegalStateException("Please include org.apidesign.bck2brwsr:launcher.html dependency!", ex);
60.75 + }
60.76 + }
60.77 +
60.78 + /** Creates launcher that is using external browser.
60.79 + *
60.80 + * @param cmd <code>null</code> to use <code>java.awt.Desktop</code> to show the launcher
60.81 + * or a string to execute in an external process (with a parameter to the URL)
60.82 + * @return launcher executing in external browser.
60.83 + */
60.84 + public static Launcher createBrowser(String cmd) {
60.85 + String msg = "Trying to create browser '" + cmd + "'";
60.86 + try {
60.87 + Class<?> c;
60.88 + if ("fxbrwsr".equals(cmd)) { // NOI18N
60.89 + msg = "Please include org.apidesign.bck2brwsr:launcher.fx dependency!";
60.90 + c = loadClass("org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher"); // NOI18N
60.91 + } else {
60.92 + msg = "Please include org.apidesign.bck2brwsr:launcher.html dependency!";
60.93 + c = loadClass("org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher"); // NOI18N
60.94 + if ("bck2brwsr".equals(cmd)) { // NOI18N
60.95 + // use default executable
60.96 + cmd = null;
60.97 + }
60.98 + }
60.99 + Constructor<?> cnstr = c.getConstructor(String.class);
60.100 + return (Launcher) cnstr.newInstance(cmd);
60.101 + } catch (Exception ex) {
60.102 + throw new IllegalStateException(msg, ex);
60.103 + }
60.104 + }
60.105 +
60.106 + /** Starts an HTTP server which provides access to classes and resources
60.107 + * available in the <code>classes</code> URL and shows a start page
60.108 + * available as {@link ClassLoader#getResource(java.lang.String)} from the
60.109 + * provide classloader. Opens a browser with URL showing the start page.
60.110 + *
60.111 + * @param classes classloader offering access to classes and resources
60.112 + * @param startpage page to show in the browser
60.113 + * @return interface that allows one to stop the server
60.114 + * @throws IOException if something goes wrong
60.115 + */
60.116 + public static Closeable showURL(ClassLoader classes, String startpage) throws IOException {
60.117 + return showURL(null, classes, startpage);
60.118 + }
60.119 + /** Starts an HTTP server which provides access to classes and resources
60.120 + * available in the <code>classes</code> URL and shows a start page
60.121 + * available as {@link ClassLoader#getResource(java.lang.String)} from the
60.122 + * provide classloader. Opens a browser with URL showing the start page.
60.123 + *
60.124 + * @param brwsr name of browser to use or <code>null</code>
60.125 + * @param classes classloader offering access to classes and resources
60.126 + * @param startpage page to show in the browser
60.127 + * @return interface that allows one to stop the server
60.128 + * @throws IOException if something goes wrong
60.129 + * @since 0.7
60.130 + */
60.131 + public static Closeable showURL(String brwsr, ClassLoader classes, String startpage) throws IOException {
60.132 + Launcher l = createBrowser(brwsr);
60.133 + l.addClassLoader(classes);
60.134 + l.showURL(startpage);
60.135 + return (Closeable) l;
60.136 + }
60.137 + /** Starts an HTTP server which provides access to certain directory.
60.138 + * The <code>startpage</code> should be relative location inside the root
60.139 + * directory. Opens a browser with URL showing the start page.
60.140 + *
60.141 + * @param directory the root directory on disk
60.142 + * @param startpage relative path from the root to the page
60.143 + * @exception IOException if something goes wrong.
60.144 + */
60.145 + public static Closeable showDir(File directory, String startpage) throws IOException {
60.146 + Launcher l = createBrowser(null);
60.147 + l.showDirectory(directory, startpage);
60.148 + return (Closeable) l;
60.149 + }
60.150 +
60.151 + abstract InvocationContext runMethod(InvocationContext c) throws IOException;
60.152 +
60.153 +
60.154 + private static Class<?> loadClass(String cn) throws ClassNotFoundException {
60.155 + return Launcher.class.getClassLoader().loadClass(cn);
60.156 + }
60.157 +
60.158 + void showDirectory(File directory, String startpage) throws IOException {
60.159 + throw new UnsupportedOperationException();
60.160 + }
60.161 +
60.162 + void showURL(String startpage) throws IOException {
60.163 + throw new UnsupportedOperationException();
60.164 + }
60.165 +
60.166 + void addClassLoader(ClassLoader classes) {
60.167 + throw new UnsupportedOperationException();
60.168 + }
60.169 +}
61.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
61.2 +++ b/launcher/fx/pom.xml Thu May 02 09:18:22 2013 +0200
61.3 @@ -0,0 +1,57 @@
61.4 +<?xml version="1.0"?>
61.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
61.6 + <modelVersion>4.0.0</modelVersion>
61.7 + <parent>
61.8 + <groupId>org.apidesign.bck2brwsr</groupId>
61.9 + <artifactId>launcher-pom</artifactId>
61.10 + <version>0.8-SNAPSHOT</version>
61.11 + </parent>
61.12 + <groupId>org.apidesign.bck2brwsr</groupId>
61.13 + <artifactId>launcher.fx</artifactId>
61.14 + <version>0.8-SNAPSHOT</version>
61.15 + <name>FXBrwsr Launcher</name>
61.16 + <url>http://maven.apache.org</url>
61.17 + <build>
61.18 + <plugins>
61.19 + <plugin>
61.20 + <groupId>org.apache.maven.plugins</groupId>
61.21 + <artifactId>maven-compiler-plugin</artifactId>
61.22 + <version>2.3.2</version>
61.23 + <configuration>
61.24 + <source>1.7</source>
61.25 + <target>1.7</target>
61.26 + </configuration>
61.27 + </plugin>
61.28 + <plugin>
61.29 + <groupId>org.apache.maven.plugins</groupId>
61.30 + <artifactId>maven-javadoc-plugin</artifactId>
61.31 + <configuration>
61.32 + <subpackages>org.apidesign.bck2brwsr.launcher.fx</subpackages>
61.33 + <skip>false</skip>
61.34 + </configuration>
61.35 + </plugin>
61.36 + </plugins>
61.37 + </build>
61.38 + <properties>
61.39 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
61.40 + </properties>
61.41 + <dependencies>
61.42 + <dependency>
61.43 + <groupId>${project.groupId}</groupId>
61.44 + <artifactId>launcher</artifactId>
61.45 + <version>${project.version}</version>
61.46 + </dependency>
61.47 + <dependency>
61.48 + <groupId>org.glassfish.grizzly</groupId>
61.49 + <artifactId>grizzly-http-server</artifactId>
61.50 + <version>2.2.19</version>
61.51 + </dependency>
61.52 + <dependency>
61.53 + <groupId>com.oracle</groupId>
61.54 + <artifactId>javafx</artifactId>
61.55 + <version>2.2</version>
61.56 + <scope>system</scope>
61.57 + <systemPath>${java.home}/lib/jfxrt.jar</systemPath>
61.58 + </dependency>
61.59 + </dependencies>
61.60 +</project>
62.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
62.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Thu May 02 09:18:22 2013 +0200
62.3 @@ -0,0 +1,588 @@
62.4 +/**
62.5 + * Back 2 Browser Bytecode Translator
62.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
62.7 + *
62.8 + * This program is free software: you can redistribute it and/or modify
62.9 + * it under the terms of the GNU General Public License as published by
62.10 + * the Free Software Foundation, version 2 of the License.
62.11 + *
62.12 + * This program is distributed in the hope that it will be useful,
62.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
62.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62.15 + * GNU General Public License for more details.
62.16 + *
62.17 + * You should have received a copy of the GNU General Public License
62.18 + * along with this program. Look for COPYING file in the top folder.
62.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
62.20 + */
62.21 +package org.apidesign.bck2brwsr.launcher;
62.22 +
62.23 +import java.io.Closeable;
62.24 +import java.io.File;
62.25 +import java.io.IOException;
62.26 +import java.io.InputStream;
62.27 +import java.io.InterruptedIOException;
62.28 +import java.io.OutputStream;
62.29 +import java.io.UnsupportedEncodingException;
62.30 +import java.io.Writer;
62.31 +import java.net.URI;
62.32 +import java.net.URISyntaxException;
62.33 +import java.net.URL;
62.34 +import java.util.ArrayList;
62.35 +import java.util.Arrays;
62.36 +import java.util.Enumeration;
62.37 +import java.util.LinkedHashSet;
62.38 +import java.util.List;
62.39 +import java.util.Set;
62.40 +import java.util.concurrent.BlockingQueue;
62.41 +import java.util.concurrent.CountDownLatch;
62.42 +import java.util.concurrent.LinkedBlockingQueue;
62.43 +import java.util.concurrent.TimeUnit;
62.44 +import java.util.logging.Level;
62.45 +import java.util.logging.Logger;
62.46 +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource;
62.47 +import org.glassfish.grizzly.PortRange;
62.48 +import org.glassfish.grizzly.http.server.HttpHandler;
62.49 +import org.glassfish.grizzly.http.server.HttpServer;
62.50 +import org.glassfish.grizzly.http.server.NetworkListener;
62.51 +import org.glassfish.grizzly.http.server.Request;
62.52 +import org.glassfish.grizzly.http.server.Response;
62.53 +import org.glassfish.grizzly.http.server.ServerConfiguration;
62.54 +import org.glassfish.grizzly.http.util.HttpStatus;
62.55 +
62.56 +/**
62.57 + * Lightweight server to launch Bck2Brwsr applications and tests.
62.58 + * Supports execution in native browser as well as Java's internal
62.59 + * execution engine.
62.60 + */
62.61 +abstract class BaseHTTPLauncher extends Launcher implements Closeable {
62.62 + private static final Logger LOG = Logger.getLogger(BaseHTTPLauncher.class.getName());
62.63 + private static final InvocationContext END = new InvocationContext(null, null, null);
62.64 + private final Set<ClassLoader> loaders = new LinkedHashSet<>();
62.65 + private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<>();
62.66 + private long timeOut;
62.67 + private final Res resources = new Res();
62.68 + private final String cmd;
62.69 + private Object[] brwsr;
62.70 + private HttpServer server;
62.71 + private CountDownLatch wait;
62.72 +
62.73 + public BaseHTTPLauncher(String cmd) {
62.74 + this.cmd = cmd;
62.75 + addClassLoader(BaseHTTPLauncher.class.getClassLoader());
62.76 + setTimeout(180000);
62.77 + }
62.78 +
62.79 + @Override
62.80 + InvocationContext runMethod(InvocationContext c) throws IOException {
62.81 + loaders.add(c.clazz.getClassLoader());
62.82 + methods.add(c);
62.83 + try {
62.84 + c.await(timeOut);
62.85 + } catch (InterruptedException ex) {
62.86 + throw new IOException(ex);
62.87 + }
62.88 + return c;
62.89 + }
62.90 +
62.91 + public void setTimeout(long ms) {
62.92 + timeOut = ms;
62.93 + }
62.94 +
62.95 + public void addClassLoader(ClassLoader url) {
62.96 + this.loaders.add(url);
62.97 + }
62.98 +
62.99 + ClassLoader[] loaders() {
62.100 + return loaders.toArray(new ClassLoader[loaders.size()]);
62.101 + }
62.102 +
62.103 + public void showURL(String startpage) throws IOException {
62.104 + if (!startpage.startsWith("/")) {
62.105 + startpage = "/" + startpage;
62.106 + }
62.107 + HttpServer s = initServer(".", true);
62.108 + int last = startpage.lastIndexOf('/');
62.109 + String prefix = startpage.substring(0, last);
62.110 + String simpleName = startpage.substring(last);
62.111 + s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/");
62.112 + try {
62.113 + launchServerAndBrwsr(s, simpleName);
62.114 + } catch (URISyntaxException | InterruptedException ex) {
62.115 + throw new IOException(ex);
62.116 + }
62.117 + }
62.118 +
62.119 + void showDirectory(File dir, String startpage) throws IOException {
62.120 + if (!startpage.startsWith("/")) {
62.121 + startpage = "/" + startpage;
62.122 + }
62.123 + HttpServer s = initServer(dir.getPath(), false);
62.124 + try {
62.125 + launchServerAndBrwsr(s, startpage);
62.126 + } catch (URISyntaxException | InterruptedException ex) {
62.127 + throw new IOException(ex);
62.128 + }
62.129 + }
62.130 +
62.131 + @Override
62.132 + public void initialize() throws IOException {
62.133 + try {
62.134 + executeInBrowser();
62.135 + } catch (InterruptedException ex) {
62.136 + final InterruptedIOException iio = new InterruptedIOException(ex.getMessage());
62.137 + iio.initCause(ex);
62.138 + throw iio;
62.139 + } catch (Exception ex) {
62.140 + if (ex instanceof IOException) {
62.141 + throw (IOException)ex;
62.142 + }
62.143 + if (ex instanceof RuntimeException) {
62.144 + throw (RuntimeException)ex;
62.145 + }
62.146 + throw new IOException(ex);
62.147 + }
62.148 + }
62.149 +
62.150 + private HttpServer initServer(String path, boolean addClasses) throws IOException {
62.151 + HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535));
62.152 +
62.153 + final ServerConfiguration conf = s.getServerConfiguration();
62.154 + if (addClasses) {
62.155 + conf.addHttpHandler(new VM(), "/bck2brwsr.js");
62.156 + conf.addHttpHandler(new Classes(resources), "/classes/");
62.157 + }
62.158 + return s;
62.159 + }
62.160 +
62.161 + private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
62.162 + wait = new CountDownLatch(1);
62.163 + server = initServer(".", true);
62.164 + final ServerConfiguration conf = server.getServerConfiguration();
62.165 +
62.166 + class DynamicResourceHandler extends HttpHandler {
62.167 + private final InvocationContext ic;
62.168 + public DynamicResourceHandler(InvocationContext ic) {
62.169 + if (ic == null || ic.resources.isEmpty()) {
62.170 + throw new NullPointerException();
62.171 + }
62.172 + this.ic = ic;
62.173 + for (Resource r : ic.resources) {
62.174 + conf.addHttpHandler(this, r.httpPath);
62.175 + }
62.176 + }
62.177 +
62.178 + public void close() {
62.179 + conf.removeHttpHandler(this);
62.180 + }
62.181 +
62.182 + @Override
62.183 + public void service(Request request, Response response) throws Exception {
62.184 + for (Resource r : ic.resources) {
62.185 + if (r.httpPath.equals(request.getRequestURI())) {
62.186 + LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
62.187 + response.setContentType(r.httpType);
62.188 + r.httpContent.reset();
62.189 + String[] params = null;
62.190 + if (r.parameters.length != 0) {
62.191 + params = new String[r.parameters.length];
62.192 + for (int i = 0; i < r.parameters.length; i++) {
62.193 + params[i] = request.getParameter(r.parameters[i]);
62.194 + }
62.195 + }
62.196 +
62.197 + copyStream(r.httpContent, response.getOutputStream(), null, params);
62.198 + }
62.199 + }
62.200 + }
62.201 + }
62.202 +
62.203 + conf.addHttpHandler(new Page(resources,
62.204 + "org/apidesign/bck2brwsr/launcher/fximpl/harness.xhtml"
62.205 + ), "/execute");
62.206 +
62.207 + conf.addHttpHandler(new HttpHandler() {
62.208 + int cnt;
62.209 + List<InvocationContext> cases = new ArrayList<>();
62.210 + DynamicResourceHandler prev;
62.211 + @Override
62.212 + public void service(Request request, Response response) throws Exception {
62.213 + String id = request.getParameter("request");
62.214 + String value = request.getParameter("result");
62.215 + if (value != null && value.indexOf((char)0xC5) != -1) {
62.216 + value = toUTF8(value);
62.217 + }
62.218 +
62.219 +
62.220 + InvocationContext mi = null;
62.221 + int caseNmbr = -1;
62.222 +
62.223 + if (id != null && value != null) {
62.224 + LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value});
62.225 + value = decodeURL(value);
62.226 + int indx = Integer.parseInt(id);
62.227 + cases.get(indx).result(value, null);
62.228 + if (++indx < cases.size()) {
62.229 + mi = cases.get(indx);
62.230 + LOG.log(Level.INFO, "Re-executing case {0}", indx);
62.231 + caseNmbr = indx;
62.232 + }
62.233 + } else {
62.234 + if (!cases.isEmpty()) {
62.235 + LOG.info("Re-executing test cases");
62.236 + mi = cases.get(0);
62.237 + caseNmbr = 0;
62.238 + }
62.239 + }
62.240 +
62.241 + if (prev != null) {
62.242 + prev.close();
62.243 + prev = null;
62.244 + }
62.245 +
62.246 + if (mi == null) {
62.247 + mi = methods.take();
62.248 + caseNmbr = cnt++;
62.249 + }
62.250 + if (mi == END) {
62.251 + response.getWriter().write("");
62.252 + wait.countDown();
62.253 + cnt = 0;
62.254 + LOG.log(Level.INFO, "End of data reached. Exiting.");
62.255 + return;
62.256 + }
62.257 +
62.258 + if (!mi.resources.isEmpty()) {
62.259 + prev = new DynamicResourceHandler(mi);
62.260 + }
62.261 +
62.262 + cases.add(mi);
62.263 + final String cn = mi.clazz.getName();
62.264 + final String mn = mi.methodName;
62.265 + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn});
62.266 + response.getWriter().write("{"
62.267 + + "className: '" + cn + "', "
62.268 + + "methodName: '" + mn + "', "
62.269 + + "request: " + caseNmbr
62.270 + );
62.271 + if (mi.html != null) {
62.272 + response.getWriter().write(", html: '");
62.273 + response.getWriter().write(encodeJSON(mi.html));
62.274 + response.getWriter().write("'");
62.275 + }
62.276 + response.getWriter().write("}");
62.277 + }
62.278 + }, "/data");
62.279 +
62.280 + this.brwsr = launchServerAndBrwsr(server, "/execute");
62.281 + }
62.282 +
62.283 + private static String encodeJSON(String in) {
62.284 + StringBuilder sb = new StringBuilder();
62.285 + for (int i = 0; i < in.length(); i++) {
62.286 + char ch = in.charAt(i);
62.287 + if (ch < 32 || ch == '\'' || ch == '"') {
62.288 + sb.append("\\u");
62.289 + String hs = "0000" + Integer.toHexString(ch);
62.290 + hs = hs.substring(hs.length() - 4);
62.291 + sb.append(hs);
62.292 + } else {
62.293 + sb.append(ch);
62.294 + }
62.295 + }
62.296 + return sb.toString();
62.297 + }
62.298 +
62.299 + @Override
62.300 + public void shutdown() throws IOException {
62.301 + methods.offer(END);
62.302 + for (;;) {
62.303 + int prev = methods.size();
62.304 + try {
62.305 + if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) {
62.306 + break;
62.307 + }
62.308 + } catch (InterruptedException ex) {
62.309 + throw new IOException(ex);
62.310 + }
62.311 + if (prev == methods.size()) {
62.312 + LOG.log(
62.313 + Level.WARNING,
62.314 + "Timeout and no test has been executed meanwhile (at {0}). Giving up.",
62.315 + methods.size()
62.316 + );
62.317 + break;
62.318 + }
62.319 + LOG.log(Level.INFO,
62.320 + "Timeout, but tests got from {0} to {1}. Trying again.",
62.321 + new Object[]{prev, methods.size()}
62.322 + );
62.323 + }
62.324 + stopServerAndBrwsr(server, brwsr);
62.325 + }
62.326 +
62.327 + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
62.328 + for (;;) {
62.329 + int ch = is.read();
62.330 + if (ch == -1) {
62.331 + break;
62.332 + }
62.333 + if (ch == '$' && params.length > 0) {
62.334 + int cnt = is.read() - '0';
62.335 + if (baseURL != null && cnt == 'U' - '0') {
62.336 + os.write(baseURL.getBytes("UTF-8"));
62.337 + } else {
62.338 + if (cnt >= 0 && cnt < params.length) {
62.339 + os.write(params[cnt].getBytes("UTF-8"));
62.340 + } else {
62.341 + os.write('$');
62.342 + os.write(cnt + '0');
62.343 + }
62.344 + }
62.345 + } else {
62.346 + os.write(ch);
62.347 + }
62.348 + }
62.349 + }
62.350 +
62.351 + private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
62.352 + server.start();
62.353 + NetworkListener listener = server.getListeners().iterator().next();
62.354 + int port = listener.getPort();
62.355 +
62.356 + URI uri = new URI("http://localhost:" + port + page);
62.357 + return showBrwsr(uri);
62.358 + }
62.359 + private static String toUTF8(String value) throws UnsupportedEncodingException {
62.360 + byte[] arr = new byte[value.length()];
62.361 + for (int i = 0; i < arr.length; i++) {
62.362 + arr[i] = (byte)value.charAt(i);
62.363 + }
62.364 + return new String(arr, "UTF-8");
62.365 + }
62.366 +
62.367 + private static String decodeURL(String s) {
62.368 + for (;;) {
62.369 + int pos = s.indexOf('%');
62.370 + if (pos == -1) {
62.371 + return s;
62.372 + }
62.373 + int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16);
62.374 + s = s.substring(0, pos) + (char)i + s.substring(pos + 2);
62.375 + }
62.376 + }
62.377 +
62.378 + private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException {
62.379 + if (brwsr == null) {
62.380 + return;
62.381 + }
62.382 + Process process = (Process)brwsr[0];
62.383 +
62.384 + server.stop();
62.385 + InputStream stdout = process.getInputStream();
62.386 + InputStream stderr = process.getErrorStream();
62.387 + drain("StdOut", stdout);
62.388 + drain("StdErr", stderr);
62.389 + process.destroy();
62.390 + int res;
62.391 + try {
62.392 + res = process.waitFor();
62.393 + } catch (InterruptedException ex) {
62.394 + throw new IOException(ex);
62.395 + }
62.396 + LOG.log(Level.INFO, "Exit code: {0}", res);
62.397 +
62.398 + deleteTree((File)brwsr[1]);
62.399 + }
62.400 +
62.401 + private static void drain(String name, InputStream is) throws IOException {
62.402 + int av = is.available();
62.403 + if (av > 0) {
62.404 + StringBuilder sb = new StringBuilder();
62.405 + sb.append("v== ").append(name).append(" ==v\n");
62.406 + while (av-- > 0) {
62.407 + sb.append((char)is.read());
62.408 + }
62.409 + sb.append("\n^== ").append(name).append(" ==^");
62.410 + LOG.log(Level.INFO, sb.toString());
62.411 + }
62.412 + }
62.413 +
62.414 + private void deleteTree(File file) {
62.415 + if (file == null) {
62.416 + return;
62.417 + }
62.418 + File[] arr = file.listFiles();
62.419 + if (arr != null) {
62.420 + for (File s : arr) {
62.421 + deleteTree(s);
62.422 + }
62.423 + }
62.424 + file.delete();
62.425 + }
62.426 +
62.427 + @Override
62.428 + public void close() throws IOException {
62.429 + shutdown();
62.430 + }
62.431 +
62.432 + protected Object[] showBrwsr(URI uri) throws IOException {
62.433 + LOG.log(Level.INFO, "Showing {0}", uri);
62.434 + if (cmd == null) {
62.435 + try {
62.436 + LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] {
62.437 + System.getProperty("java.vm.name"),
62.438 + System.getProperty("java.vm.vendor"),
62.439 + System.getProperty("java.vm.version"),
62.440 + });
62.441 + java.awt.Desktop.getDesktop().browse(uri);
62.442 + LOG.log(Level.INFO, "Desktop.browse successfully finished");
62.443 + return null;
62.444 + } catch (UnsupportedOperationException ex) {
62.445 + LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage());
62.446 + LOG.log(Level.FINE, null, ex);
62.447 + }
62.448 + }
62.449 + {
62.450 + String cmdName = cmd == null ? "xdg-open" : cmd;
62.451 + String[] cmdArr = {
62.452 + cmdName, uri.toString()
62.453 + };
62.454 + LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr));
62.455 + final Process process = Runtime.getRuntime().exec(cmdArr);
62.456 + return new Object[] { process, null };
62.457 + }
62.458 + }
62.459 +
62.460 + abstract void generateBck2BrwsrJS(StringBuilder sb, Object loader) throws IOException;
62.461 +
62.462 + private class Res {
62.463 + public InputStream get(String resource) throws IOException {
62.464 + for (ClassLoader l : loaders) {
62.465 + URL u = null;
62.466 + Enumeration<URL> en = l.getResources(resource);
62.467 + while (en.hasMoreElements()) {
62.468 + u = en.nextElement();
62.469 + }
62.470 + if (u != null) {
62.471 + return u.openStream();
62.472 + }
62.473 + }
62.474 + throw new IOException("Can't find " + resource);
62.475 + }
62.476 + }
62.477 +
62.478 + private static class Page extends HttpHandler {
62.479 + final String resource;
62.480 + private final String[] args;
62.481 + private final Res res;
62.482 +
62.483 + public Page(Res res, String resource, String... args) {
62.484 + this.res = res;
62.485 + this.resource = resource;
62.486 + this.args = args.length == 0 ? new String[] { "$0" } : args;
62.487 + }
62.488 +
62.489 + @Override
62.490 + public void service(Request request, Response response) throws Exception {
62.491 + String r = computePage(request);
62.492 + if (r.startsWith("/")) {
62.493 + r = r.substring(1);
62.494 + }
62.495 + String[] replace = {};
62.496 + if (r.endsWith(".html")) {
62.497 + response.setContentType("text/html");
62.498 + LOG.info("Content type text/html");
62.499 + replace = args;
62.500 + }
62.501 + if (r.endsWith(".xhtml")) {
62.502 + response.setContentType("application/xhtml+xml");
62.503 + LOG.info("Content type application/xhtml+xml");
62.504 + replace = args;
62.505 + }
62.506 + OutputStream os = response.getOutputStream();
62.507 + try (InputStream is = res.get(r)) {
62.508 + copyStream(is, os, request.getRequestURL().toString(), replace);
62.509 + } catch (IOException ex) {
62.510 + response.setDetailMessage(ex.getLocalizedMessage());
62.511 + response.setError();
62.512 + response.setStatus(404);
62.513 + }
62.514 + }
62.515 +
62.516 + protected String computePage(Request request) {
62.517 + String r = resource;
62.518 + if (r == null) {
62.519 + r = request.getHttpHandlerPath();
62.520 + }
62.521 + return r;
62.522 + }
62.523 + }
62.524 +
62.525 + private static class SubTree extends Page {
62.526 +
62.527 + public SubTree(Res res, String resource, String... args) {
62.528 + super(res, resource, args);
62.529 + }
62.530 +
62.531 + @Override
62.532 + protected String computePage(Request request) {
62.533 + return resource + request.getHttpHandlerPath();
62.534 + }
62.535 +
62.536 +
62.537 + }
62.538 +
62.539 + private class VM extends HttpHandler {
62.540 + @Override
62.541 + public void service(Request request, Response response) throws Exception {
62.542 + response.setCharacterEncoding("UTF-8");
62.543 + response.setContentType("text/javascript");
62.544 + StringBuilder sb = new StringBuilder();
62.545 + generateBck2BrwsrJS(sb, BaseHTTPLauncher.this.resources);
62.546 + response.getWriter().write(sb.toString());
62.547 + }
62.548 + }
62.549 +
62.550 + private static class Classes extends HttpHandler {
62.551 + private final Res loader;
62.552 +
62.553 + public Classes(Res loader) {
62.554 + this.loader = loader;
62.555 + }
62.556 +
62.557 + @Override
62.558 + public void service(Request request, Response response) throws Exception {
62.559 + String res = request.getHttpHandlerPath();
62.560 + if (res.startsWith("/")) {
62.561 + res = res.substring(1);
62.562 + }
62.563 + try (InputStream is = loader.get(res)) {
62.564 + response.setContentType("text/javascript");
62.565 + Writer w = response.getWriter();
62.566 + w.append("[");
62.567 + for (int i = 0;; i++) {
62.568 + int b = is.read();
62.569 + if (b == -1) {
62.570 + break;
62.571 + }
62.572 + if (i > 0) {
62.573 + w.append(", ");
62.574 + }
62.575 + if (i % 20 == 0) {
62.576 + w.write("\n");
62.577 + }
62.578 + if (b > 127) {
62.579 + b = b - 256;
62.580 + }
62.581 + w.append(Integer.toString(b));
62.582 + }
62.583 + w.append("\n]");
62.584 + } catch (IOException ex) {
62.585 + response.setStatus(HttpStatus.NOT_FOUND_404);
62.586 + response.setError();
62.587 + response.setDetailMessage(ex.getMessage());
62.588 + }
62.589 + }
62.590 + }
62.591 +}
63.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
63.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Thu May 02 09:18:22 2013 +0200
63.3 @@ -0,0 +1,129 @@
63.4 +/**
63.5 + * Back 2 Browser Bytecode Translator
63.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
63.7 + *
63.8 + * This program is free software: you can redistribute it and/or modify
63.9 + * it under the terms of the GNU General Public License as published by
63.10 + * the Free Software Foundation, version 2 of the License.
63.11 + *
63.12 + * This program is distributed in the hope that it will be useful,
63.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
63.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63.15 + * GNU General Public License for more details.
63.16 + *
63.17 + * You should have received a copy of the GNU General Public License
63.18 + * along with this program. Look for COPYING file in the top folder.
63.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
63.20 + */
63.21 +package org.apidesign.bck2brwsr.launcher;
63.22 +
63.23 +import org.apidesign.bck2brwsr.launcher.fximpl.FXBrwsr;
63.24 +import java.io.IOException;
63.25 +import java.io.InputStream;
63.26 +import java.lang.reflect.Method;
63.27 +import java.net.URI;
63.28 +import java.net.URL;
63.29 +import java.net.URLClassLoader;
63.30 +import java.util.Enumeration;
63.31 +
63.32 +import java.util.concurrent.Executors;
63.33 +import java.util.jar.Manifest;
63.34 +import java.util.logging.Level;
63.35 +import java.util.logging.Logger;
63.36 +import javafx.application.Platform;
63.37 +import org.apidesign.bck2brwsr.launcher.fximpl.JVMBridge;
63.38 +
63.39 +/**
63.40 + *
63.41 + * @author Jaroslav Tulach <jtulach@netbeans.org>
63.42 + */
63.43 +final class FXBrwsrLauncher extends BaseHTTPLauncher {
63.44 + private static final Logger LOG = Logger.getLogger(FXBrwsrLauncher.class.getName());
63.45 + static {
63.46 + try {
63.47 + Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
63.48 + m.setAccessible(true);
63.49 + URL l = new URL("file://" + System.getProperty("java.home") + "/lib/jfxrt.jar");
63.50 + LOG.log(Level.INFO, "url : {0}", l);
63.51 + m.invoke(ClassLoader.getSystemClassLoader(), l);
63.52 + } catch (Exception ex) {
63.53 + throw new LinkageError("Can't add jfxrt.jar on the classpath", ex);
63.54 + }
63.55 + }
63.56 +
63.57 + public FXBrwsrLauncher(String ignore) {
63.58 + super(null);
63.59 + }
63.60 +
63.61 + @Override
63.62 + protected Object[] showBrwsr(final URI url) throws IOException {
63.63 + try {
63.64 + LOG.log(Level.INFO, "showBrwsr for {0}", url);
63.65 + JVMBridge.registerClassLoaders(loaders());
63.66 + LOG.info("About to launch WebView");
63.67 + Executors.newSingleThreadExecutor().submit(new Runnable() {
63.68 + @Override
63.69 + public void run() {
63.70 + LOG.log(Level.INFO, "In FX thread. Launching!");
63.71 + try {
63.72 + FXBrwsr.launch(FXBrwsr.class, url.toString());
63.73 + LOG.log(Level.INFO, "Launcher is back. Closing");
63.74 + close();
63.75 + } catch (Throwable ex) {
63.76 + LOG.log(Level.WARNING, "Error launching Web View", ex);
63.77 + }
63.78 + }
63.79 + });
63.80 + } catch (Throwable ex) {
63.81 + LOG.log(Level.WARNING, "Can't open WebView", ex);
63.82 + }
63.83 + return null;
63.84 + }
63.85 +
63.86 + @Override
63.87 + void generateBck2BrwsrJS(StringBuilder sb, Object loader) throws IOException {
63.88 + sb.append("(function() {\n"
63.89 + + " var impl = this.bck2brwsr;\n"
63.90 + + " this.bck2brwsr = function() { return impl; };\n"
63.91 + + "})(window);\n"
63.92 + );
63.93 + JVMBridge.onBck2BrwsrLoad();
63.94 + }
63.95 +
63.96 +
63.97 +
63.98 + @Override
63.99 + public void close() throws IOException {
63.100 + super.close();
63.101 + Platform.exit();
63.102 + }
63.103 +
63.104 + public static void main(String... args) throws IOException {
63.105 + String startPage = null;
63.106 +
63.107 + final ClassLoader cl = FXBrwsrLauncher.class.getClassLoader();
63.108 + startPage = findStartPage(cl, startPage);
63.109 + if (startPage == null) {
63.110 + throw new NullPointerException("Can't find StartPage tag in manifests!");
63.111 + }
63.112 +
63.113 + Launcher.showURL("fxbrwsr", cl, startPage);
63.114 + }
63.115 +
63.116 + private static String findStartPage(final ClassLoader cl, String startPage) throws IOException {
63.117 + Enumeration<URL> en = cl.getResources("META-INF/MANIFEST.MF");
63.118 + while (en.hasMoreElements()) {
63.119 + URL url = en.nextElement();
63.120 + Manifest mf;
63.121 + try (InputStream is = url.openStream()) {
63.122 + mf = new Manifest(is);
63.123 + }
63.124 + String sp = mf.getMainAttributes().getValue("StartPage");
63.125 + if (sp != null) {
63.126 + startPage = sp;
63.127 + break;
63.128 + }
63.129 + }
63.130 + return startPage;
63.131 + }
63.132 +}
64.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
64.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fx/LauncherFX.java Thu May 02 09:18:22 2013 +0200
64.3 @@ -0,0 +1,32 @@
64.4 +/**
64.5 + * Back 2 Browser Bytecode Translator
64.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
64.7 + *
64.8 + * This program is free software: you can redistribute it and/or modify
64.9 + * it under the terms of the GNU General Public License as published by
64.10 + * the Free Software Foundation, version 2 of the License.
64.11 + *
64.12 + * This program is distributed in the hope that it will be useful,
64.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
64.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
64.15 + * GNU General Public License for more details.
64.16 + *
64.17 + * You should have received a copy of the GNU General Public License
64.18 + * along with this program. Look for COPYING file in the top folder.
64.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
64.20 + */
64.21 +package org.apidesign.bck2brwsr.launcher.fx;
64.22 +
64.23 +import org.apidesign.bck2brwsr.launcher.Launcher;
64.24 +
64.25 +/** This is a launcher for the <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a>
64.26 + * project that is using <b>JavaFX</b>'s WebView to display the browser inside
64.27 + * real Java virtual machine. Use {@link Launcher} methods to access this
64.28 + * functionality via public, supported methods.
64.29 + *
64.30 + * @author Jaroslav Tulach
64.31 + */
64.32 +public final class LauncherFX {
64.33 + private LauncherFX() {
64.34 + }
64.35 +}
65.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
65.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Thu May 02 09:18:22 2013 +0200
65.3 @@ -0,0 +1,395 @@
65.4 +/**
65.5 + * Back 2 Browser Bytecode Translator
65.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
65.7 + *
65.8 + * This program is free software: you can redistribute it and/or modify
65.9 + * it under the terms of the GNU General Public License as published by
65.10 + * the Free Software Foundation, version 2 of the License.
65.11 + *
65.12 + * This program is distributed in the hope that it will be useful,
65.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
65.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
65.15 + * GNU General Public License for more details.
65.16 + *
65.17 + * You should have received a copy of the GNU General Public License
65.18 + * along with this program. Look for COPYING file in the top folder.
65.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
65.20 + */
65.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
65.22 +
65.23 +import java.io.IOException;
65.24 +import java.io.InputStream;
65.25 +import java.io.UnsupportedEncodingException;
65.26 +import java.lang.reflect.InvocationTargetException;
65.27 +import java.lang.reflect.Method;
65.28 +import java.lang.reflect.Modifier;
65.29 +import java.net.URL;
65.30 +import java.util.Enumeration;
65.31 +import javafx.scene.web.WebEngine;
65.32 +import netscape.javascript.JSObject;
65.33 +
65.34 +/**
65.35 + *
65.36 + * @author Jaroslav Tulach <jtulach@netbeans.org>
65.37 + */
65.38 +public final class Console {
65.39 + public Console() {
65.40 + }
65.41 +
65.42 + private static Object getAttr(Object elem, String attr) {
65.43 + return InvokeJS.CObject.call("getAttr", elem, attr);
65.44 + }
65.45 +
65.46 + private static void setAttr(String id, String attr, Object value) {
65.47 + InvokeJS.CObject.call("setAttrId", id, attr, value);
65.48 + }
65.49 + private static void setAttr(Object id, String attr, Object value) {
65.50 + InvokeJS.CObject.call("setAttr", id, attr, value);
65.51 + }
65.52 +
65.53 + private static void closeWindow() {}
65.54 +
65.55 + private static Object textArea;
65.56 + private static Object statusArea;
65.57 +
65.58 + private static void log(String newText) {
65.59 + if (textArea == null) {
65.60 + return;
65.61 + }
65.62 + String attr = "value";
65.63 + setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText);
65.64 + setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight"));
65.65 + }
65.66 +
65.67 + private static void beginTest(Case c) {
65.68 + Object[] arr = new Object[2];
65.69 + beginTest(c.getClassName() + "." + c.getMethodName(), c, arr);
65.70 + textArea = arr[0];
65.71 + statusArea = arr[1];
65.72 + }
65.73 +
65.74 + private static void finishTest(Case c, Object res) {
65.75 + if ("null".equals(res)) {
65.76 + setAttr(statusArea, "innerHTML", "Success");
65.77 + } else {
65.78 + setAttr(statusArea, "innerHTML", "Result " + res);
65.79 + }
65.80 + statusArea = null;
65.81 + textArea = null;
65.82 + }
65.83 +
65.84 + private static final String BEGIN_TEST =
65.85 + "var ul = window.document.getElementById('bck2brwsr.result');\n"
65.86 + + "var li = window.document.createElement('li');\n"
65.87 + + "var span = window.document.createElement('span');"
65.88 + + "span.innerHTML = test + ' - ';\n"
65.89 + + "var details = window.document.createElement('a');\n"
65.90 + + "details.innerHTML = 'Details';\n"
65.91 + + "details.href = '#';\n"
65.92 + + "var p = window.document.createElement('p');\n"
65.93 + + "var status = window.document.createElement('a');\n"
65.94 + + "status.innerHTML = 'running';"
65.95 + + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n"
65.96 + + "status.onclick = function() { c.again(arr); }\n"
65.97 + + "var pre = window.document.createElement('textarea');\n"
65.98 + + "pre.cols = 100;"
65.99 + + "pre.rows = 10;"
65.100 + + "li.appendChild(span);\n"
65.101 + + "li.appendChild(status);\n"
65.102 + + "var span = window.document.createElement('span');"
65.103 + + "span.innerHTML = ' ';\n"
65.104 + + "li.appendChild(span);\n"
65.105 + + "li.appendChild(details);\n"
65.106 + + "p.appendChild(pre);\n"
65.107 + + "ul.appendChild(li);\n"
65.108 + + "arr[0] = pre;\n"
65.109 + + "arr[1] = status;\n";
65.110 +
65.111 + private static void beginTest(String test, Case c, Object[] arr) {
65.112 + InvokeJS.CObject.call("beginTest", test, c, arr);
65.113 + }
65.114 +
65.115 + private static final String LOAD_TEXT =
65.116 + "var request = new XMLHttpRequest();\n"
65.117 + + "request.open('GET', url, true);\n"
65.118 + + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
65.119 + + "request.onreadystatechange = function() {\n"
65.120 + + " if (this.readyState!==4) return;\n"
65.121 + + " try {"
65.122 + + " arr[0] = this.responseText;\n"
65.123 + + " callback.run__V();\n"
65.124 + + " } catch (e) { alert(e); }"
65.125 + + "};"
65.126 + + "request.send();";
65.127 + private static void loadText(String url, Runnable callback, String[] arr) throws IOException {
65.128 + InvokeJS.CObject.call("loadText", url, new Run(callback), arr);
65.129 + }
65.130 +
65.131 + public static void runHarness(String url) throws IOException {
65.132 + new Console().harness(url);
65.133 + }
65.134 +
65.135 + public void harness(String url) throws IOException {
65.136 + log("Connecting to " + url);
65.137 + Request r = new Request(url);
65.138 + }
65.139 +
65.140 + private static class Request implements Runnable {
65.141 + private final String[] arr = { null };
65.142 + private final String url;
65.143 + private Case c;
65.144 + private int retries;
65.145 +
65.146 + private Request(String url) throws IOException {
65.147 + this.url = url;
65.148 + loadText(url, this, arr);
65.149 + }
65.150 + private Request(String url, String u) throws IOException {
65.151 + this.url = url;
65.152 + loadText(u, this, arr);
65.153 + }
65.154 +
65.155 + @Override
65.156 + public void run() {
65.157 + try {
65.158 + if (c == null) {
65.159 + String data = arr[0];
65.160 +
65.161 + if (data == null) {
65.162 + log("Some error exiting");
65.163 + closeWindow();
65.164 + return;
65.165 + }
65.166 +
65.167 + if (data.isEmpty()) {
65.168 + log("No data, exiting");
65.169 + closeWindow();
65.170 + return;
65.171 + }
65.172 +
65.173 + c = Case.parseData(data);
65.174 + beginTest(c);
65.175 + log("Got \"" + data + "\"");
65.176 + } else {
65.177 + log("Processing \"" + arr[0] + "\" for " + retries + " time");
65.178 + }
65.179 + Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest();
65.180 + finishTest(c, result);
65.181 +
65.182 + String u = url + "?request=" + c.getRequestId() + "&result=" + result;
65.183 + new Request(url, u);
65.184 + } catch (Exception ex) {
65.185 + if (ex instanceof InterruptedException) {
65.186 + log("Re-scheduling in 100ms");
65.187 + schedule(this, 100);
65.188 + return;
65.189 + }
65.190 + log(ex.getClass().getName() + ":" + ex.getMessage());
65.191 + }
65.192 + }
65.193 + }
65.194 +
65.195 + private static String encodeURL(String r) throws UnsupportedEncodingException {
65.196 + final String SPECIAL = "%$&+,/:;=?@";
65.197 + StringBuilder sb = new StringBuilder();
65.198 + byte[] utf8 = r.getBytes("UTF-8");
65.199 + for (int i = 0; i < utf8.length; i++) {
65.200 + int ch = utf8[i] & 0xff;
65.201 + if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) {
65.202 + final String numbers = "0" + Integer.toHexString(ch);
65.203 + sb.append("%").append(numbers.substring(numbers.length() - 2));
65.204 + } else {
65.205 + if (ch == 32) {
65.206 + sb.append("+");
65.207 + } else {
65.208 + sb.append((char)ch);
65.209 + }
65.210 + }
65.211 + }
65.212 + return sb.toString();
65.213 + }
65.214 +
65.215 + static String invoke(String clazz, String method) throws
65.216 + ClassNotFoundException, InvocationTargetException, IllegalAccessException,
65.217 + InstantiationException, InterruptedException {
65.218 + final Object r = new Case(null).invokeMethod(clazz, method);
65.219 + return r == null ? "null" : r.toString().toString();
65.220 + }
65.221 +
65.222 + /** Helper method that inspects the classpath and loads given resource
65.223 + * (usually a class file). Used while running tests in Rhino.
65.224 + *
65.225 + * @param name resource name to find
65.226 + * @return the array of bytes in the given resource
65.227 + * @throws IOException I/O in case something goes wrong
65.228 + */
65.229 + public static byte[] read(String name) throws IOException {
65.230 + URL u = null;
65.231 + Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
65.232 + while (en.hasMoreElements()) {
65.233 + u = en.nextElement();
65.234 + }
65.235 + if (u == null) {
65.236 + throw new IOException("Can't find " + name);
65.237 + }
65.238 + try (InputStream is = u.openStream()) {
65.239 + byte[] arr;
65.240 + arr = new byte[is.available()];
65.241 + int offset = 0;
65.242 + while (offset < arr.length) {
65.243 + int len = is.read(arr, offset, arr.length - offset);
65.244 + if (len == -1) {
65.245 + throw new IOException("Can't read " + name);
65.246 + }
65.247 + offset += len;
65.248 + }
65.249 + return arr;
65.250 + }
65.251 + }
65.252 +
65.253 + private static void turnAssetionStatusOn() {
65.254 + }
65.255 +
65.256 + private static Object schedule(Runnable r, int time) {
65.257 + return InvokeJS.CObject.call("schedule", new Run(r), time);
65.258 + }
65.259 +
65.260 + private static final class Case {
65.261 + private final Object data;
65.262 + private Object inst;
65.263 +
65.264 + private Case(Object data) {
65.265 + this.data = data;
65.266 + }
65.267 +
65.268 + public static Case parseData(String s) {
65.269 + return new Case(toJSON(s));
65.270 + }
65.271 +
65.272 + public String getMethodName() {
65.273 + return (String) value("methodName", data);
65.274 + }
65.275 +
65.276 + public String getClassName() {
65.277 + return (String) value("className", data);
65.278 + }
65.279 +
65.280 + public int getRequestId() {
65.281 + Object v = value("request", data);
65.282 + if (v instanceof Number) {
65.283 + return ((Number)v).intValue();
65.284 + }
65.285 + return Integer.parseInt(v.toString());
65.286 + }
65.287 +
65.288 + public String getHtmlFragment() {
65.289 + return (String) value("html", data);
65.290 + }
65.291 +
65.292 + void again(Object[] arr) {
65.293 + try {
65.294 + textArea = arr[0];
65.295 + statusArea = arr[1];
65.296 + setAttr(textArea, "value", "");
65.297 + runTest();
65.298 + } catch (Exception ex) {
65.299 + log(ex.getClass().getName() + ":" + ex.getMessage());
65.300 + }
65.301 + }
65.302 +
65.303 + private Object runTest() throws IllegalAccessException,
65.304 + IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException,
65.305 + InvocationTargetException, InstantiationException, InterruptedException {
65.306 + if (this.getHtmlFragment() != null) {
65.307 + setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment());
65.308 + }
65.309 + log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId());
65.310 + Object result = invokeMethod(this.getClassName(), this.getMethodName());
65.311 + setAttr("bck2brwsr.fragment", "innerHTML", "");
65.312 + log("Result: " + result);
65.313 + result = encodeURL("" + result);
65.314 + log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
65.315 + return result;
65.316 + }
65.317 +
65.318 + private Object invokeMethod(String clazz, String method)
65.319 + throws ClassNotFoundException, InvocationTargetException,
65.320 + InterruptedException, IllegalAccessException, IllegalArgumentException,
65.321 + InstantiationException {
65.322 + Method found = null;
65.323 + Class<?> c = Class.forName(clazz);
65.324 + for (Method m : c.getMethods()) {
65.325 + if (m.getName().equals(method)) {
65.326 + found = m;
65.327 + }
65.328 + }
65.329 + Object res;
65.330 + if (found != null) {
65.331 + try {
65.332 + if ((found.getModifiers() & Modifier.STATIC) != 0) {
65.333 + res = found.invoke(null);
65.334 + } else {
65.335 + if (inst == null) {
65.336 + inst = c.newInstance();
65.337 + }
65.338 + res = found.invoke(inst);
65.339 + }
65.340 + } catch (Throwable ex) {
65.341 + if (ex instanceof InvocationTargetException) {
65.342 + ex = ((InvocationTargetException) ex).getTargetException();
65.343 + }
65.344 + if (ex instanceof InterruptedException) {
65.345 + throw (InterruptedException)ex;
65.346 + }
65.347 + res = ex.getClass().getName() + ":" + ex.getMessage();
65.348 + }
65.349 + } else {
65.350 + res = "Can't find method " + method + " in " + clazz;
65.351 + }
65.352 + return res;
65.353 + }
65.354 +
65.355 + private static Object toJSON(String s) {
65.356 + return InvokeJS.CObject.call("toJSON", s);
65.357 + }
65.358 +
65.359 + private static Object value(String p, Object d) {
65.360 + return ((JSObject)d).getMember(p);
65.361 + }
65.362 + }
65.363 +
65.364 + private static String safe(String txt) {
65.365 + return "try {" + txt + "} catch (err) { alert(err); }";
65.366 + }
65.367 +
65.368 + static {
65.369 + turnAssetionStatusOn();
65.370 + }
65.371 +
65.372 + private static final class InvokeJS {
65.373 + static final JSObject CObject = initJS();
65.374 +
65.375 + private static JSObject initJS() {
65.376 + WebEngine web = (WebEngine) System.getProperties().get("webEngine");
65.377 + return (JSObject) web.executeScript("(function() {"
65.378 + + "var CObject = {};"
65.379 +
65.380 + + "CObject.getAttr = function(elem, attr) { return elem[attr].toString(); };"
65.381 +
65.382 + + "CObject.setAttrId = function(id, attr, value) { window.document.getElementById(id)[attr] = value; };"
65.383 + + "CObject.setAttr = function(elem, attr, value) { elem[attr] = value; };"
65.384 +
65.385 + + "CObject.beginTest = function(test, c, arr) {" + safe(BEGIN_TEST) + "};"
65.386 +
65.387 + + "CObject.loadText = function(url, callback, arr) {" + safe(LOAD_TEXT.replace("run__V", "run")) + "};"
65.388 +
65.389 + + "CObject.schedule = function(r, time) { return window.setTimeout(function() { r.run(); }, time); };"
65.390 +
65.391 + + "CObject.toJSON = function(s) { return eval('(' + s + ')'); };"
65.392 +
65.393 + + "return CObject;"
65.394 + + "})(this)");
65.395 + }
65.396 + }
65.397 +
65.398 +}
66.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
66.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Thu May 02 09:18:22 2013 +0200
66.3 @@ -0,0 +1,184 @@
66.4 +/**
66.5 + * Back 2 Browser Bytecode Translator
66.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
66.7 + *
66.8 + * This program is free software: you can redistribute it and/or modify
66.9 + * it under the terms of the GNU General Public License as published by
66.10 + * the Free Software Foundation, version 2 of the License.
66.11 + *
66.12 + * This program is distributed in the hope that it will be useful,
66.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
66.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
66.15 + * GNU General Public License for more details.
66.16 + *
66.17 + * You should have received a copy of the GNU General Public License
66.18 + * along with this program. Look for COPYING file in the top folder.
66.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
66.20 + */
66.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
66.22 +
66.23 +import java.util.List;
66.24 +import java.util.TooManyListenersException;
66.25 +import java.util.logging.Level;
66.26 +import java.util.logging.Logger;
66.27 +import javafx.application.Application;
66.28 +import javafx.application.Platform;
66.29 +import javafx.beans.value.ChangeListener;
66.30 +import javafx.beans.value.ObservableValue;
66.31 +import javafx.event.ActionEvent;
66.32 +import javafx.event.EventHandler;
66.33 +import javafx.geometry.HPos;
66.34 +import javafx.geometry.Insets;
66.35 +import javafx.geometry.Pos;
66.36 +import javafx.geometry.VPos;
66.37 +import javafx.scene.Node;
66.38 +import javafx.scene.Scene;
66.39 +import javafx.scene.control.Button;
66.40 +import javafx.scene.layout.ColumnConstraints;
66.41 +import javafx.scene.layout.GridPane;
66.42 +import javafx.scene.layout.Pane;
66.43 +import javafx.scene.layout.Priority;
66.44 +import javafx.scene.layout.VBox;
66.45 +import javafx.scene.text.Text;
66.46 +import javafx.scene.web.WebEngine;
66.47 +import javafx.scene.web.WebEvent;
66.48 +import javafx.scene.web.WebView;
66.49 +import javafx.stage.Modality;
66.50 +import javafx.stage.Stage;
66.51 +import netscape.javascript.JSObject;
66.52 +
66.53 +/**
66.54 + * Demonstrates a WebView object accessing a web page.
66.55 + *
66.56 + * @see javafx.scene.web.WebView
66.57 + * @see javafx.scene.web.WebEngine
66.58 + */
66.59 +public class FXBrwsr extends Application {
66.60 + private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
66.61 +
66.62 + @Override
66.63 + public void start(Stage primaryStage) throws Exception {
66.64 + Pane root = new WebViewPane(getParameters().getUnnamed());
66.65 + primaryStage.setScene(new Scene(root, 1024, 768));
66.66 + LOG.info("Showing the stage");
66.67 + primaryStage.show();
66.68 + LOG.log(Level.INFO, "State shown: {0}", primaryStage.isShowing());
66.69 + }
66.70 +
66.71 + /**
66.72 + * Create a resizable WebView pane
66.73 + */
66.74 + private class WebViewPane extends Pane {
66.75 + private final JVMBridge bridge = new JVMBridge();
66.76 +
66.77 + public WebViewPane(List<String> params) {
66.78 + LOG.log(Level.INFO, "Initializing WebView with {0}", params);
66.79 + VBox.setVgrow(this, Priority.ALWAYS);
66.80 + setMaxWidth(Double.MAX_VALUE);
66.81 + setMaxHeight(Double.MAX_VALUE);
66.82 + WebView view = new WebView();
66.83 + view.setMinSize(500, 400);
66.84 + view.setPrefSize(500, 400);
66.85 + final WebEngine eng = view.getEngine();
66.86 + try {
66.87 + JVMBridge.addBck2BrwsrLoad(new InitBck2Brwsr(eng));
66.88 + } catch (TooManyListenersException ex) {
66.89 + LOG.log(Level.SEVERE, null, ex);
66.90 + }
66.91 +
66.92 + if (params.size() > 0) {
66.93 + LOG.log(Level.INFO, "loading page {0}", params.get(0));
66.94 + eng.load(params.get(0));
66.95 + LOG.fine("back from load");
66.96 + }
66.97 + eng.setOnAlert(new EventHandler<WebEvent<String>>() {
66.98 + @Override
66.99 + public void handle(WebEvent<String> t) {
66.100 + final Stage dialogStage = new Stage();
66.101 + dialogStage.initModality(Modality.WINDOW_MODAL);
66.102 + dialogStage.setTitle("Warning");
66.103 + final Button button = new Button("Close");
66.104 + final Text text = new Text(t.getData());
66.105 +
66.106 + VBox box = new VBox();
66.107 + box.setAlignment(Pos.CENTER);
66.108 + box.setSpacing(10);
66.109 + box.setPadding(new Insets(10));
66.110 + box.getChildren().addAll(text, button);
66.111 +
66.112 + dialogStage.setScene(new Scene(box));
66.113 +
66.114 + button.setCancelButton(true);
66.115 + button.setOnAction(new EventHandler<ActionEvent>() {
66.116 + @Override
66.117 + public void handle(ActionEvent t) {
66.118 + dialogStage.close();
66.119 + }
66.120 + });
66.121 +
66.122 + dialogStage.centerOnScreen();
66.123 + dialogStage.showAndWait();
66.124 + }
66.125 + });
66.126 + GridPane grid = new GridPane();
66.127 + grid.setVgap(5);
66.128 + grid.setHgap(5);
66.129 + GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
66.130 + grid.getColumnConstraints().addAll(new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true), new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true));
66.131 + grid.getChildren().addAll(view);
66.132 + getChildren().add(grid);
66.133 + }
66.134 +
66.135 + boolean initBck2Brwsr(WebEngine webEngine) {
66.136 + JSObject jsobj = (JSObject) webEngine.executeScript("window");
66.137 + LOG.log(Level.FINE, "window: {0}", jsobj);
66.138 + Object prev = jsobj.getMember("bck2brwsr");
66.139 + if ("undefined".equals(prev)) {
66.140 + System.getProperties().put("webEngine", webEngine);
66.141 + jsobj.setMember("bck2brwsr", bridge);
66.142 + return true;
66.143 + }
66.144 + return false;
66.145 + }
66.146 +
66.147 + @Override
66.148 + protected void layoutChildren() {
66.149 + List<Node> managed = getManagedChildren();
66.150 + double width = getWidth();
66.151 + double height = getHeight();
66.152 + double top = getInsets().getTop();
66.153 + double right = getInsets().getRight();
66.154 + double left = getInsets().getLeft();
66.155 + double bottom = getInsets().getBottom();
66.156 + for (int i = 0; i < managed.size(); i++) {
66.157 + Node child = managed.get(i);
66.158 + layoutInArea(child, left, top, width - left - right, height - top - bottom, 0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
66.159 + }
66.160 + }
66.161 +
66.162 + private class InitBck2Brwsr implements ChangeListener<Void>, Runnable {
66.163 + private final WebEngine eng;
66.164 +
66.165 + public InitBck2Brwsr(WebEngine eng) {
66.166 + this.eng = eng;
66.167 + }
66.168 +
66.169 + @Override
66.170 + public synchronized void changed(ObservableValue<? extends Void> ov, Void t, Void t1) {
66.171 + Platform.runLater(this);
66.172 + try {
66.173 + wait();
66.174 + } catch (InterruptedException ex) {
66.175 + LOG.log(Level.SEVERE, null, ex);
66.176 + }
66.177 + }
66.178 +
66.179 + @Override
66.180 + public synchronized void run() {
66.181 + initBck2Brwsr(eng);
66.182 + notifyAll();
66.183 + }
66.184 + }
66.185 + }
66.186 +
66.187 +}
67.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
67.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Thu May 02 09:18:22 2013 +0200
67.3 @@ -0,0 +1,64 @@
67.4 +/**
67.5 + * Back 2 Browser Bytecode Translator
67.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
67.7 + *
67.8 + * This program is free software: you can redistribute it and/or modify
67.9 + * it under the terms of the GNU General Public License as published by
67.10 + * the Free Software Foundation, version 2 of the License.
67.11 + *
67.12 + * This program is distributed in the hope that it will be useful,
67.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
67.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67.15 + * GNU General Public License for more details.
67.16 + *
67.17 + * You should have received a copy of the GNU General Public License
67.18 + * along with this program. Look for COPYING file in the top folder.
67.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
67.20 + */
67.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
67.22 +
67.23 +import java.util.TooManyListenersException;
67.24 +import javafx.beans.value.ChangeListener;
67.25 +
67.26 +/**
67.27 + *
67.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
67.29 + */
67.30 +public final class JVMBridge {
67.31 + private static ClassLoader[] ldrs;
67.32 + private static ChangeListener<Void> onBck2BrwsrLoad;
67.33 +
67.34 + public static void registerClassLoaders(ClassLoader[] loaders) {
67.35 + ldrs = loaders.clone();
67.36 + }
67.37 +
67.38 + public static void addBck2BrwsrLoad(ChangeListener<Void> l) throws TooManyListenersException {
67.39 + if (onBck2BrwsrLoad != null) {
67.40 + throw new TooManyListenersException();
67.41 + }
67.42 + onBck2BrwsrLoad = l;
67.43 + }
67.44 +
67.45 + public static void onBck2BrwsrLoad() {
67.46 + ChangeListener<Void> l = onBck2BrwsrLoad;
67.47 + if (l != null) {
67.48 + l.changed(null, null, null);
67.49 + }
67.50 + }
67.51 +
67.52 + public Class<?> loadClass(String name) throws ClassNotFoundException {
67.53 + System.err.println("trying to load " + name);
67.54 + ClassNotFoundException ex = null;
67.55 + if (ldrs != null) for (ClassLoader l : ldrs) {
67.56 + try {
67.57 + return Class.forName(name, true, l);
67.58 + } catch (ClassNotFoundException ex2) {
67.59 + ex = ex2;
67.60 + }
67.61 + }
67.62 + if (ex == null) {
67.63 + ex = new ClassNotFoundException("No loaders");
67.64 + }
67.65 + throw ex;
67.66 + }
67.67 +}
68.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
68.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Run.java Thu May 02 09:18:22 2013 +0200
68.3 @@ -0,0 +1,35 @@
68.4 +/**
68.5 + * Back 2 Browser Bytecode Translator
68.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
68.7 + *
68.8 + * This program is free software: you can redistribute it and/or modify
68.9 + * it under the terms of the GNU General Public License as published by
68.10 + * the Free Software Foundation, version 2 of the License.
68.11 + *
68.12 + * This program is distributed in the hope that it will be useful,
68.13 + * but WITHOUT ANY WAR