Merging with latest state - e.g. 0.7 version elements
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 02 May 2013 09:18:22 +0200
branchelements
changeset 10723800d11c0bdb
parent 913 146ae7b52b64
parent 1071 2894b9a9dbfc
child 1073 9321b4016d5c
Merging with latest state - e.g. 0.7 version
javaquery/api/pom.xml
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Attributes.java
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ElementGenerator.java
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PredefinedFields.java
javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java
javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ElementGeneratorTest.java
pom.xml
rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar
rt/launcher/pom.xml
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java
rt/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml
rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java
rt/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml
rt/mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml
rt/mojo/src/main/resources/archetype-resources/nbactions.xml
rt/mojo/src/main/resources/archetype-resources/pom.xml
rt/mojo/src/main/resources/archetype-resources/src/main/java/App.java
rt/mojo/src/main/resources/archetype-resources/src/main/resources/index.html
rt/mojo/src/main/resources/archetype-resources/src/test/java/AppTest.java
rt/mojo/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java
rt/mojo/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java
rt/mojo/src/test/java/org/apidesign/bck2brwsr/mojo/ArchetypeVersionTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/BrwsrCheckTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSub.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSubTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java
rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java
rt/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe
rt/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt
     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"> &amp; </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">&lt;jaroslav.tulach@apidesign.org&gt;</span>
   43.39 -     <span class="comment">*/</span>
   43.40 -    @Page(xhtml=<span class="string">&quot;</span><span class="string">Calculator.xhtml</span><span class="string">&quot;</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">&quot;</span><span class="string">clear</span><span class="string">&quot;</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">&quot;</span><span class="string">0</span><span class="string">&quot;</span>);
   43.50 -        }
   43.51 -
   43.52 -        @OnClick(id= { <span class="string">&quot;</span><span class="string">plus</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">minus</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">mul</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">div</span><span class="string">&quot;</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">&quot;</span><span class="string">0</span><span class="string">&quot;</span>);
   43.57 -        }
   43.58 -
   43.59 -        @OnClick(id=<span class="string">&quot;</span><span class="string">result</span><span class="string">&quot;</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">&quot;</span><span class="string">plus</span><span class="string">&quot;</span>: setValue(memory + getValue()); <span class="keyword-directive">break</span>;
   43.63 -                <span class="keyword-directive">case</span> <span class="string">&quot;</span><span class="string">minus</span><span class="string">&quot;</span>: setValue(memory - getValue()); <span class="keyword-directive">break</span>;
   43.64 -                <span class="keyword-directive">case</span> <span class="string">&quot;</span><span class="string">mul</span><span class="string">&quot;</span>: setValue(memory * getValue()); <span class="keyword-directive">break</span>;
   43.65 -                <span class="keyword-directive">case</span> <span class="string">&quot;</span><span class="string">div</span><span class="string">&quot;</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">&quot;</span><span class="string">n0</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n1</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n2</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n3</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n4</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n5</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n6</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n7</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n8</span><span class="string">&quot;</span>, <span class="string">&quot;</span><span class="string">n9</span><span class="string">&quot;</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">&quot;</span><span class="string">err</span><span class="string">&quot;</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\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"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\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"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\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"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\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"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